feat: completed all role routers

This commit is contained in:
=
2025-10-09 23:58:10 +05:30
parent 81b7ea1a5a
commit e232282bea
21 changed files with 581 additions and 347 deletions
+13
View File
@@ -53,6 +53,7 @@ import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-servi
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service"; import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types"; import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types";
import { TAuthMode } from "@app/server/plugins/auth/inject-identity"; import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service"; import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service"; import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service"; import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
@@ -88,6 +89,9 @@ import { TIdentityTokenAuthServiceFactory } from "@app/services/identity-token-a
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service"; import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service"; import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service"; import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
import { TMembershipGroupServiceFactory } from "@app/services/membership-group/membership-group-service";
import { TMembershipIdentityServiceFactory } from "@app/services/membership-identity/membership-identity-service";
import { TMembershipUserServiceFactory } from "@app/services/membership-user/membership-user-service";
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service"; import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
import { TNotificationServiceFactory } from "@app/services/notification/notification-service"; import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
import { TOfflineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service"; import { TOfflineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
@@ -106,6 +110,7 @@ import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key
import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service"; import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service";
import { TReminderServiceFactory } from "@app/services/reminder/reminder-types"; import { TReminderServiceFactory } from "@app/services/reminder/reminder-types";
import { TRoleServiceFactory } from "@app/services/role/role-service";
import { TSecretServiceFactory } from "@app/services/secret/secret-service"; import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service"; import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service"; import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
@@ -125,6 +130,7 @@ import { TUserServiceFactory } from "@app/services/user/user-service";
import { TUserEngagementServiceFactory } from "@app/services/user-engagement/user-engagement-service"; import { TUserEngagementServiceFactory } from "@app/services/user-engagement/user-engagement-service";
import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service"; import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service";
import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integration/workflow-integration-service"; import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integration/workflow-integration-service";
import { TConvertorServiceFactory } from "@app/services/convertor/convertor-service";
declare module "@fastify/request-context" { declare module "@fastify/request-context" {
interface RequestContextData { interface RequestContextData {
@@ -324,6 +330,13 @@ declare module "fastify" {
pamAccount: TPamAccountServiceFactory; pamAccount: TPamAccountServiceFactory;
pamSession: TPamSessionServiceFactory; pamSession: TPamSessionServiceFactory;
upgradePath: TUpgradePathService; upgradePath: TUpgradePathService;
membershipUser: TMembershipUserServiceFactory;
membershipIdentity: TMembershipIdentityServiceFactory;
membershipGroup: TMembershipGroupServiceFactory;
role: TRoleServiceFactory;
additionalPrivilege: TAdditionalPrivilegeServiceFactory;
convertor: TConvertorServiceFactory;
}; };
// this is exclusive use for middlewares in which we need to inject data // this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer // everywhere else access using service layer
@@ -1,7 +1,7 @@
import { packRules } from "@casl/ability/extra"; import { packRules } from "@casl/ability/extra";
import { z } from "zod"; import { z } from "zod";
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas"; import { AccessScope, ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { import {
backfillPermissionV1SchemaToV2Schema, backfillPermissionV1SchemaToV2Schema,
@@ -13,7 +13,6 @@ import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas"; import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => { export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -55,14 +54,16 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true)) packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true))
); );
const role = await server.services.projectRole.createRole({ const { id: projectId } = await server.services.convertor.projectSlugToId({
actorAuthMethod: req.permission.authMethod, slug: req.params.projectSlug,
actorId: req.permission.id, orgId: req.permission.orgId
actorOrgId: req.permission.orgId, });
actor: req.permission.type, const role = await server.services.role.createRole({
filter: { permission: req.permission,
type: ProjectRoleServiceIdentifierType.SLUG, scopeData: {
projectSlug: req.params.projectSlug scope: AccessScope.Project,
orgId: req.permission.orgId,
projectId
}, },
data: { data: {
...req.body, ...req.body,
@@ -73,7 +74,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId,
event: { event: {
type: EventType.CREATE_PROJECT_ROLE, type: EventType.CREATE_PROJECT_ROLE,
metadata: { metadata: {
@@ -86,7 +87,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -131,12 +132,21 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true))) ? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true)))
: undefined; : undefined;
const role = await server.services.projectRole.updateRole({ const { id: projectId } = await server.services.convertor.projectSlugToId({
actorAuthMethod: req.permission.authMethod, slug: req.params.projectSlug,
actorId: req.permission.id, orgId: req.permission.orgId
actorOrgId: req.permission.orgId, });
actor: req.permission.type,
roleId: req.params.roleId, const role = await server.services.role.updateRole({
permission: req.permission,
scopeData: {
scope: AccessScope.Project,
orgId: req.permission.orgId,
projectId
},
selector: {
id: req.params.roleId
},
data: { data: {
...req.body, ...req.body,
permissions: stringifiedPermissions permissions: stringifiedPermissions
@@ -146,7 +156,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.UPDATE_PROJECT_ROLE, type: EventType.UPDATE_PROJECT_ROLE,
metadata: { metadata: {
@@ -159,7 +169,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -188,18 +198,27 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.projectRole.deleteRole({ const { id: projectId } = await server.services.convertor.projectSlugToId({
actorAuthMethod: req.permission.authMethod, slug: req.params.projectSlug,
actorId: req.permission.id, orgId: req.permission.orgId
actorOrgId: req.permission.orgId, });
actor: req.permission.type,
roleId: req.params.roleId const role = await server.services.role.deleteRole({
permission: req.permission,
scopeData: {
scope: AccessScope.Project,
orgId: req.permission.orgId,
projectId
},
selector: {
id: req.params.roleId
}
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.DELETE_PROJECT_ROLE, type: EventType.DELETE_PROJECT_ROLE,
metadata: { metadata: {
@@ -210,7 +229,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -238,17 +257,21 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const roles = await server.services.projectRole.listRoles({ const { id: projectId } = await server.services.convertor.projectSlugToId({
actorAuthMethod: req.permission.authMethod, slug: req.params.projectSlug,
actorId: req.permission.id, orgId: req.permission.orgId
actorOrgId: req.permission.orgId,
actor: req.permission.type,
filter: {
type: ProjectRoleServiceIdentifierType.SLUG,
projectSlug: req.params.projectSlug
}
}); });
return { roles };
const { roles } = await server.services.role.listRoles({
permission: req.permission,
scopeData: {
scope: AccessScope.Project,
orgId: req.permission.orgId,
projectId
},
data: {}
});
return { roles: roles.map((el) => ({ ...el, projectId: el.projectId as string })) };
} }
}); });
@@ -265,78 +288,30 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
}), }),
response: { response: {
200: z.object({ 200: z.object({
role: SanitizedRoleSchemaV1.omit({ version: true }) role: SanitizedRoleSchemaV1
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.projectRole.getRoleBySlug({ const { id: projectId } = await server.services.convertor.projectSlugToId({
actorAuthMethod: req.permission.authMethod, slug: req.params.projectSlug,
actorId: req.permission.id, orgId: req.permission.orgId
actorOrgId: req.permission.orgId,
actor: req.permission.type,
filter: {
type: ProjectRoleServiceIdentifierType.SLUG,
projectSlug: req.params.projectSlug
},
roleSlug: req.params.slug
}); });
return { role }; const role = await server.services.role.getRoleBySlug({
} permission: req.permission,
}); scopeData: {
scope: AccessScope.Project,
server.route({ orgId: req.permission.orgId,
method: "GET", projectId
url: "/:projectId/permissions", },
config: { selector: {
rateLimit: readLimit slug: req.params.slug
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
response: {
200: z.object({
data: z.object({
membership: z.object({
id: z.string(),
roles: z
.object({
role: z.string()
})
.array()
}),
assumedPrivilegeDetails: z
.object({
actorId: z.string(),
actorType: z.string(),
actorName: z.string(),
actorEmail: z.string().optional()
})
.optional(),
permissions: z.any().array()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { permissions, membership, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission(
req.permission.id,
req.params.projectId,
req.permission.authMethod,
req.permission.orgId
);
return {
data: {
permissions,
membership,
assumedPrivilegeDetails
} }
}; });
return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
}; };
+75 -51
View File
@@ -1,7 +1,9 @@
import { packRules } from "@casl/ability/extra";
import { z } from "zod"; import { z } from "zod";
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas"; import { AccessScope, OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { OrgPermissionSchema } from "@app/ee/services/permission/org-permission";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas"; import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -25,8 +27,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
), ),
name: z.string().trim(), name: z.string().trim(),
description: z.string().trim().nullish(), description: z.string().trim().nullish(),
// TODO(scott): once UI refactored permissions: OrgPermissionSchema.array() permissions: OrgPermissionSchema.array()
permissions: z.any().array()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -36,13 +37,18 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.orgRole.createRole( const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
req.permission.id, const role = await server.services.role.createRole({
req.params.organizationId, permission: req.permission,
req.body, scopeData: {
req.permission.authMethod, scope: AccessScope.Organization,
req.permission.orgId orgId: req.params.organizationId
); },
data: {
...req.body,
permissions: stringifiedPermissions
}
});
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
@@ -59,7 +65,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
} }
}); });
return { role }; return { role: { ...role, orgId: role.orgId as string } };
} }
}); });
@@ -82,14 +88,17 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.orgRole.getRole( const role = await server.services.role.getRoleById({
req.permission.id, permission: req.permission,
req.params.organizationId, scopeData: {
req.params.roleId, scope: AccessScope.Organization,
req.permission.authMethod, orgId: req.params.organizationId
req.permission.orgId },
); selector: {
return { role }; id: req.params.roleId
}
});
return { role: { ...role, orgId: role.orgId as string } };
} }
}); });
@@ -114,8 +123,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
.optional(), .optional(),
name: z.string().trim().optional(), name: z.string().trim().optional(),
description: z.string().trim().nullish(), description: z.string().trim().nullish(),
// TODO(scott): once UI refactored permissions: OrgPermissionSchema.array().optional() permissions: OrgPermissionSchema.array().optional()
permissions: z.any().array().optional()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -125,14 +133,21 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.orgRole.updateRole( const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
req.permission.id, const role = await server.services.role.updateRole({
req.params.organizationId, permission: req.permission,
req.params.roleId, scopeData: {
req.body, scope: AccessScope.Organization,
req.permission.authMethod, orgId: req.params.organizationId
req.permission.orgId },
); selector: {
id: req.params.roleId
},
data: {
...req.body,
permissions: stringifiedPermissions
}
});
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
@@ -149,7 +164,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
} }
}); });
return { role }; return { role: { ...role, orgId: role.orgId as string } };
} }
}); });
@@ -172,13 +187,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.orgRole.deleteRole( const role = await server.services.role.deleteRole({
req.permission.id, permission: req.permission,
req.params.organizationId, scopeData: {
req.params.roleId, scope: AccessScope.Organization,
req.permission.authMethod, orgId: req.params.organizationId
req.permission.orgId },
); selector: {
id: req.params.roleId
}
});
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
@@ -189,10 +207,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
} }
}); });
return { role }; return { role: { ...role, orgId: role.orgId as string } };
} }
}); });
// TODO(simp): switch to top level roles
server.route({ server.route({
method: "GET", method: "GET",
url: "/:organizationId/roles", url: "/:organizationId/roles",
@@ -206,25 +225,30 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
response: { response: {
200: z.object({ 200: z.object({
data: z.object({ data: z.object({
roles: OrgRolesSchema.omit({ permissions: true }) roles: OrgRolesSchema.omit({ permissions: true }).array()
.merge(z.object({ permissions: z.unknown() }))
.array()
}) })
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const roles = await server.services.orgRole.listRoles( const { roles } = await server.services.role.listRoles({
req.permission.id, permission: req.permission,
req.params.organizationId, scopeData: {
req.permission.authMethod, scope: AccessScope.Organization,
req.permission.orgId orgId: req.permission.orgId
); },
return { data: { roles } }; data: {}
});
return {
data: {
roles: roles.map((el) => ({ ...el, orgId: el.orgId as string }))
}
};
} }
}); });
// TODO(simp): get this fixed
server.route({ server.route({
method: "GET", method: "GET",
url: "/:organizationId/permissions", url: "/:organizationId/permissions",
@@ -237,20 +261,20 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}), }),
response: { response: {
200: z.object({ 200: z.object({
membership: OrgMembershipsSchema, memberships: OrgMembershipsSchema.array(),
permissions: z.any().array() permissions: z.any().array()
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const { permissions, membership } = await server.services.orgRole.getUserPermission( const { permissions, memberships } = await server.services.orgRole.getUserPermission(
req.permission.id, req.permission.id,
req.params.organizationId, req.params.organizationId,
req.permission.authMethod, req.permission.authMethod,
req.permission.orgId req.permission.orgId
); );
return { permissions, membership }; return { permissions, memberships };
} }
}); });
}; };
+63 -56
View File
@@ -1,7 +1,7 @@
import { packRules } from "@casl/ability/extra"; import { packRules } from "@casl/ability/extra";
import { z } from "zod"; import { z } from "zod";
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas"; import { ProjectMembershipRole, ProjectRolesSchema, AccessScope } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns"; import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
@@ -11,7 +11,6 @@ import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas"; import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -55,13 +54,11 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
handler: async (req) => { handler: async (req) => {
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions)); const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
const role = await server.services.projectRole.createRole({ const role = await server.services.role.createRole({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId projectId: req.params.projectId
}, },
data: { data: {
@@ -73,7 +70,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.CREATE_PROJECT_ROLE, type: EventType.CREATE_PROJECT_ROLE,
metadata: { metadata: {
@@ -86,7 +83,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -133,12 +130,16 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined; const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
const role = await server.services.projectRole.updateRole({ const role = await server.services.role.updateRole({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
roleId: req.params.roleId, projectId: req.params.projectId
},
selector: {
id: req.params.roleId
},
data: { data: {
...req.body, ...req.body,
permissions: stringifiedPermissions permissions: stringifiedPermissions
@@ -148,7 +149,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.UPDATE_PROJECT_ROLE, type: EventType.UPDATE_PROJECT_ROLE,
metadata: { metadata: {
@@ -161,7 +162,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -192,18 +193,22 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.projectRole.deleteRole({ const role = await server.services.role.deleteRole({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
roleId: req.params.roleId projectId: req.params.projectId
},
selector: {
id: req.params.roleId
}
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.DELETE_PROJECT_ROLE, type: EventType.DELETE_PROJECT_ROLE,
metadata: { metadata: {
@@ -214,7 +219,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -244,17 +249,16 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const roles = await server.services.projectRole.listRoles({ const { roles } = await server.services.role.listRoles({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId projectId: req.params.projectId
} },
data: {}
}); });
return { roles }; return { roles: roles.map((el) => ({ ...el, projectId: el.projectId as string })) };
} }
}); });
@@ -273,24 +277,25 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}), }),
response: { response: {
200: z.object({ 200: z.object({
role: SanitizedRoleSchema.omit({ version: true }) role: SanitizedRoleSchema
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.projectRole.getRoleBySlug({ const role = await server.services.role.getRoleBySlug({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId projectId: req.params.projectId
}, },
roleSlug: req.params.roleSlug selector: {
slug: req.params.roleSlug
}
}); });
return { role };
return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -307,14 +312,16 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
response: { response: {
200: z.object({ 200: z.object({
data: z.object({ data: z.object({
membership: z.object({ memberships: z
id: z.string(), .object({
roles: z id: z.string(),
.object({ roles: z
role: z.string() .object({
}) role: z.string()
.array() })
}), .array()
})
.array(),
assumedPrivilegeDetails: z assumedPrivilegeDetails: z
.object({ .object({
actorId: z.string(), actorId: z.string(),
@@ -330,7 +337,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const { permissions, membership, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission( const { permissions, memberships, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission(
req.permission.id, req.permission.id,
req.params.projectId, req.params.projectId,
req.permission.authMethod, req.permission.authMethod,
@@ -340,7 +347,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
return { return {
data: { data: {
permissions, permissions,
membership, memberships,
assumedPrivilegeDetails assumedPrivilegeDetails
} }
}; };
@@ -1,7 +1,7 @@
import { packRules } from "@casl/ability/extra"; import { packRules } from "@casl/ability/extra";
import { z } from "zod"; import { z } from "zod";
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas"; import { AccessScope, ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns"; import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
@@ -11,7 +11,6 @@ import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas"; import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => { export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -55,13 +54,11 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
handler: async (req) => { handler: async (req) => {
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions)); const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
const role = await server.services.projectRole.createRole({ const role = await server.services.role.createRole({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId projectId: req.params.projectId
}, },
data: { data: {
@@ -73,7 +70,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: req.params.projectId,
event: { event: {
type: EventType.CREATE_PROJECT_ROLE, type: EventType.CREATE_PROJECT_ROLE,
metadata: { metadata: {
@@ -86,7 +83,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -133,12 +130,16 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined; const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
const role = await server.services.projectRole.updateRole({ const role = await server.services.role.updateRole({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
roleId: req.params.roleId, projectId: req.params.projectId
},
selector: {
id: req.params.roleId
},
data: { data: {
...req.body, ...req.body,
permissions: stringifiedPermissions permissions: stringifiedPermissions
@@ -148,7 +149,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.UPDATE_PROJECT_ROLE, type: EventType.UPDATE_PROJECT_ROLE,
metadata: { metadata: {
@@ -161,7 +162,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -192,18 +193,22 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.projectRole.deleteRole({ const role = await server.services.role.deleteRole({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
roleId: req.params.roleId projectId: req.params.projectId
},
selector: {
id: req.params.roleId
}
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: role.projectId, projectId: role.projectId as string,
event: { event: {
type: EventType.DELETE_PROJECT_ROLE, type: EventType.DELETE_PROJECT_ROLE,
metadata: { metadata: {
@@ -214,7 +219,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
} }
}); });
return { role }; return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
@@ -244,17 +249,16 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const roles = await server.services.projectRole.listRoles({ const { roles } = await server.services.role.listRoles({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId projectId: req.params.projectId
} },
data: {}
}); });
return { roles }; return { roles: roles.map((el) => ({ ...el, projectId: el.projectId as string })) };
} }
}); });
@@ -273,24 +277,25 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
}), }),
response: { response: {
200: z.object({ 200: z.object({
role: SanitizedRoleSchema.omit({ version: true }) role: SanitizedRoleSchema
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.projectRole.getRoleBySlug({ const role = await server.services.role.getRoleBySlug({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId projectId: req.params.projectId
}, },
roleSlug: req.params.roleSlug selector: {
slug: req.params.roleSlug
}
}); });
return { role };
return { role: { ...role, projectId: role.projectId as string } };
} }
}); });
}; };
@@ -44,10 +44,7 @@ type TGroupServiceFactoryDep = {
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">; projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">; projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">; projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
permissionService: Pick< permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRoles" | "invalidateProjectPermissionCache">;
TPermissionServiceFactory,
"getOrgPermission" | "getOrgPermissionByRole" | "invalidateProjectPermissionCache"
>;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne">; oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne">;
}; };
@@ -66,12 +66,12 @@ export type TPermissionServiceFactory = {
actorOrgId: string | undefined actorOrgId: string | undefined
) => Promise<{ ) => Promise<{
permission: MongoAbility<OrgPermissionSet, MongoQuery>; permission: MongoAbility<OrgPermissionSet, MongoQuery>;
memberships: TMemberships[]; memberships: Array<TMemberships & { roles: { role: string; customRoleSlug?: string | null }[] }>;
hasRole: (role: string) => boolean; hasRole: (role: string) => boolean;
}>; }>;
getProjectPermission: (arg: TGetProjectPermissionArg) => Promise<{ getProjectPermission: (arg: TGetProjectPermissionArg) => Promise<{
permission: MongoAbility<ProjectPermissionSet, MongoQuery>; permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
memberships: TMemberships[]; memberships: Array<TMemberships & { roles: { role: string; customRoleSlug?: string | null }[] }>;
hasRole: (role: string) => boolean; hasRole: (role: string) => boolean;
}>; }>;
// TODO(simp): check what is this doing // TODO(simp): check what is this doing
+86 -8
View File
@@ -147,6 +147,8 @@ import { TQueueServiceFactory } from "@app/queue";
import { readLimit } from "@app/server/config/rateLimiter"; import { readLimit } from "@app/server/config/rateLimiter";
import { registerSecretScanningV2Webhooks } from "@app/server/plugins/secret-scanner-v2"; import { registerSecretScanningV2Webhooks } from "@app/server/plugins/secret-scanner-v2";
import { accessTokenQueueServiceFactory } from "@app/services/access-token-queue/access-token-queue"; import { accessTokenQueueServiceFactory } from "@app/services/access-token-queue/access-token-queue";
import { additionalPrivilegeDALFactory } from "@app/services/additional-privilege/additional-privilege-dal";
import { additionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal"; import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service"; import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
import { appConnectionDALFactory } from "@app/services/app-connection/app-connection-dal"; import { appConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
@@ -231,6 +233,14 @@ import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal"; import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
import { kmsServiceFactory } from "@app/services/kms/kms-service"; import { kmsServiceFactory } from "@app/services/kms/kms-service";
import { membershipDALFactory } from "@app/services/membership/membership-dal";
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
import { membershipGroupServiceFactory } from "@app/services/membership-group/membership-group-service";
import { membershipIdentityDALFactory } from "@app/services/membership-identity/membership-identity-dal";
import { membershipIdentityServiceFactory } from "@app/services/membership-identity/membership-identity-service";
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
import { membershipUserServiceFactory } from "@app/services/membership-user/membership-user-service";
import { microsoftTeamsIntegrationDALFactory } from "@app/services/microsoft-teams/microsoft-teams-integration-dal"; import { microsoftTeamsIntegrationDALFactory } from "@app/services/microsoft-teams/microsoft-teams-integration-dal";
import { microsoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service"; import { microsoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
import { projectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal"; import { projectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
@@ -283,6 +293,8 @@ import { reminderServiceFactory } from "@app/services/reminder/reminder-service"
import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal"; import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal";
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue"; import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal"; import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
import { roleDALFactory } from "@app/services/role/role-dal";
import { roleServiceFactory } from "@app/services/role/role-service";
import { secretDALFactory } from "@app/services/secret/secret-dal"; import { secretDALFactory } from "@app/services/secret/secret-dal";
import { secretQueueFactory } from "@app/services/secret/secret-queue"; import { secretQueueFactory } from "@app/services/secret/secret-queue";
import { secretServiceFactory } from "@app/services/secret/secret-service"; import { secretServiceFactory } from "@app/services/secret/secret-service";
@@ -531,17 +543,27 @@ export const registerRoutes = async (
const secretScanningV2DAL = secretScanningV2DALFactory(db); const secretScanningV2DAL = secretScanningV2DALFactory(db);
const keyValueStoreDAL = keyValueStoreDALFactory(db); const keyValueStoreDAL = keyValueStoreDALFactory(db);
const membershipDAL = membershipDALFactory(db);
const membershipUserDAL = membershipUserDALFactory(db);
const membershipIdentityDAL = membershipIdentityDALFactory(db);
const membershipGroupDAL = membershipGroupDALFactory(db);
const additionalPrivilegeDAL = additionalPrivilegeDALFactory(db);
const membershipRoleDAL = membershipRoleDALFactory(db);
const roleDAL = roleDALFactory(db);
const eventBusService = eventBusFactory(server.redis); const eventBusService = eventBusFactory(server.redis);
const sseService = sseServiceFactory(eventBusService, server.redis); const sseService = sseServiceFactory(eventBusService, server.redis);
const permissionService = permissionServiceFactory({ const permissionService = permissionServiceFactory({
permissionDAL, permissionDAL,
orgRoleDAL,
projectRoleDAL,
serviceTokenDAL, serviceTokenDAL,
projectDAL, projectDAL,
keyStore keyStore,
roleDAL,
userDAL,
identityDAL
}); });
const assumePrivilegeService = assumePrivilegeServiceFactory({ const assumePrivilegeService = assumePrivilegeServiceFactory({
projectDAL, projectDAL,
permissionService permissionService
@@ -556,6 +578,41 @@ export const registerRoutes = async (
projectDAL projectDAL
}); });
const membershipUserService = membershipUserServiceFactory({
licenseService,
membershipRoleDAL,
membershipUserDAL,
orgDAL,
permissionService,
roleDAL,
userDAL
});
const membershipIdentityService = membershipIdentityServiceFactory({
membershipIdentityDAL,
membershipRoleDAL,
orgDAL,
permissionService,
roleDAL
});
const membershipGroupService = membershipGroupServiceFactory({
membershipGroupDAL,
membershipRoleDAL,
roleDAL
});
const roleService = roleServiceFactory({
permissionService,
roleDAL
});
const additionalPrivilegeService = additionalPrivilegeServiceFactory({
additionalPrivilegeDAL,
membershipDAL,
orgDAL,
permissionService
});
const hsmService = hsmServiceFactory({ const hsmService = hsmServiceFactory({
hsmModule, hsmModule,
envConfig envConfig
@@ -1538,7 +1595,8 @@ export const registerRoutes = async (
identityProjectDAL, identityProjectDAL,
licenseService, licenseService,
identityMetadataDAL, identityMetadataDAL,
keyStore keyStore,
orgDAL
}); });
const identityAuthTemplateService = identityAuthTemplateServiceFactory({ const identityAuthTemplateService = identityAuthTemplateServiceFactory({
@@ -1563,8 +1621,10 @@ export const registerRoutes = async (
identityProjectDAL, identityProjectDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityProjectMembershipRoleDAL, identityProjectMembershipRoleDAL,
projectRoleDAL projectRoleDAL,
orgDAL
}); });
const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({ const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({
projectDAL, projectDAL,
identityProjectAdditionalPrivilegeDAL, identityProjectAdditionalPrivilegeDAL,
@@ -1584,7 +1644,8 @@ export const registerRoutes = async (
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityAccessTokenDAL, identityAccessTokenDAL,
permissionService, permissionService,
licenseService licenseService,
orgDAL
}); });
const identityUaService = identityUaServiceFactory({ const identityUaService = identityUaServiceFactory({
@@ -1594,7 +1655,8 @@ export const registerRoutes = async (
identityUaClientSecretDAL, identityUaClientSecretDAL,
identityUaDAL, identityUaDAL,
licenseService, licenseService,
keyStore keyStore,
orgDAL
}); });
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({ const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
@@ -1604,6 +1666,7 @@ export const registerRoutes = async (
permissionService, permissionService,
licenseService, licenseService,
gatewayService, gatewayService,
orgDAL,
gatewayV2Service, gatewayV2Service,
gatewayV2DAL, gatewayV2DAL,
gatewayDAL, gatewayDAL,
@@ -1611,6 +1674,7 @@ export const registerRoutes = async (
}); });
const identityGcpAuthService = identityGcpAuthServiceFactory({ const identityGcpAuthService = identityGcpAuthServiceFactory({
identityGcpAuthDAL, identityGcpAuthDAL,
orgDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityAccessTokenDAL, identityAccessTokenDAL,
permissionService, permissionService,
@@ -1619,6 +1683,7 @@ export const registerRoutes = async (
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({ const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
identityAccessTokenDAL, identityAccessTokenDAL,
orgDAL,
identityAliCloudAuthDAL, identityAliCloudAuthDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
licenseService, licenseService,
@@ -1627,6 +1692,7 @@ export const registerRoutes = async (
const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({ const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({
identityAccessTokenDAL, identityAccessTokenDAL,
orgDAL,
identityTlsCertAuthDAL, identityTlsCertAuthDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
licenseService, licenseService,
@@ -1636,6 +1702,7 @@ export const registerRoutes = async (
const identityAwsAuthService = identityAwsAuthServiceFactory({ const identityAwsAuthService = identityAwsAuthServiceFactory({
identityAccessTokenDAL, identityAccessTokenDAL,
orgDAL,
identityAwsAuthDAL, identityAwsAuthDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
licenseService, licenseService,
@@ -1644,6 +1711,7 @@ export const registerRoutes = async (
const identityAzureAuthService = identityAzureAuthServiceFactory({ const identityAzureAuthService = identityAzureAuthServiceFactory({
identityAzureAuthDAL, identityAzureAuthDAL,
orgDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityAccessTokenDAL, identityAccessTokenDAL,
permissionService, permissionService,
@@ -1652,6 +1720,7 @@ export const registerRoutes = async (
const identityOciAuthService = identityOciAuthServiceFactory({ const identityOciAuthService = identityOciAuthServiceFactory({
identityAccessTokenDAL, identityAccessTokenDAL,
orgDAL,
identityOciAuthDAL, identityOciAuthDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
licenseService, licenseService,
@@ -1674,6 +1743,7 @@ export const registerRoutes = async (
const identityOidcAuthService = identityOidcAuthServiceFactory({ const identityOidcAuthService = identityOidcAuthServiceFactory({
identityOidcAuthDAL, identityOidcAuthDAL,
orgDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
identityAccessTokenDAL, identityAccessTokenDAL,
permissionService, permissionService,
@@ -1683,6 +1753,7 @@ export const registerRoutes = async (
const identityJwtAuthService = identityJwtAuthServiceFactory({ const identityJwtAuthService = identityJwtAuthServiceFactory({
identityJwtAuthDAL, identityJwtAuthDAL,
orgDAL,
permissionService, permissionService,
identityAccessTokenDAL, identityAccessTokenDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
@@ -1692,6 +1763,7 @@ export const registerRoutes = async (
const identityLdapAuthService = identityLdapAuthServiceFactory({ const identityLdapAuthService = identityLdapAuthServiceFactory({
identityLdapAuthDAL, identityLdapAuthDAL,
orgDAL,
permissionService, permissionService,
kmsService, kmsService,
identityAccessTokenDAL, identityAccessTokenDAL,
@@ -2300,7 +2372,13 @@ export const registerRoutes = async (
pamResource: pamResourceService, pamResource: pamResourceService,
pamAccount: pamAccountService, pamAccount: pamAccountService,
pamSession: pamSessionService, pamSession: pamSessionService,
upgradePath: upgradePathService upgradePath: upgradePathService,
membershipUser: membershipUserService,
membershipIdentity: membershipIdentityService,
membershipGroup: membershipGroupService,
role: roleService,
additionalPrivilege: additionalPrivilegeService
}); });
const cronJobs: CronJob[] = []; const cronJobs: CronJob[] = [];
@@ -209,11 +209,11 @@ export const SanitizedIdentityPrivilegeSchema = IdentityProjectAdditionalPrivile
) )
}); });
export const SanitizedRoleSchema = ProjectRolesSchema.extend({ export const SanitizedRoleSchema = ProjectRolesSchema.omit({ version: true }).extend({
permissions: UnpackedPermissionSchema.array() permissions: UnpackedPermissionSchema.array()
}); });
export const SanitizedRoleSchemaV1 = ProjectRolesSchema.extend({ export const SanitizedRoleSchemaV1 = ProjectRolesSchema.omit({ version: true }).extend({
permissions: UnpackedPermissionSchema.array().transform((caslPermission) => permissions: UnpackedPermissionSchema.array().transform((caslPermission) =>
// first map and remove other actions of folder permission // first map and remove other actions of folder permission
caslPermission caslPermission
@@ -1,7 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { import {
OrgMembershipRole, AccessScope,
ProjectMembershipRole, ProjectMembershipRole,
ProjectMembershipsSchema, ProjectMembershipsSchema,
ProjectUserMembershipRolesSchema, ProjectUserMembershipRolesSchema,
@@ -67,14 +67,23 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const memberships = await server.services.projectMembership.getProjectMemberships({ const { data: memberships } = await server.services.membershipUser.listMemberships({
actorId: req.permission.id, permission: req.permission,
actor: req.permission.type, scopeData: {
actorAuthMethod: req.permission.authMethod, scope: AccessScope.Project,
actorOrgId: req.permission.orgId, orgId: req.permission.orgId,
projectId: req.params.projectId projectId: req.params.projectId
},
data: {}
}); });
return { memberships };
return {
memberships: memberships.map((el) => ({
...el,
userId: el.actorUserId as string,
projectId: req.params.projectId
}))
};
} }
}); });
@@ -242,20 +251,17 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const usernamesAndEmails = [...req.body.emails, ...req.body.usernames]; const usernamesAndEmails = [...req.body.emails, ...req.body.usernames];
const { projectMemberships: memberships } = await server.services.org.inviteUserToOrganization({ const { memberships } = await server.services.membershipUser.createMembership({
actorAuthMethod: req.permission.authMethod, permission: req.permission,
actorId: req.permission.id, scopeData: {
actorOrgId: req.permission.orgId, scope: AccessScope.Project,
actor: req.permission.type, orgId: req.permission.orgId,
inviteeEmails: usernamesAndEmails, projectId: req.params.projectId
orgId: req.permission.orgId, },
organizationRoleSlug: OrgMembershipRole.NoAccess, data: {
projects: [ roles: (req.body.roleSlugs || [ProjectMembershipRole.Member]).map((role) => ({ isTemporary: false, role })),
{ usernames: usernamesAndEmails
id: req.params.projectId, }
projectRoleSlug: req.body.roleSlugs || [ProjectMembershipRole.Member]
}
]
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -263,15 +269,21 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
...req.auditLogInfo, ...req.auditLogInfo,
event: { event: {
type: EventType.ADD_BATCH_PROJECT_MEMBER, type: EventType.ADD_BATCH_PROJECT_MEMBER,
metadata: memberships.map(({ userId, id }) => ({ metadata: memberships.map(({ actorUserId, id }) => ({
userId: userId || "", userId: actorUserId || "",
membershipId: id, membershipId: id,
email: "" email: ""
})) }))
} }
}); });
return { memberships }; return {
memberships: memberships.map((el) => ({
...el,
userId: el.actorUserId as string,
projectId: req.params.projectId
}))
};
} }
}); });
@@ -0,0 +1,88 @@
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";
type TConvertorServiceFactoryDep = {
projectDAL: Pick<TProjectDALFactory, "findOne">;
membershipDAL: Pick<TMembershipDALFactory, "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 };
};
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` });
return project;
};
const userMembershipIdToUserId = async (scopeData: AccessScopeData, membershipId: string) => {
const dbFields = $getScopeDatabaseFields(scopeData);
const membership = await membershipDAL.findOne({
scope: scopeData.scope,
id: membershipId,
...dbFields,
scopeOrgId: scopeData.orgId
});
if (!membership || !membership.actorUserId) {
throw new NotFoundError({ message: `Membership with id ${membershipId} not found` });
}
return membership.actorUserId;
};
const groupMembershipIdToGroupId = async (scopeData: AccessScopeData, membershipId: string) => {
const dbFields = $getScopeDatabaseFields(scopeData);
const membership = await membershipDAL.findOne({
scope: scopeData.scope,
id: membershipId,
...dbFields,
scopeOrgId: scopeData.orgId
});
if (!membership || !membership.actorGroupId) {
throw new NotFoundError({ message: `Membership with id ${membershipId} not found` });
}
return membership.actorGroupId;
};
const identityMembershipIdToGroupId = async (scopeData: AccessScopeData, membershipId: string) => {
const dbFields = $getScopeDatabaseFields(scopeData);
const membership = await membershipDAL.findOne({
scope: scopeData.scope,
id: membershipId,
...dbFields,
scopeOrgId: scopeData.orgId
});
if (!membership || !membership.actorIdentityId) {
throw new NotFoundError({ message: `Membership with id ${membershipId} not found` });
}
return membership.actorIdentityId;
};
return {
projectSlugToId,
userMembershipIdToUserId,
groupMembershipIdToGroupId,
identityMembershipIdToGroupId
};
};
@@ -43,10 +43,7 @@ type TGroupProjectServiceFactoryDep = {
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">; projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
projectBotDAL: TProjectBotDALFactory; projectBotDAL: TProjectBotDALFactory;
groupDAL: Pick<TGroupDALFactory, "findOne" | "findAllGroupPossibleMembers">; groupDAL: Pick<TGroupDALFactory, "findOne" | "findAllGroupPossibleMembers">;
permissionService: Pick< permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles" | "invalidateProjectPermissionCache">;
TPermissionServiceFactory,
"getProjectPermission" | "getProjectPermissionByRole" | "invalidateProjectPermissionCache"
>;
}; };
export type TGroupProjectServiceFactory = ReturnType<typeof groupProjectServiceFactory>; export type TGroupProjectServiceFactory = ReturnType<typeof groupProjectServiceFactory>;
@@ -115,29 +115,27 @@ export const newProjectMembershipIdentityFactory = ({
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId); const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId);
const permissionRoles = await permissionService.getProjectPermissionByRoles( const permissionRoles = await permissionService.getProjectPermissionByRoles(
dto.data.roles.map((el) => el.role), dto.data.roles.filter((el) => el.role !== ProjectMembershipRole.NoAccess).map((el) => el.role),
scope.value scope.value
); );
for (const permissionRole of permissionRoles) { for (const permissionRole of permissionRoles) {
if (permissionRole?.role?.name !== ProjectMembershipRole.NoAccess) { const permissionBoundary = validatePrivilegeChangeOperation(
const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem,
shouldUseNewPrivilegeSystem, ProjectPermissionIdentityActions.GrantPrivileges,
ProjectPermissionIdentityActions.GrantPrivileges, ProjectPermissionSub.Identity,
ProjectPermissionSub.Identity, permission,
permission, permissionRole.permission
permissionRole.permission );
); if (!permissionBoundary.isValid)
if (!permissionBoundary.isValid) throw new PermissionBoundaryError({
throw new PermissionBoundaryError({ message: constructPermissionErrorMessage(
message: constructPermissionErrorMessage( "Failed to create identity project membership",
"Failed to create identity project membership", shouldUseNewPrivilegeSystem,
shouldUseNewPrivilegeSystem, ProjectPermissionIdentityActions.GrantPrivileges,
ProjectPermissionIdentityActions.GrantPrivileges, ProjectPermissionSub.Identity
ProjectPermissionSub.Identity ),
), details: { missingPermissions: permissionBoundary.missingPermissions }
details: { missingPermissions: permissionBoundary.missingPermissions } });
});
}
} }
}; };
@@ -44,6 +44,7 @@ export const membershipUserDALFactory = (db: TDbClient) => {
}) })
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId) .where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
.where(`${TableName.Membership}.actorUserId`, userId) .where(`${TableName.Membership}.actorUserId`, userId)
.where(`${TableName.Users}.isGhost`, false)
.where((qb) => { .where((qb) => {
if (scopeData.scope === AccessScope.Organization) { if (scopeData.scope === AccessScope.Organization) {
void qb.where(`${TableName.Membership}.scope`, AccessScope.Organization); void qb.where(`${TableName.Membership}.scope`, AccessScope.Organization);
@@ -60,7 +61,9 @@ export const membershipUserDALFactory = (db: TDbClient) => {
}) })
.select(selectAllTableCols(TableName.Membership)) .select(selectAllTableCols(TableName.Membership))
.select( .select(
db.ref("slug").withSchema(TableName.Role).as("roleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("id").withSchema(TableName.Role).as("customRoleId"),
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("role").withSchema(TableName.MembershipRole).as("membershipRole"), db.ref("role").withSchema(TableName.MembershipRole).as("membershipRole"),
db.ref("temporaryMode").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryMode"), db.ref("temporaryMode").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryMode"),
@@ -78,19 +81,35 @@ export const membershipUserDALFactory = (db: TDbClient) => {
db.ref("updatedAt").withSchema(TableName.MembershipRole).as("membershipRoleUpdatedAt"), db.ref("updatedAt").withSchema(TableName.MembershipRole).as("membershipRoleUpdatedAt"),
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"), db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"), db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue") db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue"),
db.ref("username").withSchema(TableName.Users).as("userUsername"),
db.ref("email").withSchema(TableName.Users).as("userEmail"),
db.ref("firstName").withSchema(TableName.Users).as("userFirstName"),
db.ref("lastName").withSchema(TableName.Users).as("userLastName"),
db.ref("id").withSchema(TableName.Users).as("userId")
); );
const data = sqlNestRelationships({ const data = sqlNestRelationships({
data: docs, data: docs,
key: "id", key: "id",
parentMapper: (el) => MembershipsSchema.parse(el), parentMapper: (el) => ({
...MembershipsSchema.parse(el),
user: {
username: el.userUsername,
email: el.userEmail,
firstName: el.userFirstName,
lastName: el.userLastName,
id: el.userId
}
}),
childrenMapper: [ childrenMapper: [
{ {
key: "membershipRoleId", key: "membershipRoleId",
label: "roles" as const, label: "roles" as const,
mapper: ({ mapper: ({
roleSlug, customRoleSlug,
customRoleName,
customRoleId,
membershipRoleId, membershipRoleId,
membershipRole, membershipRole,
membershipRoleIsTemporary, membershipRoleIsTemporary,
@@ -103,7 +122,9 @@ export const membershipUserDALFactory = (db: TDbClient) => {
}) => ({ }) => ({
id: membershipRoleId, id: membershipRoleId,
role: membershipRole, role: membershipRole,
customRoleSlug: roleSlug, customRoleSlug,
customRoleName,
customRoleId,
temporaryRange: membershipRoleTemporaryRange, temporaryRange: membershipRoleTemporaryRange,
temporaryMode: membershipRoleTemporaryMode, temporaryMode: membershipRoleTemporaryMode,
temporaryAccessStartTime: membershipRoleTemporaryAccessStartTime, temporaryAccessStartTime: membershipRoleTemporaryAccessStartTime,
@@ -140,6 +161,7 @@ export const membershipUserDALFactory = (db: TDbClient) => {
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`) .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.distinct(`${TableName.Membership}.id`) .distinct(`${TableName.Membership}.id`)
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId) .where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
.where(`${TableName.Users}.isGhost`, false)
.where((qb) => { .where((qb) => {
if (scopeData.scope === AccessScope.Organization) { if (scopeData.scope === AccessScope.Organization) {
void qb.where(`${TableName.Membership}.scope`, AccessScope.Organization); void qb.where(`${TableName.Membership}.scope`, AccessScope.Organization);
@@ -188,7 +210,9 @@ export const membershipUserDALFactory = (db: TDbClient) => {
.whereIn(`${TableName.Membership}.id`, paginatedUsers) .whereIn(`${TableName.Membership}.id`, paginatedUsers)
.select(selectAllTableCols(TableName.Membership)) .select(selectAllTableCols(TableName.Membership))
.select( .select(
db.ref("slug").withSchema(TableName.Role).as("roleSlug"), db.ref("slug").withSchema(TableName.Role).as("customRoleSlug"),
db.ref("name").withSchema(TableName.Role).as("customRoleName"),
db.ref("id").withSchema(TableName.Role).as("customRoleId"),
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("role").withSchema(TableName.MembershipRole).as("membershipRole"), db.ref("role").withSchema(TableName.MembershipRole).as("membershipRole"),
db.ref("temporaryMode").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryMode"), db.ref("temporaryMode").withSchema(TableName.MembershipRole).as("membershipRoleTemporaryMode"),
@@ -203,7 +227,12 @@ export const membershipUserDALFactory = (db: TDbClient) => {
.withSchema(TableName.MembershipRole) .withSchema(TableName.MembershipRole)
.as("membershipRoleTemporaryAccessEndTime"), .as("membershipRoleTemporaryAccessEndTime"),
db.ref("createdAt").withSchema(TableName.MembershipRole).as("membershipRoleCreatedAt"), db.ref("createdAt").withSchema(TableName.MembershipRole).as("membershipRoleCreatedAt"),
db.ref("updatedAt").withSchema(TableName.MembershipRole).as("membershipRoleUpdatedAt") db.ref("updatedAt").withSchema(TableName.MembershipRole).as("membershipRoleUpdatedAt"),
db.ref("username").withSchema(TableName.Users).as("userUsername"),
db.ref("email").withSchema(TableName.Users).as("userEmail"),
db.ref("firstName").withSchema(TableName.Users).as("userFirstName"),
db.ref("lastName").withSchema(TableName.Users).as("userLastName"),
db.ref("id").withSchema(TableName.Users).as("userId")
) )
.select( .select(
db.raw( db.raw(
@@ -214,13 +243,24 @@ export const membershipUserDALFactory = (db: TDbClient) => {
const data = sqlNestRelationships({ const data = sqlNestRelationships({
data: docs, data: docs,
key: "id", key: "id",
parentMapper: (el) => MembershipsSchema.parse(el), parentMapper: (el) => ({
...MembershipsSchema.parse(el),
user: {
username: el.userUsername,
email: el.userEmail,
firstName: el.userFirstName,
lastName: el.userLastName,
id: el.userId
}
}),
childrenMapper: [ childrenMapper: [
{ {
key: "membershipRoleId", key: "membershipRoleId",
label: "roles" as const, label: "roles" as const,
mapper: ({ mapper: ({
roleSlug, customRoleSlug,
customRoleName,
customRoleId,
membershipRoleId, membershipRoleId,
membershipRole, membershipRole,
membershipRoleIsTemporary, membershipRoleIsTemporary,
@@ -233,7 +273,9 @@ export const membershipUserDALFactory = (db: TDbClient) => {
}) => ({ }) => ({
id: membershipRoleId, id: membershipRoleId,
role: membershipRole, role: membershipRole,
customRoleSlug: roleSlug, customRoleSlug,
customRoleName,
customRoleId,
temporaryRange: membershipRoleTemporaryRange, temporaryRange: membershipRoleTemporaryRange,
temporaryMode: membershipRoleTemporaryMode, temporaryMode: membershipRoleTemporaryMode,
temporaryAccessStartTime: membershipRoleTemporaryAccessStartTime, temporaryAccessStartTime: membershipRoleTemporaryAccessStartTime,
@@ -386,7 +386,7 @@ export const membershipUserServiceFactory = ({
limit: dto.data.limit, limit: dto.data.limit,
offset: dto.data.offset, offset: dto.data.offset,
username: dto.data.username, username: dto.data.username,
role: dto.data.roles.length role: dto.data?.roles?.length
? { ? {
[SearchResourceOperators.$in]: dto.data.roles [SearchResourceOperators.$in]: dto.data.roles
} }
@@ -55,14 +55,11 @@ export type TUpdateMembershipUserDTO = {
export type TListMembershipUserDTO = { export type TListMembershipUserDTO = {
permission: OrgServiceActor; permission: OrgServiceActor;
scopeData: AccessScopeData; scopeData: AccessScopeData;
selector: {
actorId: string;
};
data: { data: {
limit?: number; limit?: number;
offset?: number; offset?: number;
username?: string; username?: string;
roles: string[]; roles?: string[];
}; };
}; };
@@ -43,10 +43,7 @@ import {
import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal";
type TProjectMembershipServiceFactoryDep = { type TProjectMembershipServiceFactoryDep = {
permissionService: Pick< permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRoles"| "invalidateProjectPermissionCache">;
TPermissionServiceFactory,
"getProjectPermission" | "getProjectPermissionByRole" | "invalidateProjectPermissionCache"
>;
smtpService: TSmtpService; smtpService: TSmtpService;
projectBotDAL: TProjectBotDALFactory; projectBotDAL: TProjectBotDALFactory;
projectMembershipDAL: TProjectMembershipDALFactory; projectMembershipDAL: TProjectMembershipDALFactory;
@@ -573,6 +570,8 @@ export const projectMembershipServiceFactory = ({
tx tx
); );
// TODO(simp): likewise check in all services if i am missing anything
// Deletion should handle all cleanups like wise
await secretReminderRecipientsDAL.delete( await secretReminderRecipientsDAL.delete(
{ {
projectId, projectId,
+7 -6
View File
@@ -2,6 +2,7 @@ import { AccessScope, TableName } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
import { newNamespaceRoleFactory } from "./namespace/namespace-role-factory"; import { newNamespaceRoleFactory } from "./namespace/namespace-role-factory";
import { newOrgRoleFactory } from "./org/org-role-factory"; import { newOrgRoleFactory } from "./org/org-role-factory";
@@ -63,7 +64,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
[scope.key]: scope.value [scope.key]: scope.value
}); });
return { ...role, [scope.key]: scope.value }; return { ...role, [scope.key]: scope.value, permissions: unpackPermissions(role.permissions) };
}; };
const updateRole = async (dto: TUpdateRoleDTO) => { const updateRole = async (dto: TUpdateRoleDTO) => {
@@ -99,7 +100,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
permissions: data?.permissions permissions: data?.permissions
}); });
return { ...role, [scope.key]: scope.value }; return { ...role, [scope.key]: scope.value, permissions: unpackPermissions(role.permissions) };
}; };
const deleteRole = async (dto: TDeleteRoleDTO) => { const deleteRole = async (dto: TDeleteRoleDTO) => {
@@ -119,7 +120,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
[scope.key]: scope.value [scope.key]: scope.value
}); });
return { ...role, [scope.key]: scope.value }; return { ...role, [scope.key]: scope.value, permissions: unpackPermissions(role.permissions) };
}; };
const listRoles = async (dto: TListRoleDTO) => { const listRoles = async (dto: TListRoleDTO) => {
@@ -136,7 +137,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
{ limit: dto.data.limit, offset: dto.data.offset, sort: [[`${TableName.Role}.slug` as "slug", "asc"]] } { limit: dto.data.limit, offset: dto.data.offset, sort: [[`${TableName.Role}.slug` as "slug", "asc"]] }
); );
return { roles }; return { roles: roles.map((el) => ({ ...el, permissions: unpackPermissions(el.permissions) })) };
}; };
const getRoleById = async (dto: TGetRoleByIdDTO) => { const getRoleById = async (dto: TGetRoleByIdDTO) => {
@@ -152,7 +153,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
}); });
if (!role) throw new NotFoundError({ message: `Role with id ${dto.selector.id} not found` }); if (!role) throw new NotFoundError({ message: `Role with id ${dto.selector.id} not found` });
return { ...role, [scope.key]: scope.value }; return { ...role, [scope.key]: scope.value, permissions: unpackPermissions(role.permissions) };
}; };
const getRoleBySlug = async (dto: TGetRoleBySlugDTO) => { const getRoleBySlug = async (dto: TGetRoleBySlugDTO) => {
@@ -168,7 +169,7 @@ export const roleServiceFactory = ({ roleDAL, permissionService }: TRoleServiceF
}); });
if (!role) throw new NotFoundError({ message: `Role with slug ${dto.selector.slug} not found` }); if (!role) throw new NotFoundError({ message: `Role with slug ${dto.selector.slug} not found` });
return { ...role, [scope.key]: scope.value }; return { ...role, [scope.key]: scope.value, permissions: unpackPermissions(role.permissions) };
}; };
return { return {
+2 -2
View File
@@ -16,7 +16,7 @@ export type TCreateRoleDTO = {
scopeData: AccessScopeData; scopeData: AccessScopeData;
data: { data: {
name: string; name: string;
description?: string; description?: string | null;
slug: string; slug: string;
permissions: unknown; permissions: unknown;
}; };
@@ -30,7 +30,7 @@ export type TUpdateRoleDTO = {
}; };
data: Partial<{ data: Partial<{
name: string; name: string;
description?: string; description?: string | null;
slug: string; slug: string;
permissions: unknown; permissions: unknown;
}>; }>;
@@ -76,6 +76,7 @@ export const useCreateOrgRole = () => {
data: { role } data: { role }
} = await apiRequest.post(`/api/v1/organization/${orgId}/roles`, { } = await apiRequest.post(`/api/v1/organization/${orgId}/roles`, {
...dto, ...dto,
// TODO(simp): removing packing
permissions: permissions.length ? packRules(permissions) : [] permissions: permissions.length ? packRules(permissions) : []
}); });