mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 13:28:34 +00:00
680 lines
24 KiB
TypeScript
680 lines
24 KiB
TypeScript
import { createMongoAbility, MongoAbility, RawRuleOf } from "@casl/ability";
|
|
import { PackRule, unpackRules } from "@casl/ability/extra";
|
|
import { requestContext } from "@fastify/request-context";
|
|
import handlebars from "handlebars";
|
|
import { Knex } from "knex";
|
|
|
|
import {
|
|
AccessScope,
|
|
ActionProjectType,
|
|
OrgMembershipRole,
|
|
ProjectMembershipRole,
|
|
ServiceTokenScopes
|
|
} from "@app/db/schemas";
|
|
import {
|
|
cryptographicOperatorPermissions,
|
|
projectAdminPermissions,
|
|
projectMemberPermissions,
|
|
projectNoAccessPermissions,
|
|
projectViewerPermission,
|
|
sshHostBootstrapPermissions
|
|
} from "@app/ee/services/permission/default-roles";
|
|
import { KeyStorePrefixes, KeyStoreTtls, TKeyStoreFactory } from "@app/keystore/keystore";
|
|
import { conditionsMatcher } from "@app/lib/casl";
|
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
|
import { objectify } from "@app/lib/fn";
|
|
import { ActorType } from "@app/services/auth/auth-type";
|
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|
import { TRoleDALFactory } from "@app/services/role/role-dal";
|
|
import { TServiceTokenDALFactory } from "@app/services/service-token/service-token-dal";
|
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
|
|
|
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
|
import { TPermissionDALFactory } from "./permission-dal";
|
|
import { escapeHandlebarsMissingDict } from "./permission-fns";
|
|
import {
|
|
TBuildOrgPermissionDTO,
|
|
TBuildProjectPermissionDTO,
|
|
TGetServiceTokenProjectPermissionArg,
|
|
TPermissionServiceFactory
|
|
} from "./permission-service-types";
|
|
import { buildServiceTokenProjectPermission, ProjectPermissionSet } from "./project-permission";
|
|
|
|
const buildOrgPermissionRules = (orgUserRoles: TBuildOrgPermissionDTO) => {
|
|
const rules = orgUserRoles
|
|
.map(({ role, permissions }) => {
|
|
switch (role) {
|
|
case OrgMembershipRole.Admin:
|
|
return orgAdminPermissions;
|
|
case OrgMembershipRole.Member:
|
|
return orgMemberPermissions;
|
|
case OrgMembershipRole.NoAccess:
|
|
return orgNoAccessPermissions;
|
|
case OrgMembershipRole.Custom:
|
|
return unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(
|
|
permissions as PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]
|
|
);
|
|
default:
|
|
throw new NotFoundError({ name: "OrgRoleInvalid", message: `Organization role '${role}' not found` });
|
|
}
|
|
})
|
|
.reduce((prev, curr) => prev.concat(curr), []);
|
|
|
|
return rules;
|
|
};
|
|
|
|
const buildProjectPermissionRules = (projectUserRoles: TBuildProjectPermissionDTO) => {
|
|
const rules = projectUserRoles
|
|
.map(({ role, permissions }) => {
|
|
switch (role) {
|
|
case ProjectMembershipRole.Admin:
|
|
return projectAdminPermissions;
|
|
case ProjectMembershipRole.Member:
|
|
return projectMemberPermissions;
|
|
case ProjectMembershipRole.Viewer:
|
|
return projectViewerPermission;
|
|
case ProjectMembershipRole.NoAccess:
|
|
return projectNoAccessPermissions;
|
|
case ProjectMembershipRole.SshHostBootstrapper:
|
|
return sshHostBootstrapPermissions;
|
|
case ProjectMembershipRole.KmsCryptographicOperator:
|
|
return cryptographicOperatorPermissions;
|
|
case ProjectMembershipRole.Custom: {
|
|
return unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
|
permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
|
|
);
|
|
}
|
|
default:
|
|
throw new NotFoundError({
|
|
name: "ProjectRoleInvalid",
|
|
message: `Project role '${role}' not found`
|
|
});
|
|
}
|
|
})
|
|
.reduce((prev, curr) => prev.concat(curr), []);
|
|
|
|
return rules;
|
|
};
|
|
|
|
type TPermissionServiceFactoryDep = {
|
|
serviceTokenDAL: Pick<TServiceTokenDALFactory, "findById">;
|
|
projectDAL: Pick<TProjectDALFactory, "findById">;
|
|
permissionDAL: TPermissionDALFactory;
|
|
keyStore: TKeyStoreFactory;
|
|
userDAL: Pick<TUserDALFactory, "findById">;
|
|
identityDAL: Pick<TIdentityDALFactory, "findById">;
|
|
roleDAL: Pick<TRoleDALFactory, "find">;
|
|
};
|
|
|
|
export const permissionServiceFactory = ({
|
|
permissionDAL,
|
|
serviceTokenDAL,
|
|
projectDAL,
|
|
userDAL,
|
|
identityDAL,
|
|
keyStore,
|
|
roleDAL
|
|
}: TPermissionServiceFactoryDep): TPermissionServiceFactory => {
|
|
const invalidateProjectPermissionCache = async (projectId: string, tx?: Knex) => {
|
|
const projectPermissionDalVersionKey = KeyStorePrefixes.ProjectPermissionDalVersion(projectId);
|
|
await keyStore.pgIncrementBy(projectPermissionDalVersionKey, {
|
|
incr: 1,
|
|
tx,
|
|
expiry: KeyStoreTtls.ProjectPermissionDalVersionTtl
|
|
});
|
|
};
|
|
|
|
// akhilmdhh: will bring this up later
|
|
// const calculateProjectPermissionTtl = (membership: unknown): number => {
|
|
// const now = new Date();
|
|
// let minTtl = KeyStoreTtls.ProjectPermissionCacheInSeconds;
|
|
//
|
|
// const getMinEndTime = (items: Array<{ temporaryAccessEndTime?: Date | null; isTemporary?: boolean }>) => {
|
|
// return items
|
|
// .filter((item) => item.isTemporary && item.temporaryAccessEndTime)
|
|
// .map((item) => item.temporaryAccessEndTime!)
|
|
// .filter((endTime) => endTime > now)
|
|
// .reduce((min, endTime) => (!min || endTime < min ? endTime : min), null as Date | null);
|
|
// };
|
|
//
|
|
// const roleTimes: Date[] = [];
|
|
// const additionalPrivilegeTimes: Date[] = [];
|
|
//
|
|
// if (
|
|
// membership &&
|
|
// typeof membership === "object" &&
|
|
// "roles" in membership &&
|
|
// Array.isArray((membership as Record<string, unknown>).roles)
|
|
// ) {
|
|
// const roles = (membership as Record<string, unknown>).roles as Array<{
|
|
// temporaryAccessEndTime?: Date | null;
|
|
// isTemporary?: boolean;
|
|
// }>;
|
|
// const minRoleEndTime = getMinEndTime(roles);
|
|
// if (minRoleEndTime) roleTimes.push(minRoleEndTime);
|
|
// }
|
|
//
|
|
// if (
|
|
// membership &&
|
|
// typeof membership === "object" &&
|
|
// "additionalPrivileges" in membership &&
|
|
// Array.isArray((membership as Record<string, unknown>).additionalPrivileges)
|
|
// ) {
|
|
// const additionalPrivileges = (membership as Record<string, unknown>).additionalPrivileges as Array<{
|
|
// temporaryAccessEndTime?: Date | null;
|
|
// isTemporary?: boolean;
|
|
// }>;
|
|
// const minAdditionalEndTime = getMinEndTime(additionalPrivileges);
|
|
// if (minAdditionalEndTime) additionalPrivilegeTimes.push(minAdditionalEndTime);
|
|
// }
|
|
//
|
|
// const allEndTimes = [...roleTimes, ...additionalPrivilegeTimes];
|
|
// if (allEndTimes.length > 0) {
|
|
// const nearestEndTime = allEndTimes.reduce((min, endTime) => (!min || endTime < min ? endTime : min));
|
|
// const timeUntilExpiry = Math.floor((nearestEndTime.getTime() - now.getTime()) / 1000);
|
|
// minTtl = Math.min(minTtl, Math.max(1, timeUntilExpiry));
|
|
// }
|
|
//
|
|
// return minTtl;
|
|
// };
|
|
|
|
const getOrgPermission: TPermissionServiceFactory["getOrgPermission"] = async (
|
|
type,
|
|
id,
|
|
orgId,
|
|
authMethod,
|
|
actorOrgId
|
|
) => {
|
|
if (type !== ActorType.USER && type !== ActorType.IDENTITY) {
|
|
throw new BadRequestError({
|
|
message: "Invalid actor provided",
|
|
name: "Get org permission"
|
|
});
|
|
}
|
|
|
|
if (orgId !== actorOrgId) {
|
|
throw new ForbiddenRequestError({ name: "You are not logged into this organization" });
|
|
}
|
|
|
|
const permissionData = await permissionDAL.getPermission({
|
|
scopeData: {
|
|
scope: AccessScope.Organization,
|
|
orgId
|
|
},
|
|
actorId: id,
|
|
actorType: type
|
|
});
|
|
if (!permissionData?.length) throw new ForbiddenRequestError({ name: "You are not member of this organization" });
|
|
|
|
const permissionFromRoles = permissionData.flatMap((membership) => {
|
|
const activeRoles = membership?.roles
|
|
.filter(
|
|
({ isTemporary, temporaryAccessEndTime }) =>
|
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
|
)
|
|
.map(({ role, permissions }) => ({ role, permissions }));
|
|
const activeAdditionalPrivileges = membership?.additionalPrivileges
|
|
.filter(
|
|
({ isTemporary, temporaryAccessEndTime }) =>
|
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
|
)
|
|
.map(({ permissions }) => ({ role: OrgMembershipRole.Custom, permissions }));
|
|
return activeRoles.concat(activeAdditionalPrivileges);
|
|
});
|
|
|
|
const permission = createMongoAbility<OrgPermissionSet>(buildOrgPermissionRules(permissionFromRoles), {
|
|
conditionsMatcher
|
|
});
|
|
|
|
// TODO(simp): validate this
|
|
// validateOrgSSO(
|
|
// authMethod,
|
|
// membership.orgAuthEnforced,
|
|
// membership.orgGoogleSsoAuthEnforced,
|
|
// membership.bypassOrgAuthEnabled,
|
|
// membership.role as OrgMembershipRole
|
|
// );
|
|
|
|
return {
|
|
permission,
|
|
memberships: permissionData,
|
|
hasRole: (role: string) =>
|
|
permissionData.some((memberships) => memberships.roles.some((el) => role === (el.customRoleSlug || el.role)))
|
|
};
|
|
};
|
|
|
|
const getServiceTokenProjectPermission = async ({
|
|
serviceTokenId,
|
|
projectId,
|
|
actorOrgId,
|
|
actionProjectType
|
|
}: TGetServiceTokenProjectPermissionArg) => {
|
|
const serviceToken = await serviceTokenDAL.findById(serviceTokenId);
|
|
if (!serviceToken) throw new NotFoundError({ message: `Service token with ID '${serviceTokenId}' not found` });
|
|
|
|
const serviceTokenProject = await projectDAL.findById(serviceToken.projectId);
|
|
|
|
if (!serviceTokenProject) throw new BadRequestError({ message: "Service token not linked to a project" });
|
|
|
|
if (serviceTokenProject.orgId !== actorOrgId) {
|
|
throw new ForbiddenRequestError({ message: "Service token not a part of the specified organization" });
|
|
}
|
|
|
|
if (serviceToken.projectId !== projectId) {
|
|
throw new ForbiddenRequestError({
|
|
name: `Service token not a part of the specified project with ID ${projectId}`
|
|
});
|
|
}
|
|
|
|
if (serviceTokenProject.orgId !== actorOrgId) {
|
|
throw new ForbiddenRequestError({
|
|
message: `Service token not a part of the specified organization with ID ${actorOrgId}`
|
|
});
|
|
}
|
|
|
|
if (actionProjectType !== ActionProjectType.Any && actionProjectType !== serviceTokenProject.type) {
|
|
throw new BadRequestError({
|
|
message: `The project is of type ${serviceTokenProject.type}. Operations of type ${actionProjectType} are not allowed.`
|
|
});
|
|
}
|
|
|
|
const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []);
|
|
return {
|
|
permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions),
|
|
memberships: [],
|
|
hasRole: () => false
|
|
};
|
|
};
|
|
|
|
const getProjectPermission: TPermissionServiceFactory["getProjectPermission"] = async ({
|
|
actor: inputActor,
|
|
actorId: inputActorId,
|
|
projectId,
|
|
actorAuthMethod,
|
|
actorOrgId,
|
|
actionProjectType
|
|
}) => {
|
|
let actor = inputActor;
|
|
let actorId = inputActorId;
|
|
|
|
if (actor === ActorType.SERVICE) {
|
|
return getServiceTokenProjectPermission({
|
|
serviceTokenId: actorId,
|
|
projectId,
|
|
actorOrgId,
|
|
actionProjectType
|
|
});
|
|
}
|
|
|
|
const assumedPrivilegeDetailsCtx = requestContext.get("assumedPrivilegeDetails");
|
|
if (
|
|
assumedPrivilegeDetailsCtx &&
|
|
actor === ActorType.USER &&
|
|
actorId === assumedPrivilegeDetailsCtx.requesterId &&
|
|
projectId === assumedPrivilegeDetailsCtx.projectId
|
|
) {
|
|
actor = assumedPrivilegeDetailsCtx.actorType;
|
|
actorId = assumedPrivilegeDetailsCtx.actorId;
|
|
}
|
|
|
|
if (ActorType.USER !== actor && ActorType.IDENTITY !== actor) {
|
|
throw new BadRequestError({
|
|
message: "Invalid actor provided",
|
|
name: "Get org permission"
|
|
});
|
|
}
|
|
|
|
const projectDetails = await projectDAL.findById(projectId);
|
|
if (!projectDetails) {
|
|
throw new NotFoundError({ message: `Project with ${projectId} not found` });
|
|
}
|
|
|
|
if (projectDetails.orgId !== actorOrgId) {
|
|
throw new ForbiddenRequestError({ name: "You are not logged into this organization" });
|
|
}
|
|
|
|
if (actionProjectType !== ActionProjectType.Any && actionProjectType !== projectDetails.type) {
|
|
throw new BadRequestError({
|
|
message: `The project is of type ${projectDetails.type}. Operations of type ${actionProjectType} are not allowed.`
|
|
});
|
|
}
|
|
|
|
const permissionData = await permissionDAL.getPermission({
|
|
scopeData: {
|
|
scope: AccessScope.Project,
|
|
orgId: projectDetails.orgId,
|
|
projectId
|
|
},
|
|
actorId,
|
|
actorType: actor
|
|
});
|
|
if (!permissionData?.length) throw new ForbiddenRequestError({ name: "You are not member of this organization" });
|
|
|
|
const permissionFromRoles = permissionData.flatMap((membership) => {
|
|
const activeRoles = membership?.roles
|
|
.filter(
|
|
({ isTemporary, temporaryAccessEndTime }) =>
|
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
|
)
|
|
.map(({ role, permissions }) => ({ role, permissions }));
|
|
const activeAdditionalPrivileges = membership?.additionalPrivileges
|
|
.filter(
|
|
({ isTemporary, temporaryAccessEndTime }) =>
|
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
|
)
|
|
.map(({ permissions }) => ({ role: ProjectMembershipRole.Custom, permissions }));
|
|
return activeRoles.concat(activeAdditionalPrivileges);
|
|
});
|
|
|
|
// validateOrgSSO(
|
|
// authMethod,
|
|
// userProjectPermission.orgAuthEnforced,
|
|
// userProjectPermission.orgGoogleSsoAuthEnforced,
|
|
// userProjectPermission.bypassOrgAuthEnabled,
|
|
// userProjectPermission.orgRole
|
|
// );
|
|
|
|
const rules = buildProjectPermissionRules(permissionFromRoles);
|
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
|
const unescapedMetadata = objectify(
|
|
permissionData?.[0]?.metadata,
|
|
(i) => i.key,
|
|
(i) => i.value
|
|
);
|
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
|
requestContext.set("identityPermissionMetadata", { metadata: unescapedMetadata });
|
|
|
|
let username = "";
|
|
if (actor === ActorType.USER) {
|
|
const userDetails = await userDAL.findById(actorId);
|
|
username = userDetails?.username;
|
|
} else {
|
|
const identityDetails = await identityDAL.findById(actorId);
|
|
username = identityDetails?.name;
|
|
}
|
|
|
|
const unescapedIdentityAuthInfo = requestContext.get("identityAuthInfo");
|
|
const identityAuthInfo =
|
|
unescapedIdentityAuthInfo?.identityId === actorId && unescapedIdentityAuthInfo
|
|
? escapeHandlebarsMissingDict(unescapedIdentityAuthInfo as never, "identity.auth")
|
|
: {};
|
|
|
|
const interpolateRules = templatedRules(
|
|
{
|
|
identity: {
|
|
id: actorId,
|
|
username,
|
|
metadata: metadataKeyValuePair,
|
|
auth: identityAuthInfo
|
|
}
|
|
},
|
|
{ data: false }
|
|
);
|
|
|
|
const permission = createMongoAbility<ProjectPermissionSet>(
|
|
JSON.parse(interpolateRules) as RawRuleOf<MongoAbility<ProjectPermissionSet>>[],
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
);
|
|
|
|
return {
|
|
permission,
|
|
memberships: permissionData,
|
|
hasRole: (role: string) =>
|
|
permissionData.some((memberships) => memberships.roles.some((el) => role === (el.customRoleSlug || el.role)))
|
|
};
|
|
};
|
|
|
|
const getProjectPermissions: TPermissionServiceFactory["getProjectPermissions"] = async (projectId, orgId) => {
|
|
// fetch user permissions
|
|
const rawUserProjectPermissions = await permissionDAL.getProjectUserPermissions(projectId, orgId);
|
|
const userPermissions = rawUserProjectPermissions.map((userProjectPermission) => {
|
|
const rolePermissions =
|
|
userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
|
const additionalPrivileges =
|
|
userProjectPermission.additionalPrivileges?.map(({ permissions }) => ({
|
|
role: ProjectMembershipRole.Custom,
|
|
permissions
|
|
})) || [];
|
|
|
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
|
objectify(
|
|
userProjectPermission.metadata,
|
|
(i) => i.key,
|
|
(i) => i.value
|
|
),
|
|
"identity.metadata"
|
|
);
|
|
const interpolateRules = templatedRules(
|
|
{
|
|
identity: {
|
|
id: userProjectPermission.userId,
|
|
username: userProjectPermission.username,
|
|
metadata: metadataKeyValuePair
|
|
}
|
|
},
|
|
{ data: false }
|
|
);
|
|
const permission = createMongoAbility<ProjectPermissionSet>(
|
|
JSON.parse(interpolateRules) as RawRuleOf<MongoAbility<ProjectPermissionSet>>[],
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
);
|
|
|
|
return {
|
|
permission,
|
|
id: userProjectPermission.userId,
|
|
name: userProjectPermission.username
|
|
};
|
|
});
|
|
|
|
// fetch identity permissions
|
|
const rawIdentityProjectPermissions = await permissionDAL.getProjectIdentityPermissions(projectId, orgId);
|
|
const identityPermissions = rawIdentityProjectPermissions.map((identityProjectPermission) => {
|
|
const rolePermissions =
|
|
identityProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
|
const additionalPrivileges =
|
|
identityProjectPermission.additionalPrivileges?.map(({ permissions }) => ({
|
|
role: ProjectMembershipRole.Custom,
|
|
permissions
|
|
})) || [];
|
|
|
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
|
objectify(
|
|
identityProjectPermission.metadata,
|
|
(i) => i.key,
|
|
(i) => i.value
|
|
),
|
|
"identity.metadata"
|
|
);
|
|
const interpolateRules = templatedRules(
|
|
{
|
|
identity: {
|
|
id: identityProjectPermission.identityId,
|
|
username: identityProjectPermission.username,
|
|
metadata: metadataKeyValuePair
|
|
}
|
|
},
|
|
{ data: false }
|
|
);
|
|
const permission = createMongoAbility<ProjectPermissionSet>(
|
|
JSON.parse(interpolateRules) as RawRuleOf<MongoAbility<ProjectPermissionSet>>[],
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
);
|
|
|
|
return {
|
|
permission,
|
|
id: identityProjectPermission.identityId,
|
|
name: identityProjectPermission.username
|
|
};
|
|
});
|
|
|
|
// fetch group permissions
|
|
const rawGroupProjectPermissions = await permissionDAL.getProjectGroupPermissions(projectId);
|
|
const groupPermissions = rawGroupProjectPermissions.map((groupProjectPermission) => {
|
|
const rolePermissions =
|
|
groupProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
|
const rules = buildProjectPermissionRules(rolePermissions);
|
|
const permission = createMongoAbility<ProjectPermissionSet>(rules, {
|
|
conditionsMatcher
|
|
});
|
|
|
|
return {
|
|
permission,
|
|
id: groupProjectPermission.groupId,
|
|
name: groupProjectPermission.username
|
|
};
|
|
});
|
|
|
|
return {
|
|
userPermissions,
|
|
identityPermissions,
|
|
groupPermissions
|
|
};
|
|
};
|
|
|
|
// instead of actor type this will fetch by role slug. meaning it can be the pre defined slugs like
|
|
// admin member or user defined ones like biller etc
|
|
const getOrgPermissionByRoles: TPermissionServiceFactory["getOrgPermissionByRoles"] = async (roles, orgId) => {
|
|
const formattedRoles = roles.map((role) => ({
|
|
name: role,
|
|
isCustom: !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole)
|
|
}));
|
|
|
|
const customRoles = formattedRoles.filter((el) => el.isCustom).map((el) => el.name);
|
|
const customRoleDetails = customRoles.length
|
|
? await roleDAL.find({
|
|
orgId,
|
|
$in: {
|
|
slug: customRoles
|
|
}
|
|
})
|
|
: [];
|
|
if (customRoles.length !== customRoleDetails.length) {
|
|
const missingRoles = customRoles.filter((role) => !customRoleDetails.find((el) => el.slug === role));
|
|
throw new NotFoundError({
|
|
message: `Specified roles '${missingRoles.join(",")}' was not found in the organization with ID '${orgId}'`
|
|
});
|
|
}
|
|
|
|
return formattedRoles.map((el) => {
|
|
if (el.isCustom) {
|
|
const roleDetails = customRoleDetails.find((role) => role.slug === el.name);
|
|
return {
|
|
permission: createMongoAbility<OrgPermissionSet>(
|
|
buildOrgPermissionRules([{ role: OrgMembershipRole.Custom, permissions: roleDetails?.permissions || [] }]),
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
),
|
|
role: roleDetails!
|
|
};
|
|
}
|
|
|
|
return {
|
|
permission: createMongoAbility<OrgPermissionSet>(
|
|
buildOrgPermissionRules([{ role: el.name, permissions: [] }]),
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
)
|
|
};
|
|
});
|
|
};
|
|
|
|
const getProjectPermissionByRoles: TPermissionServiceFactory["getProjectPermissionByRoles"] = async (
|
|
roles,
|
|
projectId
|
|
) => {
|
|
const formattedRoles = roles.map((role) => ({
|
|
name: role,
|
|
isCustom: !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole)
|
|
}));
|
|
|
|
const customRoles = formattedRoles.filter((el) => el.isCustom).map((el) => el.name);
|
|
const customRoleDetails = customRoles.length
|
|
? await roleDAL.find({
|
|
projectId,
|
|
$in: {
|
|
slug: customRoles
|
|
}
|
|
})
|
|
: [];
|
|
if (customRoles.length !== customRoleDetails.length) {
|
|
const missingRoles = customRoles.filter((role) => !customRoleDetails.find((el) => el.slug === role));
|
|
throw new NotFoundError({
|
|
message: `Specified roles '${missingRoles.join(",")}' was not found in the project with ID '${projectId}'`
|
|
});
|
|
}
|
|
|
|
return formattedRoles.map((el) => {
|
|
if (el.isCustom) {
|
|
const roleDetails = customRoleDetails.find((role) => role.slug === el.name);
|
|
return {
|
|
permission: createMongoAbility<ProjectPermissionSet>(
|
|
buildProjectPermissionRules([
|
|
{ role: ProjectMembershipRole.Custom, permissions: roleDetails?.permissions || [] }
|
|
]),
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
),
|
|
role: roleDetails!
|
|
};
|
|
}
|
|
|
|
return {
|
|
permission: createMongoAbility<ProjectPermissionSet>(
|
|
buildProjectPermissionRules([{ role: el.name, permissions: [] }]),
|
|
{
|
|
conditionsMatcher
|
|
}
|
|
)
|
|
};
|
|
});
|
|
};
|
|
|
|
const checkGroupProjectPermission: TPermissionServiceFactory["checkGroupProjectPermission"] = async ({
|
|
groupId,
|
|
projectId,
|
|
checkPermissions
|
|
}) => {
|
|
const rawGroupProjectPermissions = await permissionDAL.getProjectGroupPermissions(projectId, groupId);
|
|
const groupPermissions = rawGroupProjectPermissions.map((groupProjectPermission) => {
|
|
const rolePermissions =
|
|
groupProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
|
const rules = buildProjectPermissionRules(rolePermissions);
|
|
const permission = createMongoAbility<ProjectPermissionSet>(rules, {
|
|
conditionsMatcher
|
|
});
|
|
|
|
return {
|
|
permission,
|
|
id: groupProjectPermission.groupId,
|
|
name: groupProjectPermission.username,
|
|
membershipId: groupProjectPermission.id
|
|
};
|
|
});
|
|
return groupPermissions.some((groupPermission) => groupPermission.permission.can(...checkPermissions));
|
|
};
|
|
|
|
return {
|
|
getOrgPermission,
|
|
getProjectPermission,
|
|
getProjectPermissions,
|
|
getOrgPermissionByRoles,
|
|
getProjectPermissionByRoles,
|
|
checkGroupProjectPermission,
|
|
invalidateProjectPermissionCache
|
|
};
|
|
};
|