mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: completed ap, org roles and project roles and project membership
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { ApiDocsTags, IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
@@ -16,6 +15,7 @@ import {
|
||||
SanitizedIdentityPrivilegeSchema
|
||||
} from "@app/server/routes/sanitizedSchemas";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { AccessScope, MembershipActors, TemporaryPermissionMode } from "@app/db/schemas";
|
||||
|
||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -56,6 +56,10 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
if (!permissions && !privilegePermission) {
|
||||
throw new UnauthorizedError({ message: "Permission or privilegePermission must be provided" });
|
||||
}
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.body.projectSlug
|
||||
});
|
||||
|
||||
const permission = privilegePermission
|
||||
? privilegePermission.actions.map((action) => ({
|
||||
@@ -64,19 +68,35 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
conditions: privilegePermission.conditions
|
||||
}))
|
||||
: permissions!;
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
slug: req.body.slug ?? slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: false,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
data: {
|
||||
actorId: req.body.identityId,
|
||||
actorType: MembershipActors.Identity,
|
||||
...req.body,
|
||||
isTemporary: false,
|
||||
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.body.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -106,7 +126,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
||||
).optional(),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -138,19 +158,39 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
}))
|
||||
: permissions!;
|
||||
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
slug: req.body.slug ?? slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: true,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.body.projectSlug
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
data: {
|
||||
actorId: req.body.identityId,
|
||||
actorType: MembershipActors.Identity,
|
||||
...req.body,
|
||||
isTemporary: true,
|
||||
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.body.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -183,7 +223,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
).optional(),
|
||||
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -216,18 +256,43 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
conditions: privilegePermission.conditions
|
||||
}))
|
||||
: permissions!;
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
slug: req.body.privilegeSlug,
|
||||
identityId: req.body.identityId,
|
||||
projectSlug: req.body.projectSlug,
|
||||
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.body.projectSlug
|
||||
});
|
||||
|
||||
const { membershipId } = await server.services.convertor.identityIdToMembershipId(
|
||||
req.body.identityId,
|
||||
AccessScope.Project,
|
||||
projectId
|
||||
);
|
||||
|
||||
const { privilegeId } = await server.services.convertor.additionalPrivilegeNameToId(
|
||||
req.body.privilegeSlug,
|
||||
membershipId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.updateAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
actorId: req.body.identityId,
|
||||
actorType: MembershipActors.Identity,
|
||||
id: privilegeId
|
||||
},
|
||||
data: {
|
||||
...req.body,
|
||||
isTemporary: true,
|
||||
...updatedInfo,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
permissions: permission
|
||||
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
@@ -235,7 +300,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
: undefined
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.body.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -267,16 +341,45 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.deleteBySlug({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
slug: req.body.privilegeSlug,
|
||||
identityId: req.body.identityId,
|
||||
projectSlug: req.body.projectSlug
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.body.projectSlug
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
const { membershipId } = await server.services.convertor.identityIdToMembershipId(
|
||||
req.body.identityId,
|
||||
AccessScope.Project,
|
||||
projectId
|
||||
);
|
||||
|
||||
const { privilegeId } = await server.services.convertor.additionalPrivilegeNameToId(
|
||||
req.body.privilegeSlug,
|
||||
membershipId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.deleteAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
actorId: req.body.identityId,
|
||||
actorType: MembershipActors.Identity,
|
||||
id: privilegeId
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.body.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -310,15 +413,45 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.getPrivilegeDetailsBySlug({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
slug: req.params.privilegeSlug,
|
||||
...req.query
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.query.projectSlug
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
const { membershipId } = await server.services.convertor.identityIdToMembershipId(
|
||||
req.query.identityId,
|
||||
AccessScope.Project,
|
||||
projectId
|
||||
);
|
||||
|
||||
const { privilegeId } = await server.services.convertor.additionalPrivilegeNameToId(
|
||||
req.params.privilegeSlug,
|
||||
membershipId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeById({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
actorId: req.query.identityId,
|
||||
actorType: MembershipActors.Identity,
|
||||
id: privilegeId
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.query.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -349,15 +482,32 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privileges = await server.services.identityProjectAdditionalPrivilege.listIdentityProjectPrivileges({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
orgId: req.permission.orgId,
|
||||
slug: req.query.projectSlug
|
||||
});
|
||||
|
||||
const { additionalPrivileges: privileges } = await server.services.additionalPrivilege.listAdditionalPrivileges({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
actorId: req.query.identityId,
|
||||
actorType: MembershipActors.Identity
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
privileges
|
||||
privileges: privileges.map((privilege) => ({
|
||||
...privilege,
|
||||
identityId: req.query.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}))
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { z } from "zod";
|
||||
|
||||
import { AccessScope, MembershipActors, TemporaryPermissionMode } from "@app/db/schemas";
|
||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
@@ -34,7 +34,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
z.object({
|
||||
isTemporary: z.literal(true),
|
||||
temporaryMode: z
|
||||
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -55,17 +55,31 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.create({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectMembershipId: req.body.projectMembershipId,
|
||||
...req.body.type,
|
||||
slug: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||
permissions: req.body.permissions
|
||||
const { userId, membership } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.body.projectMembershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
data: {
|
||||
actorId: userId,
|
||||
actorType: MembershipActors.User,
|
||||
...req.body.type,
|
||||
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||
permissions: req.body.permissions
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: { ...privilege, userId, projectId: membership.scopeProjectId as string, slug: privilege.name }
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -91,7 +105,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
z.object({
|
||||
isTemporary: z.literal(true).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -113,21 +127,43 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.updateById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
...req.body.type,
|
||||
permissions: req.body.permissions
|
||||
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
req.body.permissions
|
||||
: undefined,
|
||||
privilegeId: req.params.privilegeId
|
||||
const { membership } = await server.services.convertor.additionalPrivilegeIdToMembershipId(
|
||||
req.params.privilegeId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.updateAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
data: {
|
||||
...req.body,
|
||||
...req.body.type,
|
||||
permissions: req.body.permissions
|
||||
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore-error this is valid ts
|
||||
req.body.permissions
|
||||
: undefined
|
||||
},
|
||||
selector: {
|
||||
id: req.params.privilegeId,
|
||||
actorId: membership.actorUserId as string,
|
||||
actorType: MembershipActors.User
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
userId: membership.actorUserId as string,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -149,14 +185,34 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.deleteById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
privilegeId: req.params.privilegeId
|
||||
const { membership } = await server.services.convertor.additionalPrivilegeIdToMembershipId(
|
||||
req.params.privilegeId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.deleteAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
id: req.params.privilegeId,
|
||||
actorId: membership.actorUserId as string,
|
||||
actorType: MembershipActors.User
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
userId: membership.actorUserId as string,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -178,14 +234,33 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privileges = await server.services.projectUserAdditionalPrivilege.listPrivileges({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectMembershipId: req.query.projectMembershipId
|
||||
const { userId, membership } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.query.projectMembershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivileges: privileges } = await server.services.additionalPrivilege.listAdditionalPrivileges({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
actorId: userId,
|
||||
actorType: MembershipActors.User
|
||||
}
|
||||
});
|
||||
return { privileges };
|
||||
|
||||
return {
|
||||
privileges: privileges.map((privilege) => ({
|
||||
...privilege,
|
||||
userId: membership.actorUserId as string,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}))
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,14 +282,34 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.projectUserAdditionalPrivilege.getPrivilegeDetailsById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
privilegeId: req.params.privilegeId
|
||||
const { membership } = await server.services.convertor.additionalPrivilegeIdToMembershipId(
|
||||
req.params.privilegeId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeById({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
id: req.params.privilegeId,
|
||||
actorId: membership.actorUserId as string,
|
||||
actorType: MembershipActors.User
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
userId: membership.actorUserId as string,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
||||
import { AccessScope, MembershipActors, TemporaryPermissionMode } from "@app/db/schemas";
|
||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { ApiDocsTags, IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
||||
@@ -43,7 +43,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
z.object({
|
||||
isTemporary: z.literal(true),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -64,18 +64,31 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.create({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
projectId: req.body.projectId,
|
||||
identityId: req.body.identityId,
|
||||
...req.body.type,
|
||||
slug: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||
permissions: req.body.permissions
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: req.body.projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
data: {
|
||||
actorId: req.body.identityId,
|
||||
actorType: MembershipActors.Identity,
|
||||
...req.body.type,
|
||||
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||
permissions: req.body.permissions
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.body.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId: req.body.projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -108,7 +121,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
z.object({
|
||||
isTemporary: z.literal(true).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.nativeEnum(TemporaryPermissionMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.string()
|
||||
@@ -129,19 +142,40 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.updateById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
id: req.params.id,
|
||||
const { membership } = await server.services.convertor.additionalPrivilegeIdToMembershipId(
|
||||
req.params.id,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.updateAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
id: req.params.id,
|
||||
actorId: membership.actorIdentityId as string,
|
||||
actorType: MembershipActors.Identity
|
||||
},
|
||||
data: {
|
||||
...req.body,
|
||||
...req.body.type,
|
||||
permissions: req.body.permissions || undefined
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: membership.actorIdentityId as string,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -171,14 +205,35 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.deleteById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.id
|
||||
const { membership } = await server.services.convertor.additionalPrivilegeIdToMembershipId(
|
||||
req.params.id,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.deleteAdditionalPrivilege({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
id: req.params.id,
|
||||
actorId: membership.actorIdentityId as string,
|
||||
actorType: MembershipActors.Identity
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: membership.actorIdentityId as string,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -208,14 +263,35 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.getPrivilegeDetailsById({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.id
|
||||
const { membership } = await server.services.convertor.additionalPrivilegeIdToMembershipId(
|
||||
req.params.id,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeById({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
id: req.params.id,
|
||||
actorId: membership.actorIdentityId as string,
|
||||
actorType: MembershipActors.Identity
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: membership.actorIdentityId as string,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId: membership.scopeProjectId as string,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -249,15 +325,36 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.getPrivilegeDetailsBySlug({
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actor: req.permission.type,
|
||||
actorOrgId: req.permission.orgId,
|
||||
slug: req.params.privilegeSlug,
|
||||
...req.query
|
||||
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||
slug: req.query.projectSlug,
|
||||
orgId: req.permission.orgId
|
||||
});
|
||||
return { privilege };
|
||||
|
||||
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeByName(
|
||||
{
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
name: req.params.privilegeSlug,
|
||||
actorId: req.query.identityId,
|
||||
actorType: MembershipActors.Identity
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
privilege: {
|
||||
...privilege,
|
||||
identityId: req.query.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId,
|
||||
slug: privilege.name
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -288,15 +385,27 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const privileges = await server.services.identityProjectAdditionalPrivilegeV2.listIdentityProjectPrivileges({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query
|
||||
const { additionalPrivileges: privileges } = await server.services.additionalPrivilege.listAdditionalPrivileges({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
projectId: req.query.projectId,
|
||||
orgId: req.permission.orgId
|
||||
},
|
||||
selector: {
|
||||
actorId: req.query.identityId,
|
||||
actorType: MembershipActors.Identity
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
privileges
|
||||
privileges: privileges.map((privilege) => ({
|
||||
...privilege,
|
||||
identityId: req.query.identityId,
|
||||
projectMembershipId: privilege.membershipId,
|
||||
projectId: req.query.projectId,
|
||||
slug: privilege.name
|
||||
}))
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ProjectMembershipRole,
|
||||
ProjectMembershipsSchema,
|
||||
ProjectUserMembershipRolesSchema,
|
||||
TemporaryPermissionMode,
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
@@ -14,7 +15,6 @@ import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||
|
||||
export const registerProjectMembershipRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -134,18 +134,34 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.projectMembership.getProjectMembershipById({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
id: req.params.membershipId
|
||||
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.params.membershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
const membership = await server.services.membershipUser.getMembershipByUserId({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
},
|
||||
selector: {
|
||||
userId
|
||||
}
|
||||
});
|
||||
return { membership };
|
||||
|
||||
return {
|
||||
membership: {
|
||||
...membership,
|
||||
userId,
|
||||
projectId: req.params.projectId
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(simp): look into multi deletion and this as well
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:projectId/memberships/details",
|
||||
@@ -317,7 +333,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
z.object({
|
||||
role: z.string(),
|
||||
isTemporary: z.literal(true),
|
||||
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
|
||||
temporaryMode: z.nativeEnum(TemporaryPermissionMode),
|
||||
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||
temporaryAccessStartTime: z.string().datetime()
|
||||
})
|
||||
@@ -335,17 +351,28 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const roles = await server.services.projectMembership.updateProjectMembership({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
membershipId: req.params.membershipId,
|
||||
roles: req.body.roles
|
||||
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.params.membershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { membership } = await server.services.membershipUser.updateMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
},
|
||||
selector: {
|
||||
userId
|
||||
},
|
||||
data: {
|
||||
roles: req.body.roles
|
||||
}
|
||||
});
|
||||
|
||||
return { roles };
|
||||
return { roles: membership.roles.map((el) => ({ ...el, projectMembershipId: req.params.membershipId })) };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -443,13 +470,22 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.projectMembership.deleteProjectMembership({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
membershipId: req.params.membershipId
|
||||
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||
req.params.membershipId,
|
||||
AccessScope.Project,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
const { membership } = await server.services.membershipUser.deleteMembership({
|
||||
permission: req.permission,
|
||||
scopeData: {
|
||||
scope: AccessScope.Project,
|
||||
orgId: req.permission.orgId,
|
||||
projectId: req.params.projectId
|
||||
},
|
||||
selector: {
|
||||
userId
|
||||
}
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -458,12 +494,19 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
event: {
|
||||
type: EventType.REMOVE_PROJECT_MEMBER,
|
||||
metadata: {
|
||||
userId: membership.userId,
|
||||
userId: membership.actorUserId as string,
|
||||
email: ""
|
||||
}
|
||||
}
|
||||
});
|
||||
return { membership };
|
||||
|
||||
return {
|
||||
membership: {
|
||||
...membership,
|
||||
userId,
|
||||
projectId: req.params.projectId
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
export type TAdditionalPrivilegeDALFactory = ReturnType<typeof additionalPrivilegeDALFactory>;
|
||||
|
||||
export const additionalPrivilegeDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.AdditionalPrivilege);
|
||||
return orm;
|
||||
|
||||
const getMembershipById = async (privilegeId: string) => {
|
||||
const doc = await db
|
||||
.replicaNode()(TableName.AdditionalPrivilege)
|
||||
.join(TableName.Membership, `${TableName.AdditionalPrivilege}.membershipId`, `${TableName.Membership}.id`)
|
||||
.select(selectAllTableCols(TableName.Membership))
|
||||
.where(`${TableName.AdditionalPrivilege}.id`, privilegeId)
|
||||
.first();
|
||||
return doc;
|
||||
};
|
||||
|
||||
return { ...orm, getMembershipById };
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
TCreateAdditionalPrivilegesDTO,
|
||||
TDeleteAdditionalPrivilegesDTO,
|
||||
TGetAdditionalPrivilegesByIdDTO,
|
||||
TGetAdditionalPrivilegesByNameDTO,
|
||||
TListAdditionalPrivilegesDTO,
|
||||
TUpdateAdditionalPrivilegesDTO
|
||||
} from "./additional-privilege-types";
|
||||
@@ -71,7 +72,9 @@ export const additionalPrivilegeServiceFactory = ({
|
||||
permissions: JSON.stringify(packRules(data.permissions as RawRule[]))
|
||||
});
|
||||
|
||||
return { additionalPrivilege: { ...additionalPrivilege, permissions: data.permissions } };
|
||||
return {
|
||||
additionalPrivilege: { ...additionalPrivilege, permissions: unpackPermissions(additionalPrivilege.permissions) }
|
||||
};
|
||||
}
|
||||
|
||||
if (!data.temporaryAccessStartTime || !data.temporaryRange) {
|
||||
@@ -92,7 +95,9 @@ export const additionalPrivilegeServiceFactory = ({
|
||||
temporaryRange: data.temporaryRange
|
||||
});
|
||||
|
||||
return { additionalPrivilege: { ...additionalPrivilege, permissions: data.permissions } };
|
||||
return {
|
||||
additionalPrivilege: { ...additionalPrivilege, permissions: unpackPermissions(additionalPrivilege.permissions) }
|
||||
};
|
||||
};
|
||||
|
||||
const updateAdditionalPrivilege = async (dto: TUpdateAdditionalPrivilegesDTO) => {
|
||||
@@ -169,7 +174,7 @@ export const additionalPrivilegeServiceFactory = ({
|
||||
const getAdditionalPrivilegeById = async (dto: TGetAdditionalPrivilegesByIdDTO) => {
|
||||
const { scopeData, selector } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
const { membershipId } = await factory.onListAdditionalPrivilegesGuard(dto);
|
||||
const { membershipId } = await factory.onGetAdditionalPrivilegesByIdGuard(dto);
|
||||
const additionalPrivilege = await additionalPrivilegeDAL.findOne({
|
||||
id: selector.id,
|
||||
membershipId
|
||||
@@ -182,6 +187,22 @@ export const additionalPrivilegeServiceFactory = ({
|
||||
};
|
||||
};
|
||||
|
||||
const getAdditionalPrivilegeByName = async (dto: TGetAdditionalPrivilegesByNameDTO) => {
|
||||
const { scopeData, selector } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
const { membershipId } = await factory.onGetAdditionalPrivilegesByIdGuard(dto);
|
||||
const additionalPrivilege = await additionalPrivilegeDAL.findOne({
|
||||
name: selector.name,
|
||||
membershipId
|
||||
});
|
||||
if (!additionalPrivilege)
|
||||
throw new NotFoundError({ message: `Additional privilege with name ${selector.name} doesn't exist` });
|
||||
|
||||
return {
|
||||
additionalPrivilege: { ...additionalPrivilege, permissions: unpackPermissions(additionalPrivilege.permissions) }
|
||||
};
|
||||
};
|
||||
|
||||
const listAdditionalPrivileges = async (dto: TListAdditionalPrivilegesDTO) => {
|
||||
const { scopeData } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
@@ -203,6 +224,7 @@ export const additionalPrivilegeServiceFactory = ({
|
||||
updateAdditionalPrivilege,
|
||||
deleteAdditionalPrivilege,
|
||||
getAdditionalPrivilegeById,
|
||||
getAdditionalPrivilegeByName,
|
||||
listAdditionalPrivileges
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6,7 +6,9 @@ export interface TAdditionalPrivilegesScopeFactory {
|
||||
onUpdateAdditionalPrivilegesGuard: (arg: TUpdateAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
|
||||
onDeleteAdditionalPrivilegesGuard: (arg: TDeleteAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
|
||||
onListAdditionalPrivilegesGuard: (arg: TListAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
|
||||
onGetAdditionalPrivilegesByIdGuard: (arg: TGetAdditionalPrivilegesByIdDTO) => Promise<{ membershipId: string }>;
|
||||
onGetAdditionalPrivilegesByIdGuard: (
|
||||
arg: TGetAdditionalPrivilegesByIdDTO | TGetAdditionalPrivilegesByNameDTO
|
||||
) => Promise<{ membershipId: string }>;
|
||||
getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string };
|
||||
}
|
||||
|
||||
@@ -71,3 +73,13 @@ export type TGetAdditionalPrivilegesByIdDTO = {
|
||||
actorType: MembershipActors;
|
||||
};
|
||||
};
|
||||
|
||||
export type TGetAdditionalPrivilegesByNameDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
name: string;
|
||||
actorId: string;
|
||||
actorType: MembershipActors;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,88 +1,118 @@
|
||||
import { AccessScope } from "@app/db/schemas";
|
||||
import { NotFoundError } from "@app/lib/errors";
|
||||
|
||||
import { TMembershipDALFactory } from "../membership/membership-dal";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { AccessScope, AccessScopeData } from "@app/db/schemas";
|
||||
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
||||
|
||||
type TConvertorServiceFactoryDep = {
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne">;
|
||||
membershipDAL: Pick<TMembershipDALFactory, "findOne">;
|
||||
additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "getMembershipById" | "findOne">;
|
||||
};
|
||||
|
||||
export type TConvertorServiceFactory = ReturnType<typeof convertorServiceFactory>;
|
||||
|
||||
export const convertorServiceFactory = ({ projectDAL, membershipDAL }: TConvertorServiceFactoryDep) => {
|
||||
const $getScopeDatabaseFields = (scopeData: AccessScopeData) => {
|
||||
if (scopeData.scope === AccessScope.Organization) {
|
||||
return { scopeOrgId: scopeData.orgId };
|
||||
}
|
||||
|
||||
if (scopeData.scope === AccessScope.Namespace) {
|
||||
return { scopeNamespaceId: scopeData.namespaceId, scopeOrgId: scopeData.orgId };
|
||||
}
|
||||
|
||||
return { scopeProjectId: scopeData.projectId, scopeOrgId: scopeData.orgId };
|
||||
};
|
||||
|
||||
export const convertorServiceFactory = ({
|
||||
projectDAL,
|
||||
membershipDAL,
|
||||
additionalPrivilegeDAL
|
||||
}: TConvertorServiceFactoryDep) => {
|
||||
const projectSlugToId = async (dto: { slug: string; orgId: string }) => {
|
||||
const project = await projectDAL.findOne({
|
||||
orgId: dto.orgId,
|
||||
slug: dto.slug
|
||||
});
|
||||
if (!project) throw new NotFoundError({ message: `Project with slug ${slug} not found` });
|
||||
if (!project) throw new NotFoundError({ message: `Project with slug ${dto.slug} not found` });
|
||||
return project;
|
||||
};
|
||||
|
||||
const userMembershipIdToUserId = async (scopeData: AccessScopeData, membershipId: string) => {
|
||||
const dbFields = $getScopeDatabaseFields(scopeData);
|
||||
|
||||
const userMembershipIdToUserId = async (membershipId: string, scope: AccessScope, orgId: string) => {
|
||||
const membership = await membershipDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
scope,
|
||||
id: membershipId,
|
||||
...dbFields,
|
||||
scopeOrgId: scopeData.orgId
|
||||
scopeOrgId: orgId
|
||||
});
|
||||
if (!membership || !membership.actorUserId) {
|
||||
throw new NotFoundError({ message: `Membership with id ${membershipId} not found` });
|
||||
}
|
||||
return membership.actorUserId;
|
||||
return { userId: membership.actorUserId, membership };
|
||||
};
|
||||
|
||||
const groupMembershipIdToGroupId = async (scopeData: AccessScopeData, membershipId: string) => {
|
||||
const dbFields = $getScopeDatabaseFields(scopeData);
|
||||
|
||||
const groupMembershipIdToGroupId = async (membershipId: string, scope: AccessScope, orgId: string) => {
|
||||
const membership = await membershipDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
scope,
|
||||
id: membershipId,
|
||||
...dbFields,
|
||||
scopeOrgId: scopeData.orgId
|
||||
scopeOrgId: orgId
|
||||
});
|
||||
if (!membership || !membership.actorGroupId) {
|
||||
throw new NotFoundError({ message: `Membership with id ${membershipId} not found` });
|
||||
}
|
||||
|
||||
return membership.actorGroupId;
|
||||
return { groupId: membership.actorGroupId, membership };
|
||||
};
|
||||
|
||||
const identityMembershipIdToGroupId = async (scopeData: AccessScopeData, membershipId: string) => {
|
||||
const dbFields = $getScopeDatabaseFields(scopeData);
|
||||
|
||||
const identityMembershipIdToIdentityId = async (membershipId: string, scope: AccessScope, orgId: string) => {
|
||||
const membership = await membershipDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
scope,
|
||||
id: membershipId,
|
||||
...dbFields,
|
||||
scopeOrgId: scopeData.orgId
|
||||
scopeOrgId: orgId
|
||||
});
|
||||
if (!membership || !membership.actorIdentityId) {
|
||||
throw new NotFoundError({ message: `Membership with id ${membershipId} not found` });
|
||||
}
|
||||
|
||||
return membership.actorIdentityId;
|
||||
return { identityId: membership.actorIdentityId, membership };
|
||||
};
|
||||
|
||||
const identityIdToMembershipId = async (identityId: string, scope: AccessScope, scopeId: string) => {
|
||||
let fieldName = "scopeOrgId";
|
||||
if (scope === AccessScope.Project) {
|
||||
fieldName = "scopeProjectId";
|
||||
} else if (scope === AccessScope.Namespace) {
|
||||
fieldName = "scopeNamespaceId";
|
||||
}
|
||||
|
||||
const membership = await membershipDAL.findOne({
|
||||
scope,
|
||||
actorIdentityId: identityId,
|
||||
[fieldName]: scopeId
|
||||
});
|
||||
|
||||
if (!membership) {
|
||||
throw new NotFoundError({ message: `Identity with id ${identityId} not found` });
|
||||
}
|
||||
|
||||
return { membershipId: membership.id, membership };
|
||||
};
|
||||
|
||||
const additionalPrivilegeIdToMembershipId = async (privilegeId: string, scope: AccessScope, orgId: string) => {
|
||||
const membership = await additionalPrivilegeDAL.getMembershipById(privilegeId);
|
||||
if (!membership || membership.scope !== scope || membership.scopeOrgId !== orgId) {
|
||||
throw new NotFoundError({ message: `Privilege with id ${privilegeId} not found` });
|
||||
}
|
||||
|
||||
return { membership, membershipId: membership.id };
|
||||
};
|
||||
const additionalPrivilegeNameToId = async (privilegeName: string, membershipId: string) => {
|
||||
const privilege = await additionalPrivilegeDAL.findOne({
|
||||
name: privilegeName,
|
||||
membershipId
|
||||
});
|
||||
if (!privilege) {
|
||||
throw new NotFoundError({ message: `Privilege with slug ${privilegeName} not found` });
|
||||
}
|
||||
|
||||
return { privilegeId: privilege.id, privilege };
|
||||
};
|
||||
|
||||
return {
|
||||
projectSlugToId,
|
||||
userMembershipIdToUserId,
|
||||
groupMembershipIdToGroupId,
|
||||
identityMembershipIdToGroupId
|
||||
identityMembershipIdToIdentityId,
|
||||
additionalPrivilegeIdToMembershipId,
|
||||
additionalPrivilegeNameToId,
|
||||
identityIdToMembershipId
|
||||
};
|
||||
};
|
||||
|
||||
@@ -274,7 +274,7 @@ export const membershipUserServiceFactory = ({
|
||||
const existingMembership = await membershipUserDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
...scopeDatabaseFields,
|
||||
actorUserId: dto.selector.actorId
|
||||
actorUserId: dto.selector.userId
|
||||
});
|
||||
if (!existingMembership)
|
||||
throw new BadRequestError({
|
||||
@@ -338,31 +338,31 @@ export const membershipUserServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return doc;
|
||||
const insertedRoles = await membershipRoleDAL.insertMany(roleDocs, tx);
|
||||
return { ...doc, roles: insertedRoles };
|
||||
});
|
||||
|
||||
return { memberships: membershipDoc };
|
||||
return { membership: membershipDoc };
|
||||
};
|
||||
|
||||
const deleteMembership = async (dto: TDeleteMembershipUserDTO) => {
|
||||
const { scopeData } = dto;
|
||||
const factory = scopeFactory[scopeData.scope];
|
||||
|
||||
const { actorIdOfDeletor } = await factory.onDeleteMembershipUserGuard(dto);
|
||||
await factory.onDeleteMembershipUserGuard(dto);
|
||||
|
||||
const scopeDatabaseFields = factory.getScopeDatabaseFields(dto.scopeData);
|
||||
const existingMembership = await membershipUserDAL.findOne({
|
||||
scope: scopeData.scope,
|
||||
...scopeDatabaseFields,
|
||||
actorUserId: dto.selector.actorId
|
||||
actorUserId: dto.selector.userId
|
||||
});
|
||||
if (!existingMembership)
|
||||
throw new BadRequestError({
|
||||
message: "User doesn't have membership"
|
||||
});
|
||||
|
||||
if (existingMembership.actorUserId === actorIdOfDeletor)
|
||||
if (existingMembership.actorUserId === dto.permission.id)
|
||||
throw new BadRequestError({
|
||||
message: "You can delete you own membership"
|
||||
});
|
||||
@@ -403,7 +403,7 @@ export const membershipUserServiceFactory = ({
|
||||
await factory.onGetMembershipUserByUserIdGuard(dto);
|
||||
const membership = await membershipUserDAL.getUserById({
|
||||
scopeData,
|
||||
userId: selector.actorId
|
||||
userId: selector.userId
|
||||
});
|
||||
if (!membership) throw new NotFoundError({ message: `User membership not found` });
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface TMembershipUserScopeFactory {
|
||||
onCreateMembershipComplete: (arg: { id: string; email: string }[]) => Promise<void>;
|
||||
|
||||
onUpdateMembershipUserGuard: (arg: TUpdateMembershipUserDTO) => Promise<void>;
|
||||
onDeleteMembershipUserGuard: (arg: TDeleteMembershipUserDTO) => Promise<{ actorIdOfDeletor: string }>;
|
||||
onDeleteMembershipUserGuard: (arg: TDeleteMembershipUserDTO) => Promise<void>;
|
||||
onListMembershipUserGuard: (arg: TListMembershipUserDTO) => Promise<void>;
|
||||
onGetMembershipUserByUserIdGuard: (arg: TGetMembershipUserByUserIdDTO) => Promise<void>;
|
||||
getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string };
|
||||
@@ -37,7 +37,7 @@ export type TUpdateMembershipUserDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
actorId: string;
|
||||
userId: string;
|
||||
};
|
||||
data: {
|
||||
isActive?: boolean;
|
||||
@@ -67,7 +67,7 @@ export type TDeleteMembershipUserDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
actorId: string;
|
||||
userId: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -75,6 +75,6 @@ export type TGetMembershipUserByUserIdDTO = {
|
||||
permission: OrgServiceActor;
|
||||
scopeData: AccessScopeData;
|
||||
selector: {
|
||||
actorId: string;
|
||||
userId: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -63,4 +63,4 @@ export const newNamespaceMembershipUserFactory = (
|
||||
getScopeDatabaseFields,
|
||||
isCustomRole
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -67,7 +67,6 @@ export const newOrgMembershipUserFactory = ({
|
||||
dto.permission.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member);
|
||||
return { actorIdOfDeletor: dto.permission.id };
|
||||
};
|
||||
|
||||
const onListMembershipUserGuard: TMembershipUserScopeFactory["onListMembershipUserGuard"] = async (dto) => {
|
||||
|
||||
@@ -134,7 +134,6 @@ export const newProjectMembershipUserFactory = ({
|
||||
actorOrgId: dto.permission.orgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Delete, ProjectPermissionSub.Member);
|
||||
return { actorIdOfDeletor: dto.permission.id };
|
||||
};
|
||||
|
||||
const onListMembershipUserGuard: TMembershipUserScopeFactory["onListMembershipUserGuard"] = async (dto) => {
|
||||
|
||||
Reference in New Issue
Block a user