mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: completed membership group identity
This commit is contained in:
268
backend/src/services/membership-group/membership-group-dal.ts
Normal file
268
backend/src/services/membership-group/membership-group-dal.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { AccessScope, AccessScopeData, MembershipsSchema, TableName } from "@app/db/schemas";
|
||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
|
||||
import { buildKnexFilterForSearchResource } from "@app/lib/search-resource/db";
|
||||
import { TSearchResourceOperator } from "@app/lib/search-resource/search";
|
||||
|
||||
export type TMembershipGroupDALFactory = ReturnType<typeof membershipGroupDALFactory>;
|
||||
|
||||
type TFindGroupArg = {
|
||||
scopeData: AccessScopeData;
|
||||
tx?: Knex;
|
||||
filter: Partial<{
|
||||
limit: number;
|
||||
offset: number;
|
||||
groupId: string;
|
||||
name: Omit<TSearchResourceOperator, "number">;
|
||||
role: Omit<TSearchResourceOperator, "number">;
|
||||
}>;
|
||||
};
|
||||
|
||||
type TGetGroupByIdArg = {
|
||||
scopeData: AccessScopeData;
|
||||
tx?: Knex;
|
||||
groupId: string;
|
||||
};
|
||||
|
||||
export const membershipGroupDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.Membership);
|
||||
|
||||
const getGroupById = async ({ scopeData, tx, groupId }: TGetGroupByIdArg) => {
|
||||
try {
|
||||
const docs = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.whereNotNull(`${TableName.Membership}.actorGroupId`)
|
||||
.join(TableName.Groups, `${TableName.Groups}.id`, `${TableName.Membership}.actorGroupId`)
|
||||
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
|
||||
.where(`${TableName.Membership}.actorGroupId`, groupId)
|
||||
.where((qb) => {
|
||||
if (scopeData.scope === AccessScope.Organization) {
|
||||
void qb.where(`${TableName.Membership}.scope`, AccessScope.Organization);
|
||||
} else if (scopeData.scope === AccessScope.Namespace) {
|
||||
void qb
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Namespace)
|
||||
.where(`${TableName.Membership}.scopeNamespaceId`, scopeData.namespaceId)
|
||||
.whereNull(`${TableName.Membership}.scopeNamespaceId`);
|
||||
} else if (scopeData.scope === AccessScope.Project) {
|
||||
void qb
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, scopeData.projectId);
|
||||
}
|
||||
})
|
||||
.select(selectAllTableCols(TableName.Membership))
|
||||
.select(
|
||||
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
||||
db.ref("slug").withSchema(TableName.Groups).as("groupSlug"),
|
||||
db.ref("slug").withSchema(TableName.Role).as("roleSlug"),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole).as("membershipRole"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryMode"),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole).as("membershipRoleIsTemporary"),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryRange"),
|
||||
db
|
||||
.ref("temporaryAccessStartTime")
|
||||
.withSchema(TableName.MembershipRole)
|
||||
.as("membershipRoleTemporaryAccessStartTime"),
|
||||
db
|
||||
.ref("temporaryAccessEndTime")
|
||||
.withSchema(TableName.MembershipRole)
|
||||
.as("membershipRoleTemporaryAccessEndTime"),
|
||||
db.ref("createdAt").withSchema(TableName.MembershipRole).as("membershipRoleCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.MembershipRole).as("membershipRoleUpdatedAt")
|
||||
);
|
||||
|
||||
const data = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (el) => {
|
||||
const { groupName, groupSlug } = el;
|
||||
return {
|
||||
...MembershipsSchema.parse(el),
|
||||
group: {
|
||||
id: groupId,
|
||||
name: groupName,
|
||||
slug: groupSlug
|
||||
}
|
||||
};
|
||||
},
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "membershipRoleId",
|
||||
label: "roles" as const,
|
||||
mapper: ({
|
||||
roleSlug,
|
||||
membershipRoleId,
|
||||
membershipRole,
|
||||
membershipRoleIsTemporary,
|
||||
membershipRoleTemporaryMode,
|
||||
membershipRoleTemporaryRange,
|
||||
membershipRoleTemporaryAccessEndTime,
|
||||
membershipRoleTemporaryAccessStartTime,
|
||||
membershipRoleCreatedAt,
|
||||
membershipRoleUpdatedAt
|
||||
}) => ({
|
||||
id: membershipRoleId,
|
||||
role: membershipRole,
|
||||
customRoleSlug: roleSlug,
|
||||
temporaryRange: membershipRoleTemporaryRange,
|
||||
temporaryMode: membershipRoleTemporaryMode,
|
||||
temporaryAccessStartTime: membershipRoleTemporaryAccessStartTime,
|
||||
temporaryAccessEndTime: membershipRoleTemporaryAccessEndTime,
|
||||
isTemporary: membershipRoleIsTemporary,
|
||||
createdAt: membershipRoleCreatedAt,
|
||||
updatedAt: membershipRoleUpdatedAt
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return data?.[0];
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "MembershipGetByGroupId" });
|
||||
}
|
||||
};
|
||||
|
||||
const findGroups = async ({ scopeData, tx, filter }: TFindGroupArg) => {
|
||||
try {
|
||||
const paginatedGroups = (tx || db.replicaNode())(TableName.Membership)
|
||||
.whereNotNull(`${TableName.Membership}.actorGroupId`)
|
||||
.join(TableName.Groups, `${TableName.Groups}.id`, `${TableName.Membership}.actorGroupId`)
|
||||
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.distinct(`${TableName.Membership}.id`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
|
||||
.where((qb) => {
|
||||
if (filter.groupId) {
|
||||
void qb.where(`${TableName.Groups}.id`, filter.groupId);
|
||||
}
|
||||
|
||||
if (scopeData.scope === AccessScope.Organization) {
|
||||
void qb.where(`${TableName.Membership}.scope`, AccessScope.Organization);
|
||||
} else if (scopeData.scope === AccessScope.Namespace) {
|
||||
void qb
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Namespace)
|
||||
.where(`${TableName.Membership}.scopeNamespaceId`, scopeData.namespaceId)
|
||||
.whereNull(`${TableName.Membership}.scopeNamespaceId`);
|
||||
} else if (scopeData.scope === AccessScope.Project) {
|
||||
void qb
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||
.where(`${TableName.Membership}.scopeProjectId`, scopeData.projectId);
|
||||
}
|
||||
});
|
||||
|
||||
if (filter.limit) void paginatedGroups.limit(filter.limit);
|
||||
if (filter.offset) void paginatedGroups.offset(filter.offset);
|
||||
|
||||
if (filter.name || filter.role) {
|
||||
buildKnexFilterForSearchResource(
|
||||
paginatedGroups,
|
||||
{
|
||||
name: filter.name!,
|
||||
role: filter.role!
|
||||
},
|
||||
(attr) => {
|
||||
switch (attr) {
|
||||
case "role":
|
||||
return [`${TableName.Role}.slug`, `${TableName.MembershipRole}.role`];
|
||||
case "name":
|
||||
return `${TableName.Groups}.name`;
|
||||
default:
|
||||
throw new BadRequestError({ message: `Invalid ${String(attr)} provided` });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const docs = await (tx || db.replicaNode())(TableName.Membership)
|
||||
.whereNotNull(`${TableName.Membership}.actorGroupId`)
|
||||
.join(TableName.Groups, `${TableName.Groups}.id`, `${TableName.Membership}.actorGroupId`)
|
||||
.join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.distinct(`${TableName.Membership}.id`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
|
||||
.whereIn(`${TableName.Membership}.id`, paginatedGroups)
|
||||
.select(selectAllTableCols(TableName.Membership))
|
||||
.select(
|
||||
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
||||
db.ref("slug").withSchema(TableName.Groups).as("groupSlug"),
|
||||
db.ref("id").withSchema(TableName.Groups).as("groupId"),
|
||||
|
||||
db.ref("slug").withSchema(TableName.Role).as("roleSlug"),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("role").withSchema(TableName.MembershipRole).as("membershipRole"),
|
||||
db.ref("temporaryMode").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryMode"),
|
||||
db.ref("isTemporary").withSchema(TableName.MembershipRole).as("membershipRoleIsTemporary"),
|
||||
db.ref("temporaryRange").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryRange"),
|
||||
db
|
||||
.ref("temporaryAccessStartTime")
|
||||
.withSchema(TableName.MembershipRole)
|
||||
.as("membershipRoleTemporaryAccessStartTime"),
|
||||
db
|
||||
.ref("temporaryAccessEndTime")
|
||||
.withSchema(TableName.MembershipRole)
|
||||
.as("membershipRoleTemporaryAccessEndTime"),
|
||||
db.ref("createdAt").withSchema(TableName.MembershipRole).as("membershipRoleCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.MembershipRole).as("membershipRoleUpdatedAt")
|
||||
)
|
||||
.select(
|
||||
db.raw(
|
||||
`count(${TableName.Membership}."actorGroupId") OVER(PARTITION BY ${TableName.Membership}."scopeOrgId") as total`
|
||||
)
|
||||
);
|
||||
|
||||
const data = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (el) => {
|
||||
const { groupId, groupName, groupSlug } = el;
|
||||
return {
|
||||
...MembershipsSchema.parse(el),
|
||||
group: {
|
||||
id: groupId,
|
||||
name: groupName,
|
||||
slug: groupSlug
|
||||
}
|
||||
};
|
||||
},
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "membershipRoleId",
|
||||
label: "roles" as const,
|
||||
mapper: ({
|
||||
roleSlug,
|
||||
membershipRoleId,
|
||||
membershipRole,
|
||||
membershipRoleIsTemporary,
|
||||
membershipRoleTemporaryMode,
|
||||
membershipRoleTemporaryRange,
|
||||
membershipRoleTemporaryAccessEndTime,
|
||||
membershipRoleTemporaryAccessStartTime,
|
||||
membershipRoleCreatedAt,
|
||||
membershipRoleUpdatedAt
|
||||
}) => ({
|
||||
id: membershipRoleId,
|
||||
role: membershipRole,
|
||||
customRoleSlug: roleSlug,
|
||||
temporaryRange: membershipRoleTemporaryRange,
|
||||
temporaryMode: membershipRoleTemporaryMode,
|
||||
temporaryAccessStartTime: membershipRoleTemporaryAccessStartTime,
|
||||
temporaryAccessEndTime: membershipRoleTemporaryAccessEndTime,
|
||||
isTemporary: membershipRoleIsTemporary,
|
||||
createdAt: membershipRoleCreatedAt,
|
||||
updatedAt: membershipRoleUpdatedAt
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
return { data, totalCount: Number((data?.[0] as unknown as { total: number })?.total ?? 0) };
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "MembershipfindGroup" });
|
||||
}
|
||||
};
|
||||
|
||||
return { ...orm, findGroups, getGroupById };
|
||||
};
|
||||
@@ -0,0 +1,303 @@
|
||||
import { ProjectMembershipRole, TemporaryPermissionMode, TMembershipRolesInsert } from "@app/db/schemas";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { SearchResourceOperators } from "@app/lib/search-resource/search";
|
||||
|
||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||
import { TRoleDALFactory } from "../role/role-dal";
|
||||
import { TMembershipGroupDALFactory } from "./membership-group-dal";
|
||||
import {
|
||||
TCreateMembershipGroupDTO,
|
||||
TDeleteMembershipGroupDTO,
|
||||
TGetMembershipGroupByGroupIdDTO,
|
||||
TListMembershipGroupDTO,
|
||||
TMembershipGroupScopeFactory,
|
||||
TUpdateMembershipGroupDTO
|
||||
} from "./membership-group-types";
|
||||
|
||||
type TMembershipGroupServiceFactoryDep = {
|
||||
membershipGroupDAL: TMembershipGroupDALFactory;
|
||||
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "insertMany" | "delete">;
|
||||
roleDAL: Pick<TRoleDALFactory, "find">;
|
||||
};
|
||||
|
||||
export type TMembershipGroupServiceFactory = ReturnType<typeof membershipGroupServiceFactory>;
|
||||
|
||||
export const membershipGroupServiceFactory = ({
|
||||
membershipGroupDAL,
|
||||
roleDAL,
|
||||
membershipRoleDAL
|
||||
}: TMembershipGroupServiceFactoryDep) => {
|
||||
const scopeFactory: Record<string, TMembershipGroupScopeFactory> = {};
|
||||
|
||||
const createMembership = async (dto: TCreateMembershipGroupDTO) => {
|
||||
const { scopeData, data } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
const hasOnePermanentRole = data.roles.some((el) => el.isTemporary);
|
||||
if (hasOnePermanentRole) {
|
||||
throw new BadRequestError({
|
||||
message: "Group must have atleast one permanent role"
|
||||
});
|
||||
}
|
||||
const isInvalidTemporaryRole = data.roles.some((el) => {
|
||||
if (el.isTemporary) {
|
||||
if (!el.temporaryAccessStartTime || !el.temporaryRange) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (isInvalidTemporaryRole) {
|
||||
throw new BadRequestError({
|
||||
message: "Temporary role must have access start time and range"
|
||||
});
|
||||
}
|
||||
|
||||
const scopeDatabaseFields = factory.getScopeDatabaseFields(dto.scopeData);
|
||||
await factory.onCreateMembershipGroupGuard(dto);
|
||||
|
||||
const customInputRoles = data.roles.filter((el) => factory.isCustomRole(el.role));
|
||||
const hasCustomRole = customInputRoles.length > 0;
|
||||
|
||||
const scopeField = factory.getScopeField(dto.scopeData);
|
||||
const customRoles = hasCustomRole
|
||||
? await roleDAL.find({
|
||||
[scopeField.key]: scopeField.value,
|
||||
$in: { slug: customInputRoles.map(({ role }) => role) }
|
||||
})
|
||||
: [];
|
||||
if (customRoles.length !== customInputRoles.length) {
|
||||
throw new NotFoundError({ message: "One or more custom roles not found" });
|
||||
}
|
||||
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
const membership = await membershipGroupDAL.transaction(async (tx) => {
|
||||
const doc = await membershipGroupDAL.create(
|
||||
{
|
||||
scope: scopeData.scope,
|
||||
...scopeDatabaseFields,
|
||||
actorGroupId: dto.data.groupId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const roleDocs: TMembershipRolesInsert[] = [];
|
||||
data.roles.forEach((membershipRole) => {
|
||||
const isCustomRole = Boolean(customRolesGroupBySlug?.[membershipRole.role]?.[0]);
|
||||
if (membershipRole.isTemporary) {
|
||||
const relativeTimeInMs = membershipRole.temporaryRange ? ms(membershipRole.temporaryRange) : null;
|
||||
roleDocs.push({
|
||||
membershipId: doc.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : membershipRole.role,
|
||||
customRoleId: customRolesGroupBySlug[membershipRole.role]
|
||||
? customRolesGroupBySlug[membershipRole.role][0].id
|
||||
: null,
|
||||
isTemporary: true,
|
||||
temporaryMode: TemporaryPermissionMode.Relative,
|
||||
temporaryRange: membershipRole.temporaryRange,
|
||||
temporaryAccessStartTime: new Date(membershipRole.temporaryAccessStartTime as string),
|
||||
temporaryAccessEndTime: new Date(
|
||||
new Date(membershipRole.temporaryAccessStartTime as string).getTime() + (relativeTimeInMs as number)
|
||||
)
|
||||
});
|
||||
} else {
|
||||
roleDocs.push({
|
||||
membershipId: doc.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : membershipRole.role,
|
||||
customRoleId: customRolesGroupBySlug[membershipRole.role]
|
||||
? customRolesGroupBySlug[membershipRole.role][0].id
|
||||
: null
|
||||
});
|
||||
}
|
||||
});
|
||||
await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return doc;
|
||||
});
|
||||
|
||||
return { membership };
|
||||
};
|
||||
|
||||
const updateMembership = async (dto: TUpdateMembershipGroupDTO) => {
|
||||
const { scopeData, data } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
await factory.onUpdateMembershipGroupGuard(dto);
|
||||
|
||||
const customInputRoles = data.roles.filter((el) => factory.isCustomRole(el.role));
|
||||
const hasCustomRole = customInputRoles.length > 0;
|
||||
|
||||
const hasOnePermanentRole = data.roles.some((el) => el.isTemporary);
|
||||
if (hasOnePermanentRole) {
|
||||
throw new BadRequestError({
|
||||
message: "Group must have atleast one permanent role"
|
||||
});
|
||||
}
|
||||
const isInvalidTemporaryRole = data.roles.some((el) => {
|
||||
if (el.isTemporary) {
|
||||
if (!el.temporaryAccessStartTime || !el.temporaryRange) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (isInvalidTemporaryRole) {
|
||||
throw new BadRequestError({
|
||||
message: "Temporary role must have access start time and range"
|
||||
});
|
||||
}
|
||||
|
||||
const scopeDatabaseFields = factory.getScopeDatabaseFields(dto.scopeData);
|
||||
const existingMembership = await membershipGroupDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
...scopeDatabaseFields,
|
||||
actorIdentityId: dto.selector.groupId
|
||||
});
|
||||
if (!existingMembership)
|
||||
throw new BadRequestError({
|
||||
message: "Group doesn't have membership"
|
||||
});
|
||||
|
||||
const scopeField = factory.getScopeField(dto.scopeData);
|
||||
const customRoles = hasCustomRole
|
||||
? await roleDAL.find({
|
||||
[scopeField.key]: scopeField.value,
|
||||
$in: { slug: customInputRoles.map(({ role }) => role) }
|
||||
})
|
||||
: [];
|
||||
if (customRoles.length !== customInputRoles.length) {
|
||||
throw new NotFoundError({ message: "One or more custom roles not found" });
|
||||
}
|
||||
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
const membershipDoc = await membershipGroupDAL.transaction(async (tx) => {
|
||||
const doc = await membershipGroupDAL.updateById(
|
||||
existingMembership.id,
|
||||
{
|
||||
isActive: data.isActive
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const roleDocs: TMembershipRolesInsert[] = [];
|
||||
data.roles.forEach((membershipRole) => {
|
||||
const isCustomRole = Boolean(customRolesGroupBySlug?.[membershipRole.role]?.[0]);
|
||||
if (membershipRole.isTemporary) {
|
||||
const relativeTimeInMs = membershipRole.temporaryRange ? ms(membershipRole.temporaryRange) : null;
|
||||
roleDocs.push({
|
||||
membershipId: doc.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : membershipRole.role,
|
||||
customRoleId: customRolesGroupBySlug[membershipRole.role]
|
||||
? customRolesGroupBySlug[membershipRole.role][0].id
|
||||
: null,
|
||||
isTemporary: true,
|
||||
temporaryMode: TemporaryPermissionMode.Relative,
|
||||
temporaryRange: membershipRole.temporaryRange,
|
||||
temporaryAccessStartTime: new Date(membershipRole.temporaryAccessStartTime as string),
|
||||
temporaryAccessEndTime: new Date(
|
||||
new Date(membershipRole.temporaryAccessStartTime as string).getTime() + (relativeTimeInMs as number)
|
||||
)
|
||||
});
|
||||
} else {
|
||||
roleDocs.push({
|
||||
membershipId: doc.id,
|
||||
role: isCustomRole ? ProjectMembershipRole.Custom : membershipRole.role,
|
||||
customRoleId: customRolesGroupBySlug[membershipRole.role]
|
||||
? customRolesGroupBySlug[membershipRole.role][0].id
|
||||
: null
|
||||
});
|
||||
}
|
||||
});
|
||||
await membershipRoleDAL.delete(
|
||||
{
|
||||
membershipId: doc.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return doc;
|
||||
});
|
||||
|
||||
return { membership: membershipDoc };
|
||||
};
|
||||
|
||||
const deleteMembership = async (dto: TDeleteMembershipGroupDTO) => {
|
||||
const { scopeData } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
await factory.onDeleteMembershipGroupGuard(dto);
|
||||
|
||||
const scopeDatabaseFields = factory.getScopeDatabaseFields(dto.scopeData);
|
||||
const existingMembership = await membershipGroupDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
...scopeDatabaseFields,
|
||||
actorIdentityId: dto.selector.groupId
|
||||
});
|
||||
if (!existingMembership)
|
||||
throw new BadRequestError({
|
||||
message: "Group doesn't have membership"
|
||||
});
|
||||
|
||||
if (existingMembership.actorIdentityId === dto.permission.id)
|
||||
throw new BadRequestError({
|
||||
message: "You can't delete you own membership"
|
||||
});
|
||||
|
||||
const membershipDoc = await membershipGroupDAL.transaction(async (tx) => {
|
||||
await membershipRoleDAL.delete({ membershipId: existingMembership.id }, tx);
|
||||
const doc = await membershipGroupDAL.deleteById(existingMembership.id, tx);
|
||||
return doc;
|
||||
});
|
||||
return { membership: membershipDoc };
|
||||
};
|
||||
|
||||
const listMemberships = async (dto: TListMembershipGroupDTO) => {
|
||||
const { scopeData } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
await factory.onListMembershipGroupGuard(dto);
|
||||
const memberships = await membershipGroupDAL.findGroups({
|
||||
scopeData,
|
||||
filter: {
|
||||
limit: dto.data.limit,
|
||||
offset: dto.data.offset,
|
||||
name: dto.data.groupName
|
||||
? {
|
||||
[SearchResourceOperators.$contains]: dto.data.groupName
|
||||
}
|
||||
: undefined,
|
||||
role: dto.data.roles.length
|
||||
? {
|
||||
[SearchResourceOperators.$in]: dto.data.roles
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
});
|
||||
return memberships;
|
||||
};
|
||||
|
||||
const getMembershipByGroupId = async (dto: TGetMembershipGroupByGroupIdDTO) => {
|
||||
const { scopeData, selector } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
await factory.onGetMembershipGroupByGroupIdGuard(dto);
|
||||
const membership = await membershipGroupDAL.getGroupById({
|
||||
scopeData,
|
||||
groupId: selector.groupId
|
||||
});
|
||||
if (!membership) throw new NotFoundError({ message: `Group membership not found` });
|
||||
|
||||
return membership;
|
||||
};
|
||||
|
||||
return {
|
||||
createMembership,
|
||||
updateMembership,
|
||||
deleteMembership,
|
||||
listMemberships,
|
||||
getMembershipByGroupId
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import { AccessScopeData, TemporaryPermissionMode } from "@app/db/schemas";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
export interface TMembershipGroupScopeFactory {
|
||||
onCreateMembershipGroupGuard: (arg: TCreateMembershipGroupDTO) => Promise<void>;
|
||||
|
||||
onUpdateMembershipGroupGuard: (arg: TUpdateMembershipGroupDTO) => Promise<void>;
|
||||
onDeleteMembershipGroupGuard: (arg: TDeleteMembershipGroupDTO) => Promise<void>;
|
||||
onListMembershipGroupGuard: (arg: TListMembershipGroupDTO) => Promise<void>;
|
||||
onGetMembershipGroupByGroupIdGuard: (arg: TGetMembershipGroupByGroupIdDTO) => Promise<void>;
|
||||
getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string };
|
||||
getScopeDatabaseFields: (scope: AccessScopeData) => {
|
||||
scopeOrgId: string;
|
||||
scopeNamespaceId?: string | null;
|
||||
scopeProjectId?: string | null;
|
||||
};
|
||||
isCustomRole: (role: string) => boolean;
|
||||
}
|
||||
|
||||
export type TCreateMembershipGroupDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
data: {
|
||||
groupId: string;
|
||||
roles: {
|
||||
role: string;
|
||||
isTemporary: boolean;
|
||||
temporaryMode?: TemporaryPermissionMode.Relative;
|
||||
temporaryRange?: string;
|
||||
temporaryAccessStartTime?: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
|
||||
export type TUpdateMembershipGroupDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
groupId: string;
|
||||
};
|
||||
data: {
|
||||
isActive?: boolean;
|
||||
metadata?: { key: string; value: string }[];
|
||||
roles: {
|
||||
role: string;
|
||||
isTemporary: boolean;
|
||||
temporaryMode?: TemporaryPermissionMode.Relative;
|
||||
temporaryRange?: string;
|
||||
temporaryAccessStartTime?: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
|
||||
export type TListMembershipGroupDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
groupId: string;
|
||||
};
|
||||
data: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
groupName?: string;
|
||||
roles: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type TDeleteMembershipGroupDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
groupId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type TGetMembershipGroupByGroupIdDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
groupId: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
import { AccessScope } from "@app/db/schemas";
|
||||
import { InternalServerError } from "@app/lib/errors";
|
||||
|
||||
import { TMembershipGroupScopeFactory } from "../membership-group-types";
|
||||
|
||||
type TNamespaceMembershipGroupScopeFactoryDep = Record<string, never>;
|
||||
|
||||
export const newNamespaceMembershipGroupFactory = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
deps: TNamespaceMembershipGroupScopeFactoryDep
|
||||
): TMembershipGroupScopeFactory => {
|
||||
const getScopeField: TMembershipGroupScopeFactory["getScopeField"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Namespace) {
|
||||
return { key: "namespaceId" as const, value: dto.namespaceId };
|
||||
}
|
||||
throw new InternalServerError({ message: "Invalid scope provided for the namespace factory" });
|
||||
};
|
||||
|
||||
const getScopeDatabaseFields: TMembershipGroupScopeFactory["getScopeDatabaseFields"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Namespace) {
|
||||
return { scopeOrgId: dto.orgId, scopeNamespaceId: dto.namespaceId };
|
||||
}
|
||||
throw new InternalServerError({ message: "Invalid scope provided for the namespace factory" });
|
||||
};
|
||||
|
||||
const isCustomRole: TMembershipGroupScopeFactory["isCustomRole"] = () => {
|
||||
throw new InternalServerError({ message: "Namespace membership group isCustomRole not implemented" });
|
||||
};
|
||||
|
||||
const onCreateMembershipGroupGuard: TMembershipGroupScopeFactory["onCreateMembershipGroupGuard"] = async () => {
|
||||
throw new InternalServerError({ message: "Namespace membership group create not implemented" });
|
||||
};
|
||||
|
||||
const onUpdateMembershipGroupGuard: TMembershipGroupScopeFactory["onUpdateMembershipGroupGuard"] = async () => {
|
||||
throw new InternalServerError({ message: "Namespace membership group update not implemented" });
|
||||
};
|
||||
|
||||
const onDeleteMembershipGroupGuard: TMembershipGroupScopeFactory["onDeleteMembershipGroupGuard"] = async () => {
|
||||
throw new InternalServerError({ message: "Namespace membership group delete not implemented" });
|
||||
};
|
||||
|
||||
const onListMembershipGroupGuard: TMembershipGroupScopeFactory["onListMembershipGroupGuard"] = async () => {
|
||||
throw new InternalServerError({ message: "Namespace membership group list not implemented" });
|
||||
};
|
||||
|
||||
const onGetMembershipGroupByGroupIdGuard: TMembershipGroupScopeFactory["onGetMembershipGroupByGroupIdGuard"] =
|
||||
async () => {
|
||||
throw new InternalServerError({ message: "Namespace membership group get by group id not implemented" });
|
||||
};
|
||||
|
||||
return {
|
||||
onCreateMembershipGroupGuard,
|
||||
onUpdateMembershipGroupGuard,
|
||||
onDeleteMembershipGroupGuard,
|
||||
onListMembershipGroupGuard,
|
||||
onGetMembershipGroupByGroupIdGuard,
|
||||
getScopeField,
|
||||
getScopeDatabaseFields,
|
||||
isCustomRole
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { OrgPermissionGroupActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
constructPermissionErrorMessage,
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { BadRequestError, InternalServerError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { isCustomOrgRole } from "@app/services/org/org-role-fns";
|
||||
|
||||
import { TMembershipGroupScopeFactory } from "../membership-group-types";
|
||||
|
||||
type TOrgMembershipGroupScopeFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRoles">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
|
||||
export const newOrgMembershipGroupFactory = ({
|
||||
permissionService,
|
||||
orgDAL
|
||||
}: TOrgMembershipGroupScopeFactoryDep): TMembershipGroupScopeFactory => {
|
||||
const getScopeField: TMembershipGroupScopeFactory["getScopeField"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Organization) {
|
||||
return { key: "orgId" as const, value: dto.orgId };
|
||||
}
|
||||
throw new InternalServerError({ message: "Invalid scope provided for the org factory" });
|
||||
};
|
||||
|
||||
const getScopeDatabaseFields: TMembershipGroupScopeFactory["getScopeDatabaseFields"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Organization) {
|
||||
return { scopeOrgId: dto.orgId };
|
||||
}
|
||||
throw new InternalServerError({ message: "Invalid scope provided for the org factory" });
|
||||
};
|
||||
|
||||
const isCustomRole: TMembershipGroupScopeFactory["isCustomRole"] = (role: string) => isCustomOrgRole(role);
|
||||
|
||||
const onCreateMembershipGroupGuard: TMembershipGroupScopeFactory["onCreateMembershipGroupGuard"] = async () => {
|
||||
throw new BadRequestError({
|
||||
message: "Organizatin membership cannot be created for groups"
|
||||
});
|
||||
};
|
||||
|
||||
const onUpdateMembershipGroupGuard: TMembershipGroupScopeFactory["onUpdateMembershipGroupGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||
const permissionRoles = await permissionService.getOrgPermissionByRoles(
|
||||
dto.data.roles.map((el) => el.role),
|
||||
dto.permission.orgId
|
||||
);
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId);
|
||||
for (const permissionRole of permissionRoles) {
|
||||
if (permissionRole?.role?.name !== OrgMembershipRole.NoAccess) {
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionGroupActions.GrantPrivileges,
|
||||
OrgPermissionSubjects.Groups,
|
||||
permission,
|
||||
permissionRole.permission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create group org membership",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionGroupActions.GrantPrivileges,
|
||||
OrgPermissionSubjects.Groups
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteMembershipGroupGuard: TMembershipGroupScopeFactory["onDeleteMembershipGroupGuard"] = async () => {
|
||||
throw new BadRequestError({
|
||||
message: "Organizatin membership cannot be created for organization scoped group"
|
||||
});
|
||||
};
|
||||
|
||||
const onListMembershipGroupGuard: TMembershipGroupScopeFactory["onListMembershipGroupGuard"] = async (dto) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||
};
|
||||
|
||||
const onGetMembershipGroupByGroupIdGuard: TMembershipGroupScopeFactory["onGetMembershipGroupByGroupIdGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
dto.permission.type,
|
||||
dto.permission.id,
|
||||
dto.permission.orgId,
|
||||
dto.permission.authMethod,
|
||||
dto.permission.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||
};
|
||||
|
||||
return {
|
||||
onCreateMembershipGroupGuard,
|
||||
onUpdateMembershipGroupGuard,
|
||||
onDeleteMembershipGroupGuard,
|
||||
onListMembershipGroupGuard,
|
||||
onGetMembershipGroupByGroupIdGuard,
|
||||
getScopeField,
|
||||
getScopeDatabaseFields,
|
||||
isCustomRole
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,188 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { AccessScope, ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||
import {
|
||||
constructPermissionErrorMessage,
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import {
|
||||
isCustomProjectRole,
|
||||
ProjectPermissionGroupActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, InternalServerError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
|
||||
import { TMembershipGroupDALFactory } from "../membership-group-dal";
|
||||
import { TMembershipGroupScopeFactory } from "../membership-group-types";
|
||||
|
||||
type TProjectMembershipGroupScopeFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "findOne">;
|
||||
};
|
||||
|
||||
export const newProjectMembershipGroupFactory = ({
|
||||
permissionService,
|
||||
orgDAL,
|
||||
membershipGroupDAL
|
||||
}: TProjectMembershipGroupScopeFactoryDep): TMembershipGroupScopeFactory => {
|
||||
const getScopeField: TMembershipGroupScopeFactory["getScopeField"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Project) {
|
||||
return { key: "projectId" as const, value: dto.projectId };
|
||||
}
|
||||
throw new InternalServerError({ message: "Invalid scope provided for the project factory" });
|
||||
};
|
||||
|
||||
const getScopeDatabaseFields: TMembershipGroupScopeFactory["getScopeDatabaseFields"] = (dto) => {
|
||||
if (dto.scope === AccessScope.Project) {
|
||||
return { scopeOrgId: dto.orgId, scopeProjectId: dto.projectId };
|
||||
}
|
||||
throw new InternalServerError({ message: "Invalid scope provided for the project factory" });
|
||||
};
|
||||
|
||||
const isCustomRole: TMembershipGroupScopeFactory["isCustomRole"] = (role) => isCustomProjectRole(role);
|
||||
|
||||
const onCreateMembershipGroupGuard: TMembershipGroupScopeFactory["onCreateMembershipGroupGuard"] = async (dto) => {
|
||||
const scope = getScopeField(dto.scopeData);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
projectId: scope.value,
|
||||
actorOrgId: dto.permission.orgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Create, ProjectPermissionSub.Groups);
|
||||
const orgMembership = await membershipGroupDAL.findOne({
|
||||
actorGroupId: dto.data.groupId,
|
||||
scopeOrgId: dto.permission.orgId,
|
||||
scope: AccessScope.Organization
|
||||
});
|
||||
if (!orgMembership)
|
||||
throw new BadRequestError({ message: `Group ${dto.data.groupId} is missing organization membership` });
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId);
|
||||
const permissionRoles = await permissionService.getProjectPermissionByRoles(
|
||||
dto.data.roles.map((el) => el.role),
|
||||
scope.value
|
||||
);
|
||||
for (const permissionRole of permissionRoles) {
|
||||
if (permissionRole?.role?.name !== ProjectMembershipRole.NoAccess) {
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups,
|
||||
permission,
|
||||
permissionRole.permission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create group project membership",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onUpdateMembershipGroupGuard: TMembershipGroupScopeFactory["onUpdateMembershipGroupGuard"] = async (dto) => {
|
||||
const scope = getScopeField(dto.scopeData);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
projectId: scope.value,
|
||||
actorOrgId: dto.permission.orgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Edit, ProjectPermissionSub.Groups);
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId);
|
||||
const permissionRoles = await permissionService.getProjectPermissionByRoles(
|
||||
dto.data.roles.map((el) => el.role),
|
||||
scope.value
|
||||
);
|
||||
for (const permissionRole of permissionRoles) {
|
||||
if (permissionRole?.role?.name !== ProjectMembershipRole.NoAccess) {
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups,
|
||||
permission,
|
||||
permissionRole.permission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create group project membership",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
ProjectPermissionGroupActions.GrantPrivileges,
|
||||
ProjectPermissionSub.Groups
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteMembershipGroupGuard: TMembershipGroupScopeFactory["onDeleteMembershipGroupGuard"] = async (dto) => {
|
||||
const scope = getScopeField(dto.scopeData);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
projectId: scope.value,
|
||||
actorOrgId: dto.permission.orgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Delete, ProjectPermissionSub.Groups);
|
||||
};
|
||||
|
||||
const onListMembershipGroupGuard: TMembershipGroupScopeFactory["onListMembershipGroupGuard"] = async (dto) => {
|
||||
const scope = getScopeField(dto.scopeData);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
projectId: scope.value,
|
||||
actorOrgId: dto.permission.orgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
|
||||
};
|
||||
|
||||
const onGetMembershipGroupByGroupIdGuard: TMembershipGroupScopeFactory["onGetMembershipGroupByGroupIdGuard"] = async (
|
||||
dto
|
||||
) => {
|
||||
const scope = getScopeField(dto.scopeData);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: dto.permission.type,
|
||||
actorId: dto.permission.id,
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actorAuthMethod: dto.permission.authMethod,
|
||||
projectId: scope.value,
|
||||
actorOrgId: dto.permission.orgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
|
||||
};
|
||||
|
||||
return {
|
||||
onCreateMembershipGroupGuard,
|
||||
onUpdateMembershipGroupGuard,
|
||||
onDeleteMembershipGroupGuard,
|
||||
onListMembershipGroupGuard,
|
||||
onGetMembershipGroupByGroupIdGuard,
|
||||
getScopeField,
|
||||
getScopeDatabaseFields,
|
||||
isCustomRole
|
||||
};
|
||||
};
|
||||
@@ -101,7 +101,7 @@ export const membershipIdentityServiceFactory = ({
|
||||
{
|
||||
scope: scopeData.scope,
|
||||
...scopeDatabaseFields,
|
||||
actorIdentityId: dto.permission.id
|
||||
actorIdentityId: dto.data.identityId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user