mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 08:34:52 +00:00
Merge pull request #3447 from akhilmhdh/feat/assume-role
Implemented project permission impersonation
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionIdentityActions,
|
||||
ProjectPermissionMemberActions,
|
||||
ProjectPermissionSub
|
||||
} from "../permission/project-permission";
|
||||
import { TAssumeProjectPrivilegeDTO } from "./assume-privilege-types";
|
||||
|
||||
type TAssumePrivilegeServiceFactoryDep = {
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
};
|
||||
|
||||
export type TAssumePrivilegeServiceFactory = ReturnType<typeof assumePrivilegeServiceFactory>;
|
||||
|
||||
export const assumePrivilegeServiceFactory = ({ projectDAL, permissionService }: TAssumePrivilegeServiceFactoryDep) => {
|
||||
const assumeProjectPrivileges = async ({
|
||||
targetActorType,
|
||||
targetActorId,
|
||||
projectId,
|
||||
actorPermissionDetails,
|
||||
tokenVersionId
|
||||
}: TAssumeProjectPrivilegeDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (!project) throw new NotFoundError({ message: `Project with ID '${projectId}' not found` });
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actorPermissionDetails.type,
|
||||
actorId: actorPermissionDetails.id,
|
||||
projectId,
|
||||
actorAuthMethod: actorPermissionDetails.authMethod,
|
||||
actorOrgId: actorPermissionDetails.orgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
|
||||
if (targetActorType === ActorType.USER) {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionMemberActions.AssumePrivileges,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
} else {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.AssumePrivileges,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
}
|
||||
|
||||
// check entity is part of project
|
||||
await permissionService.getProjectPermission({
|
||||
actor: targetActorType,
|
||||
actorId: targetActorId,
|
||||
projectId,
|
||||
actorAuthMethod: actorPermissionDetails.authMethod,
|
||||
actorOrgId: actorPermissionDetails.orgId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
|
||||
const appCfg = getConfig();
|
||||
const assumePrivilegesToken = jwt.sign(
|
||||
{
|
||||
tokenVersionId,
|
||||
actorType: targetActorType,
|
||||
actorId: targetActorId,
|
||||
projectId,
|
||||
requesterId: actorPermissionDetails.id
|
||||
},
|
||||
appCfg.AUTH_SECRET,
|
||||
{ expiresIn: "1hr" }
|
||||
);
|
||||
|
||||
return { actorType: targetActorType, actorId: targetActorId, projectId, assumePrivilegesToken };
|
||||
};
|
||||
|
||||
const verifyAssumePrivilegeToken = (token: string, tokenVersionId: string) => {
|
||||
const appCfg = getConfig();
|
||||
const decodedToken = jwt.verify(token, appCfg.AUTH_SECRET) as {
|
||||
tokenVersionId: string;
|
||||
projectId: string;
|
||||
requesterId: string;
|
||||
actorType: ActorType;
|
||||
actorId: string;
|
||||
};
|
||||
if (decodedToken.tokenVersionId !== tokenVersionId) {
|
||||
throw new ForbiddenRequestError({ message: "Invalid token version" });
|
||||
}
|
||||
return decodedToken;
|
||||
};
|
||||
|
||||
return {
|
||||
assumeProjectPrivileges,
|
||||
verifyAssumePrivilegeToken
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
export type TAssumeProjectPrivilegeDTO = {
|
||||
targetActorType: ActorType.USER | ActorType.IDENTITY;
|
||||
targetActorId: string;
|
||||
projectId: string;
|
||||
tokenVersionId: string;
|
||||
actorPermissionDetails: OrgServiceActor;
|
||||
};
|
||||
@@ -320,7 +320,9 @@ export enum EventType {
|
||||
DELETE_SECRET_ROTATION = "delete-secret-rotation",
|
||||
SECRET_ROTATION_ROTATE_SECRETS = "secret-rotation-rotate-secrets",
|
||||
|
||||
PROJECT_ACCESS_REQUEST = "project-access-request"
|
||||
PROJECT_ACCESS_REQUEST = "project-access-request",
|
||||
PROJECT_ASSUME_PRIVILEGE_SESSION_START = "project-assume-privileges-session-start",
|
||||
PROJECT_ASSUME_PRIVILEGE_SESSION_END = "project-assume-privileges-session-end"
|
||||
}
|
||||
|
||||
export const filterableSecretEvents: EventType[] = [
|
||||
@@ -2454,6 +2456,29 @@ interface ProjectAccessRequestEvent {
|
||||
};
|
||||
}
|
||||
|
||||
interface ProjectAssumePrivilegesEvent {
|
||||
type: EventType.PROJECT_ASSUME_PRIVILEGE_SESSION_START;
|
||||
metadata: {
|
||||
projectId: string;
|
||||
requesterId: string;
|
||||
requesterEmail: string;
|
||||
targetActorType: ActorType;
|
||||
targetActorId: string;
|
||||
duration: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ProjectAssumePrivilegesExitEvent {
|
||||
type: EventType.PROJECT_ASSUME_PRIVILEGE_SESSION_END;
|
||||
metadata: {
|
||||
projectId: string;
|
||||
requesterId: string;
|
||||
requesterEmail: string;
|
||||
targetActorType: ActorType;
|
||||
targetActorId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetupKmipEvent {
|
||||
type: EventType.SETUP_KMIP;
|
||||
metadata: {
|
||||
@@ -2759,6 +2784,8 @@ export type Event =
|
||||
| KmipOperationLocateEvent
|
||||
| KmipOperationRegisterEvent
|
||||
| ProjectAccessRequestEvent
|
||||
| ProjectAssumePrivilegesEvent
|
||||
| ProjectAssumePrivilegesExitEvent
|
||||
| CreateSecretRequestEvent
|
||||
| SecretApprovalRequestReview
|
||||
| GetSecretRotationsEvent
|
||||
|
||||
@@ -551,13 +551,26 @@ export const permissionServiceFactory = ({
|
||||
};
|
||||
|
||||
const getProjectPermission = async <T extends ActorType>({
|
||||
actor,
|
||||
actorId,
|
||||
actor: inputActor,
|
||||
actorId: inputActorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType
|
||||
}: TGetProjectPermissionArg): Promise<TProjectPermissionRT<T>> => {
|
||||
let actor = inputActor;
|
||||
let actorId = inputActorId;
|
||||
const assumedPrivilegeDetailsCtx = requestContext.get("assumedPrivilegeDetails");
|
||||
if (
|
||||
assumedPrivilegeDetailsCtx &&
|
||||
actor === ActorType.USER &&
|
||||
actorId === assumedPrivilegeDetailsCtx.requesterId &&
|
||||
projectId === assumedPrivilegeDetailsCtx.projectId
|
||||
) {
|
||||
actor = assumedPrivilegeDetailsCtx.actorType;
|
||||
actorId = assumedPrivilegeDetailsCtx.actorId;
|
||||
}
|
||||
|
||||
switch (actor) {
|
||||
case ActorType.USER:
|
||||
return getUserProjectPermission({
|
||||
|
||||
@@ -50,7 +50,8 @@ export enum ProjectPermissionIdentityActions {
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
GrantPrivileges = "grant-privileges"
|
||||
GrantPrivileges = "grant-privileges",
|
||||
AssumePrivileges = "assume-privileges"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionMemberActions {
|
||||
@@ -58,7 +59,8 @@ export enum ProjectPermissionMemberActions {
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
GrantPrivileges = "grant-privileges"
|
||||
GrantPrivileges = "grant-privileges",
|
||||
AssumePrivileges = "assume-privileges"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionGroupActions {
|
||||
@@ -714,7 +716,8 @@ const buildAdminPermissionRules = () => {
|
||||
ProjectPermissionMemberActions.Edit,
|
||||
ProjectPermissionMemberActions.Delete,
|
||||
ProjectPermissionMemberActions.Read,
|
||||
ProjectPermissionMemberActions.GrantPrivileges
|
||||
ProjectPermissionMemberActions.GrantPrivileges,
|
||||
ProjectPermissionMemberActions.AssumePrivileges
|
||||
],
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
@@ -736,7 +739,8 @@ const buildAdminPermissionRules = () => {
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
ProjectPermissionIdentityActions.Delete,
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
ProjectPermissionIdentityActions.GrantPrivileges
|
||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||
ProjectPermissionIdentityActions.AssumePrivileges
|
||||
],
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user