From fd98465dec0af45ba41b49a96b171020112e5d2c Mon Sep 17 00:00:00 2001 From: = Date: Thu, 2 Oct 2025 15:19:33 +0530 Subject: [PATCH] feat: fixing more and more ts --- backend/src/@types/fastify.d.ts | 11 +- backend/src/ee/routes/v1/org-role-router.ts | 21 +- .../src/ee/routes/v1/project-role-router.ts | 14 +- backend/src/server/routes/index.ts | 260 ++++++-------- .../v1/identity-alicloud-auth-router.ts | 2 +- .../routes/v1/identity-aws-iam-auth-router.ts | 2 +- .../routes/v1/identity-azure-auth-router.ts | 2 +- .../routes/v1/identity-gcp-auth-router.ts | 2 +- .../routes/v1/identity-jwt-auth-router.ts | 2 +- .../v1/identity-kubernetes-auth-router.ts | 2 +- .../routes/v1/identity-ldap-auth-router.ts | 2 +- .../routes/v1/identity-oci-auth-router.ts | 2 +- .../routes/v1/identity-oidc-auth-router.ts | 2 +- .../v1/identity-tls-cert-auth-router.ts | 2 +- .../routes/v1/identity-token-auth-router.ts | 6 +- .../v1/identity-universal-auth-router.ts | 2 +- .../identity-access-token-service.ts | 4 +- .../identity-alicloud-auth-service.ts | 75 ++-- .../identity-aws-auth-service.ts | 75 ++-- .../identity-azure-auth-service.ts | 75 ++-- .../identity-gcp-auth-service.ts | 75 ++-- .../identity-jwt-auth-service.ts | 81 +++-- .../identity-kubernetes-auth-service.ts | 95 +++-- .../identity-ldap-auth-service.ts | 108 ++++-- .../identity-oci-auth-service.ts | 73 ++-- .../identity-oidc-auth-service.ts | 83 +++-- .../identity-project-service.ts | 324 +----------------- .../identity-tls-cert-auth-service.ts | 83 +++-- .../identity-tls-cert-auth-types.ts | 4 +- .../identity-token-auth-service.ts | 119 +++++-- .../identity-ua/identity-ua-service.ts | 161 ++++++--- .../src/services/identity/identity-org-dal.ts | 2 +- .../src/services/identity/identity-service.ts | 82 +++-- .../membership-group-service.ts | 2 +- .../services/org-admin/org-admin-service.ts | 47 ++- backend/src/services/org/org-role-service.ts | 1 - backend/src/services/org/org-service.ts | 202 +++++++---- .../project-key/project-key-service.ts | 15 +- backend/src/services/project/project-dal.ts | 37 +- backend/src/services/project/project-queue.ts | 31 +- .../src/services/project/project-service.ts | 86 ++--- .../src/services/role/org/org-role-factory.ts | 1 + backend/src/services/role/role-service.ts | 73 +++- backend/src/services/role/role-types.ts | 5 + backend/src/services/secret/secret-queue.ts | 27 +- .../super-admin/super-admin-service.ts | 104 ++++-- backend/src/services/user/user-service.ts | 46 ++- 47 files changed, 1369 insertions(+), 1161 deletions(-) diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index 8f5dd0f23..0ead72407 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -20,8 +20,6 @@ import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2 import { TGithubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service"; import { TGroupServiceFactory } from "@app/ee/services/group/group-service"; import { TIdentityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template"; -import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service"; -import { TIdentityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service"; import { TKmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal"; import { TKmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service"; import { TKmipServiceFactory } from "@app/ee/services/kmip/kmip-service"; @@ -35,7 +33,6 @@ import { TPamSessionServiceFactory } from "@app/ee/services/pam-session/pam-sess import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPitServiceFactory } from "@app/ee/services/pit/pit-service"; import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-types"; -import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types"; import { RateLimitConfiguration, TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-types"; import { TRelayServiceFactory } from "@app/ee/services/relay/relay-service"; import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-types"; @@ -53,7 +50,6 @@ 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 { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types"; 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 { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service"; import { TAuthLoginFactory } from "@app/services/auth/auth-login-service"; @@ -95,7 +91,6 @@ import { TMembershipUserServiceFactory } from "@app/services/membership-user/mem import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service"; import { TNotificationServiceFactory } from "@app/services/notification/notification-service"; import { TOfflineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service"; -import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service"; import { TOrgServiceFactory } from "@app/services/org/org-service"; import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-service"; import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service"; @@ -108,7 +103,6 @@ import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service"; import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service"; import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; -import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service"; import { TReminderServiceFactory } from "@app/services/reminder/reminder-types"; import { TRoleServiceFactory } from "@app/services/role/role-service"; import { TSecretServiceFactory } from "@app/services/secret/secret-service"; @@ -131,6 +125,7 @@ import { TUserEngagementServiceFactory } from "@app/services/user-engagement/use import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service"; import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integration/workflow-integration-service"; import { TConvertorServiceFactory } from "@app/services/convertor/convertor-service"; +import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service"; declare module "@fastify/request-context" { interface RequestContextData { @@ -219,7 +214,6 @@ declare module "fastify" { authToken: TAuthTokenServiceFactory; permission: TPermissionServiceFactory; org: TOrgServiceFactory; - orgRole: TOrgRoleServiceFactory; oidc: TOidcConfigServiceFactory; superAdmin: TSuperAdminServiceFactory; user: TUserServiceFactory; @@ -231,7 +225,6 @@ declare module "fastify" { projectMembership: TProjectMembershipServiceFactory; projectEnv: TProjectEnvServiceFactory; projectKey: TProjectKeyServiceFactory; - projectRole: TProjectRoleServiceFactory; secret: TSecretServiceFactory; secretReplication: TSecretReplicationServiceFactory; secretTag: TSecretTagServiceFactory; @@ -331,8 +324,8 @@ declare module "fastify" { membershipUser: TMembershipUserServiceFactory; membershipIdentity: TMembershipIdentityServiceFactory; membershipGroup: TMembershipGroupServiceFactory; - role: TRoleServiceFactory; additionalPrivilege: TAdditionalPrivilegeServiceFactory; + role: TRoleServiceFactory; convertor: TConvertorServiceFactory; }; // this is exclusive use for middlewares in which we need to inject data diff --git a/backend/src/ee/routes/v1/org-role-router.ts b/backend/src/ee/routes/v1/org-role-router.ts index c7aacf46e..46fb58e54 100644 --- a/backend/src/ee/routes/v1/org-role-router.ts +++ b/backend/src/ee/routes/v1/org-role-router.ts @@ -277,21 +277,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { permissions, memberships } = await server.services.orgRole.getUserPermission( - req.permission.id, - req.params.organizationId, - req.permission.authMethod, - req.permission.orgId - ); + const { permissions, memberships } = await server.services.role.getUserPermission({ + permission: req.permission, + scopeData: { + scope: AccessScope.Organization, + orgId: req.permission.orgId + } + }); return { permissions, - memberships: memberships.map((el) => ({ - ...el, - role: el.roles[0].customRoleSlug || el.roles[0].role, - orgId: el.scopeOrgId, - status: el.status || "", - isActive: el.isActive || true - })) + memberships }; } }); diff --git a/backend/src/ee/routes/v1/project-role-router.ts b/backend/src/ee/routes/v1/project-role-router.ts index 2a5c77d66..acf34cb3b 100644 --- a/backend/src/ee/routes/v1/project-role-router.ts +++ b/backend/src/ee/routes/v1/project-role-router.ts @@ -337,12 +337,14 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { permissions, memberships, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission( - req.permission.id, - req.params.projectId, - req.permission.authMethod, - req.permission.orgId - ); + const { permissions, memberships, assumedPrivilegeDetails } = await server.services.role.getUserPermission({ + permission: req.permission, + scopeData: { + scope: AccessScope.Project, + projectId: req.params.projectId, + orgId: req.permission.orgId + } + }); return { data: { diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index c0c2b35fe..59ca201fc 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -50,9 +50,6 @@ import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { HsmModule } from "@app/ee/services/hsm/hsm-types"; import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal"; import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service"; -import { identityProjectAdditionalPrivilegeDALFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-dal"; -import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service"; -import { identityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service"; import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal"; import { kmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal"; import { kmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service"; @@ -79,8 +76,6 @@ import { permissionServiceFactory } from "@app/ee/services/permission/permission import { pitServiceFactory } from "@app/ee/services/pit/pit-service"; import { projectTemplateDALFactory } from "@app/ee/services/project-template/project-template-dal"; import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service"; -import { projectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal"; -import { projectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service"; import { rateLimitDALFactory } from "@app/ee/services/rate-limit/rate-limit-dal"; import { rateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service"; import { instanceRelayConfigDalFactory } from "@app/ee/services/relay/instance-relay-config-dal"; @@ -189,8 +184,6 @@ import { folderCommitChangesDALFactory } from "@app/services/folder-commit-chang import { folderTreeCheckpointDALFactory } from "@app/services/folder-tree-checkpoint/folder-tree-checkpoint-dal"; import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal"; import { groupProjectDALFactory } from "@app/services/group-project/group-project-dal"; -import { groupProjectMembershipRoleDALFactory } from "@app/services/group-project/group-project-membership-role-dal"; -import { groupProjectServiceFactory } from "@app/services/group-project/group-project-service"; import { identityDALFactory } from "@app/services/identity/identity-dal"; import { identityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal"; import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal"; @@ -216,8 +209,6 @@ import { identityOciAuthServiceFactory } from "@app/services/identity-oci-auth/i import { identityOidcAuthDALFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-dal"; import { identityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service"; import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal"; -import { identityProjectMembershipRoleDALFactory } from "@app/services/identity-project/identity-project-membership-role-dal"; -import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service"; import { identityTlsCertAuthDALFactory } from "@app/services/identity-tls-cert-auth/identity-tls-cert-auth-dal"; import { identityTlsCertAuthServiceFactory } from "@app/services/identity-tls-cert-auth/identity-tls-cert-auth-service"; import { identityTokenAuthDALFactory } from "@app/services/identity-token-auth/identity-token-auth-dal"; @@ -252,7 +243,6 @@ import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-re import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal"; import { orgBotDALFactory } from "@app/services/org/org-bot-dal"; import { orgDALFactory } from "@app/services/org/org-dal"; -import { orgRoleServiceFactory } from "@app/services/org/org-role-service"; import { orgServiceFactory } from "@app/services/org/org-service"; import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service"; import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; @@ -283,9 +273,6 @@ import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal" import { projectKeyServiceFactory } from "@app/services/project-key/project-key-service"; import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; -import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal"; -import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal"; -import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service"; import { reminderDALFactory } from "@app/services/reminder/reminder-dal"; import { dailyReminderQueueServiceFactory } from "@app/services/reminder/reminder-queue"; import { reminderServiceFactory } from "@app/services/reminder/reminder-service"; @@ -353,6 +340,9 @@ import { initializeOauthConfigSync } from "./v1/sso-router"; import { registerV2Routes } from "./v2"; import { registerV3Routes } from "./v3"; import { registerV4Routes } from "./v4"; +import { groupProjectServiceFactory } from "@app/services/group-project/group-project-service"; +import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service"; +import { convertorServiceFactory } from "@app/services/convertor/convertor-service"; const histogram = monitorEventLoopDelay({ resolution: 20 }); histogram.enable(); @@ -399,9 +389,6 @@ export const registerRoutes = async ( const projectDAL = projectDALFactory(db); const projectSshConfigDAL = projectSshConfigDALFactory(db); const projectMembershipDAL = projectMembershipDALFactory(db); - const projectUserAdditionalPrivilegeDAL = projectUserAdditionalPrivilegeDALFactory(db); - const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(db); - const projectRoleDAL = projectRoleDALFactory(db); const projectEnvDAL = projectEnvDALFactory(db); const projectKeyDAL = projectKeyDALFactory(db); const projectBotDAL = projectBotDALFactory(db); @@ -433,8 +420,6 @@ export const registerRoutes = async ( const identityAccessTokenDAL = identityAccessTokenDALFactory(db); const identityOrgMembershipDAL = identityOrgDALFactory(db); const identityProjectDAL = identityProjectDALFactory(db); - const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db); - const identityProjectAdditionalPrivilegeDAL = identityProjectAdditionalPrivilegeDALFactory(db); const identityAuthTemplateDAL = identityAuthTemplateDALFactory(db); const identityTokenAuthDAL = identityTokenAuthDALFactory(db); @@ -492,7 +477,6 @@ export const registerRoutes = async ( const gitAppOrgDAL = gitAppDALFactory(db); const groupDAL = groupDALFactory(db); const groupProjectDAL = groupProjectDALFactory(db); - const groupProjectMembershipRoleDAL = groupProjectMembershipRoleDALFactory(db); const userGroupMembershipDAL = userGroupMembershipDALFactory(db); const secretScanningDAL = secretScanningDALFactory(db); const secretSharingDAL = secretSharingDALFactory(db); @@ -602,7 +586,10 @@ export const registerRoutes = async ( const roleService = roleServiceFactory({ permissionService, - roleDAL + roleDAL, + projectDAL, + identityDAL, + userDAL }); const additionalPrivilegeService = additionalPrivilegeServiceFactory({ additionalPrivilegeDAL, @@ -676,13 +663,12 @@ export const registerRoutes = async ( userDAL, secretApprovalRequestDAL }); - const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, orgMembershipDAL }); + const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL }); const samlService = samlConfigServiceFactory({ identityMetadataDAL, permissionService, orgDAL, - orgMembershipDAL, userDAL, userAliasDAL, samlConfigDAL, @@ -695,12 +681,12 @@ export const registerRoutes = async ( licenseService, tokenService, smtpService, - kmsService + kmsService, + membershipRoleDAL }); const groupService = groupServiceFactory({ userDAL, groupDAL, - groupProjectDAL, orgDAL, userGroupMembershipDAL, projectDAL, @@ -708,17 +694,13 @@ export const registerRoutes = async ( projectKeyDAL, permissionService, licenseService, - oidcConfigDAL + oidcConfigDAL, + membershipGroupDAL, + membershipRoleDAL }); const groupProjectService = groupProjectServiceFactory({ groupDAL, - groupProjectDAL, - groupProjectMembershipRoleDAL, - userGroupMembershipDAL, projectDAL, - projectKeyDAL, - projectBotDAL, - projectRoleDAL, permissionService }); @@ -763,18 +745,18 @@ export const registerRoutes = async ( userDAL, userAliasDAL, orgDAL, - orgMembershipDAL, projectDAL, - projectUserAdditionalPrivilegeDAL, projectMembershipDAL, - groupDAL, - groupProjectDAL, userGroupMembershipDAL, projectKeyDAL, projectBotDAL, permissionService, smtpService, - externalGroupOrgRoleMappingDAL + externalGroupOrgRoleMappingDAL, + groupDAL, + membershipGroupDAL, + membershipRoleDAL, + membershipUserDAL }); const githubOrgSyncConfigService = githubOrgSyncServiceFactory({ @@ -791,9 +773,7 @@ export const registerRoutes = async ( ldapConfigDAL, ldapGroupMapDAL, orgDAL, - orgMembershipDAL, groupDAL, - groupProjectDAL, projectKeyDAL, projectDAL, projectBotDAL, @@ -804,7 +784,9 @@ export const registerRoutes = async ( licenseService, tokenService, smtpService, - kmsService + kmsService, + membershipGroupDAL, + membershipRoleDAL }); const telemetryService = telemetryServiceFactory({ @@ -826,13 +808,12 @@ export const registerRoutes = async ( const userService = userServiceFactory({ userDAL, orgDAL, - orgMembershipDAL, tokenService, permissionService, groupProjectDAL, smtpService, - projectMembershipDAL, - userAliasDAL + userAliasDAL, + membershipUserDAL }); const upgradePathService = upgradePathServiceFactory({ keyStore }); @@ -849,9 +830,10 @@ export const registerRoutes = async ( tokenService, orgDAL, totpService, - orgMembershipDAL, auditLogService, - notificationService + notificationService, + membershipRoleDAL, + membershipUserDAL }); const passwordService = authPaswordServiceFactory({ tokenService, @@ -881,14 +863,10 @@ export const registerRoutes = async ( folderDAL, licenseService, samlConfigDAL, - orgRoleDAL, permissionService, orgDAL, incidentContactDAL, tokenService, - projectUserAdditionalPrivilegeDAL, - projectUserMembershipRoleDAL, - projectRoleDAL, projectDAL, projectMembershipDAL, orgMembershipDAL, @@ -901,7 +879,10 @@ export const registerRoutes = async ( ldapConfigDAL, loginService, projectBotService, - reminderService + reminderService, + membershipRoleDAL, + membershipUserDAL, + roleDAL }); const signupService = authSignupServiceFactory({ tokenService, @@ -912,18 +893,10 @@ export const registerRoutes = async ( projectKeyDAL, projectDAL, projectBotDAL, - groupProjectDAL, - projectMembershipDAL, - projectUserMembershipRoleDAL, orgDAL, orgService, - licenseService - }); - const orgRoleService = orgRoleServiceFactory({ - permissionService, - orgRoleDAL, - orgDAL, - externalGroupOrgRoleMappingDAL + licenseService, + membershipGroupDAL }); const microsoftTeamsService = microsoftTeamsServiceFactory({ @@ -940,8 +913,6 @@ export const registerRoutes = async ( userAliasDAL, identityTokenAuthDAL, identityAccessTokenDAL, - orgMembershipDAL, - identityOrgMembershipDAL, authService: loginService, serverCfgDAL: superAdminDAL, kmsRootConfigDAL, @@ -953,7 +924,10 @@ export const registerRoutes = async ( microsoftTeamsService, invalidateCacheQueue, smtpService, - tokenService + tokenService, + membershipIdentityDAL, + membershipRoleDAL, + membershipUserDAL }); const offlineUsageReportService = offlineUsageReportServiceFactory({ @@ -965,9 +939,10 @@ export const registerRoutes = async ( smtpService, projectDAL, permissionService, - projectUserMembershipRoleDAL, - projectMembershipDAL, - notificationService + notificationService, + membershipRoleDAL, + membershipUserDAL, + projectMembershipDAL }); const rateLimitService = rateLimitServiceFactory({ @@ -993,32 +968,24 @@ export const registerRoutes = async ( const projectMembershipService = projectMembershipServiceFactory({ projectMembershipDAL, - projectUserMembershipRoleDAL, projectDAL, permissionService, - projectBotDAL, orgDAL, userDAL, - projectUserAdditionalPrivilegeDAL, userGroupMembershipDAL, smtpService, projectKeyDAL, - projectRoleDAL, groupProjectDAL, secretReminderRecipientsDAL, licenseService, - notificationService - }); - const projectUserAdditionalPrivilegeService = projectUserAdditionalPrivilegeServiceFactory({ - permissionService, - projectMembershipDAL, - projectUserAdditionalPrivilegeDAL, - accessApprovalRequestDAL + notificationService, + membershipDAL }); + const projectKeyService = projectKeyServiceFactory({ permissionService, projectKeyDAL, - projectMembershipDAL + membershipUserDAL }); const projectQueueService = projectQueueFactory({ @@ -1034,10 +1001,10 @@ export const registerRoutes = async ( secretVersionDAL, projectKeyDAL, projectBotDAL, - projectMembershipDAL, secretApprovalRequestDAL, secretApprovalSecretDAL: secretApprovalRequestSecretDAL, - projectUserMembershipRoleDAL + membershipRoleDAL, + membershipUserDAL }); const certificateAuthorityDAL = certificateAuthorityDALFactory(db); @@ -1249,14 +1216,15 @@ export const registerRoutes = async ( snapshotSecretV2BridgeDAL, secretApprovalRequestDAL, projectKeyDAL, - projectUserMembershipRoleDAL, orgService, resourceMetadataDAL, folderCommitService, secretSyncQueue, reminderService, eventBusService, - licenseService + licenseService, + membershipRoleDAL, + membershipUserDAL }); const projectService = projectServiceFactory({ @@ -1267,13 +1235,10 @@ export const registerRoutes = async ( secretV2BridgeDAL, projectQueue: projectQueueService, projectBotService, - identityProjectDAL, - identityOrgMembershipDAL, userDAL, projectEnvDAL, orgDAL, projectMembershipDAL, - projectRoleDAL, folderDAL, licenseService, pkiSubscriberDAL, @@ -1287,8 +1252,6 @@ export const registerRoutes = async ( sshCertificateTemplateDAL, sshHostDAL, sshHostGroupDAL, - projectUserMembershipRoleDAL, - identityProjectMembershipRoleDAL, keyStore, kmsService, certificateTemplateDAL, @@ -1297,7 +1260,6 @@ export const registerRoutes = async ( projectMicrosoftTeamsConfigDAL, microsoftTeamsIntegrationDAL, projectTemplateService, - groupProjectDAL, smtpService, reminderService, notificationService @@ -1314,16 +1276,6 @@ export const registerRoutes = async ( secretApprovalPolicyEnvironmentDAL: sapEnvironmentDAL }); - const projectRoleService = projectRoleServiceFactory({ - permissionService, - projectRoleDAL, - projectUserMembershipRoleDAL, - identityProjectMembershipRoleDAL, - projectDAL, - identityDAL, - userDAL - }); - const snapshotService = secretSnapshotServiceFactory({ permissionService, licenseService, @@ -1478,21 +1430,16 @@ export const registerRoutes = async ( groupDAL, permissionService, projectEnvDAL, - projectMembershipDAL, projectDAL, userDAL, accessApprovalRequestDAL, - additionalPrivilegeDAL: projectUserAdditionalPrivilegeDAL, - accessApprovalRequestReviewerDAL, - orgMembershipDAL + accessApprovalRequestReviewerDAL }); const accessApprovalRequestService = accessApprovalRequestServiceFactory({ projectDAL, permissionService, accessApprovalRequestReviewerDAL, - additionalPrivilegeDAL: projectUserAdditionalPrivilegeDAL, - projectMembershipDAL, accessApprovalPolicyDAL, accessApprovalRequestDAL, projectEnvDAL, @@ -1594,7 +1541,14 @@ export const registerRoutes = async ( licenseService, identityMetadataDAL, keyStore, - orgDAL + orgDAL, + membershipIdentityDAL, + membershipRoleDAL + }); + const identityProjectService = identityProjectServiceFactory({ + identityProjectDAL, + membershipIdentityDAL, + permissionService }); const identityAuthTemplateService = identityAuthTemplateServiceFactory({ @@ -1613,53 +1567,28 @@ export const registerRoutes = async ( identityDAL }); - const identityProjectService = identityProjectServiceFactory({ - permissionService, - projectDAL, - identityProjectDAL, - identityOrgMembershipDAL, - identityProjectMembershipRoleDAL, - projectRoleDAL, - orgDAL - }); - - const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({ - projectDAL, - identityProjectAdditionalPrivilegeDAL, - permissionService, - identityProjectDAL - }); - - const identityProjectAdditionalPrivilegeV2Service = identityProjectAdditionalPrivilegeV2ServiceFactory({ - projectDAL, - identityProjectAdditionalPrivilegeDAL, - permissionService, - identityProjectDAL - }); - const identityTokenAuthService = identityTokenAuthServiceFactory({ identityTokenAuthDAL, - identityOrgMembershipDAL, identityAccessTokenDAL, permissionService, licenseService, - orgDAL + orgDAL, + membershipIdentityDAL }); const identityUaService = identityUaServiceFactory({ - identityOrgMembershipDAL, permissionService, identityAccessTokenDAL, identityUaClientSecretDAL, identityUaDAL, licenseService, keyStore, - orgDAL + orgDAL, + membershipIdentityDAL }); const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({ identityKubernetesAuthDAL, - identityOrgMembershipDAL, identityAccessTokenDAL, permissionService, licenseService, @@ -1668,61 +1597,62 @@ export const registerRoutes = async ( gatewayV2Service, gatewayV2DAL, gatewayDAL, - kmsService + kmsService, + membershipIdentityDAL }); const identityGcpAuthService = identityGcpAuthServiceFactory({ identityGcpAuthDAL, orgDAL, - identityOrgMembershipDAL, identityAccessTokenDAL, permissionService, - licenseService + licenseService, + membershipIdentityDAL }); const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({ identityAccessTokenDAL, orgDAL, identityAliCloudAuthDAL, - identityOrgMembershipDAL, licenseService, - permissionService + permissionService, + membershipIdentityDAL }); const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({ identityAccessTokenDAL, orgDAL, identityTlsCertAuthDAL, - identityOrgMembershipDAL, licenseService, permissionService, - kmsService + kmsService, + membershipIdentityDAL }); const identityAwsAuthService = identityAwsAuthServiceFactory({ identityAccessTokenDAL, orgDAL, identityAwsAuthDAL, - identityOrgMembershipDAL, licenseService, - permissionService + permissionService, + membershipIdentityDAL }); const identityAzureAuthService = identityAzureAuthServiceFactory({ identityAzureAuthDAL, orgDAL, - identityOrgMembershipDAL, identityAccessTokenDAL, permissionService, - licenseService + licenseService, + membershipIdentityDAL }); const identityOciAuthService = identityOciAuthServiceFactory({ identityAccessTokenDAL, orgDAL, identityOciAuthDAL, - identityOrgMembershipDAL, licenseService, - permissionService + permissionService, + membershipIdentityDAL }); const pitService = pitServiceFactory({ @@ -1742,11 +1672,11 @@ export const registerRoutes = async ( const identityOidcAuthService = identityOidcAuthServiceFactory({ identityOidcAuthDAL, orgDAL, - identityOrgMembershipDAL, identityAccessTokenDAL, permissionService, licenseService, - kmsService + kmsService, + membershipIdentityDAL }); const identityJwtAuthService = identityJwtAuthServiceFactory({ @@ -1754,9 +1684,9 @@ export const registerRoutes = async ( orgDAL, permissionService, identityAccessTokenDAL, - identityOrgMembershipDAL, licenseService, - kmsService + kmsService, + membershipIdentityDAL }); const identityLdapAuthService = identityLdapAuthServiceFactory({ @@ -1765,11 +1695,17 @@ export const registerRoutes = async ( permissionService, kmsService, identityAccessTokenDAL, - identityOrgMembershipDAL, licenseService, identityDAL, identityAuthTemplateDAL, - keyStore + keyStore, + membershipIdentityDAL + }); + + const convertorService = convertorServiceFactory({ + additionalPrivilegeDAL, + membershipDAL, + projectDAL }); const dynamicSecretProviders = buildDynamicSecretProviders({ @@ -1844,7 +1780,6 @@ export const registerRoutes = async ( const oidcService = oidcConfigServiceFactory({ orgDAL, - orgMembershipDAL, userDAL, userAliasDAL, licenseService, @@ -1857,9 +1792,10 @@ export const registerRoutes = async ( projectKeyDAL, projectDAL, userGroupMembershipDAL, - groupProjectDAL, groupDAL, - auditLogService + auditLogService, + membershipGroupDAL, + membershipRoleDAL }); const userEngagementService = userEngagementServiceFactory({ @@ -1915,8 +1851,8 @@ export const registerRoutes = async ( const externalGroupOrgRoleMappingService = externalGroupOrgRoleMappingServiceFactory({ permissionService, licenseService, - orgRoleDAL, - externalGroupOrgRoleMappingDAL + externalGroupOrgRoleMappingDAL, + roleDAL }); const appConnectionService = appConnectionServiceFactory({ @@ -2262,7 +2198,6 @@ export const registerRoutes = async ( groupProject: groupProjectService, permission: permissionService, org: orgService, - orgRole: orgRoleService, oidc: oidcService, apiKey: apiKeyService, authToken: tokenService, @@ -2272,7 +2207,6 @@ export const registerRoutes = async ( projectMembership: projectMembershipService, projectKey: projectKeyService, projectEnv: projectEnvService, - projectRole: projectRoleService, secret: secretService, secretReplication: secretReplicationService, secretTag: secretTagService, @@ -2287,7 +2221,6 @@ export const registerRoutes = async ( identity: identityService, identityAuthTemplate: identityAuthTemplateService, identityAccessToken: identityAccessTokenService, - identityProject: identityProjectService, identityTokenAuth: identityTokenAuthService, identityUa: identityUaService, identityKubernetesAuth: identityKubernetesAuthService, @@ -2334,9 +2267,6 @@ export const registerRoutes = async ( scim: scimService, secretBlindIndex: secretBlindIndexService, telemetry: telemetryService, - projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService, - identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService, - identityProjectAdditionalPrivilegeV2: identityProjectAdditionalPrivilegeV2Service, secretSharing: secretSharingService, userEngagement: userEngagementService, externalKms: externalKmsService, @@ -2376,7 +2306,9 @@ export const registerRoutes = async ( membershipIdentity: membershipIdentityService, membershipGroup: membershipGroupService, role: roleService, - additionalPrivilege: additionalPrivilegeService + additionalPrivilege: additionalPrivilegeService, + identityProject: identityProjectService, + convertor: convertorService }); const cronJobs: CronJob[] = []; diff --git a/backend/src/server/routes/v1/identity-alicloud-auth-router.ts b/backend/src/server/routes/v1/identity-alicloud-auth-router.ts index a9b2d9b03..4c444d704 100644 --- a/backend/src/server/routes/v1/identity-alicloud-auth-router.ts +++ b/backend/src/server/routes/v1/identity-alicloud-auth-router.ts @@ -78,7 +78,7 @@ export const registerIdentityAliCloudAuthRouter = async (server: FastifyZodProvi await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-aws-iam-auth-router.ts b/backend/src/server/routes/v1/identity-aws-iam-auth-router.ts index effd66a68..fe7cf7d5b 100644 --- a/backend/src/server/routes/v1/identity-aws-iam-auth-router.ts +++ b/backend/src/server/routes/v1/identity-aws-iam-auth-router.ts @@ -45,7 +45,7 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider) await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_AWS_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-azure-auth-router.ts b/backend/src/server/routes/v1/identity-azure-auth-router.ts index 9053bc2e3..9ef733a25 100644 --- a/backend/src/server/routes/v1/identity-azure-auth-router.ts +++ b/backend/src/server/routes/v1/identity-azure-auth-router.ts @@ -40,7 +40,7 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_AZURE_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-gcp-auth-router.ts b/backend/src/server/routes/v1/identity-gcp-auth-router.ts index b83faf9d9..91e8038a7 100644 --- a/backend/src/server/routes/v1/identity-gcp-auth-router.ts +++ b/backend/src/server/routes/v1/identity-gcp-auth-router.ts @@ -40,7 +40,7 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider) await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_GCP_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-jwt-auth-router.ts b/backend/src/server/routes/v1/identity-jwt-auth-router.ts index 373a8b927..ecffaebe1 100644 --- a/backend/src/server/routes/v1/identity-jwt-auth-router.ts +++ b/backend/src/server/routes/v1/identity-jwt-auth-router.ts @@ -119,7 +119,7 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider) await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_JWT_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts b/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts index 879310790..6f0500c29 100644 --- a/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts +++ b/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts @@ -64,7 +64,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-ldap-auth-router.ts b/backend/src/server/routes/v1/identity-ldap-auth-router.ts index 512f253e0..ac384d216 100644 --- a/backend/src/server/routes/v1/identity-ldap-auth-router.ts +++ b/backend/src/server/routes/v1/identity-ldap-auth-router.ts @@ -168,7 +168,7 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider) await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_LDAP_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-oci-auth-router.ts b/backend/src/server/routes/v1/identity-oci-auth-router.ts index e529f300b..df19492a6 100644 --- a/backend/src/server/routes/v1/identity-oci-auth-router.ts +++ b/backend/src/server/routes/v1/identity-oci-auth-router.ts @@ -57,7 +57,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_OCI_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-oidc-auth-router.ts b/backend/src/server/routes/v1/identity-oidc-auth-router.ts index 74cd94eb5..147105aba 100644 --- a/backend/src/server/routes/v1/identity-oidc-auth-router.ts +++ b/backend/src/server/routes/v1/identity-oidc-auth-router.ts @@ -67,7 +67,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider) await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_OIDC_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-tls-cert-auth-router.ts b/backend/src/server/routes/v1/identity-tls-cert-auth-router.ts index 0bb9e08ea..0503ed16f 100644 --- a/backend/src/server/routes/v1/identity-tls-cert-auth-router.ts +++ b/backend/src/server/routes/v1/identity-tls-cert-auth-router.ts @@ -72,7 +72,7 @@ export const registerIdentityTlsCertAuthRouter = async (server: FastifyZodProvid await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_TLS_CERT_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-token-auth-router.ts b/backend/src/server/routes/v1/identity-token-auth-router.ts index e22c41889..9a33d0651 100644 --- a/backend/src/server/routes/v1/identity-token-auth-router.ts +++ b/backend/src/server/routes/v1/identity-token-auth-router.ts @@ -332,7 +332,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.CREATE_TOKEN_IDENTITY_TOKEN_AUTH, metadata: { @@ -393,7 +393,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.GET_TOKENS_IDENTITY_TOKEN_AUTH, metadata: { @@ -447,7 +447,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.UPDATE_TOKEN_IDENTITY_TOKEN_AUTH, metadata: { diff --git a/backend/src/server/routes/v1/identity-universal-auth-router.ts b/backend/src/server/routes/v1/identity-universal-auth-router.ts index 6d911a88e..153e1e641 100644 --- a/backend/src/server/routes/v1/identity-universal-auth-router.ts +++ b/backend/src/server/routes/v1/identity-universal-auth-router.ts @@ -59,7 +59,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - orgId: identityMembershipOrg?.orgId, + orgId: identityMembershipOrg.scopeOrgId, event: { type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH, metadata: { diff --git a/backend/src/services/identity-access-token/identity-access-token-service.ts b/backend/src/services/identity-access-token/identity-access-token-service.ts index 71f7ec365..1b230f7a5 100644 --- a/backend/src/services/identity-access-token/identity-access-token-service.ts +++ b/backend/src/services/identity-access-token/identity-access-token-service.ts @@ -204,7 +204,7 @@ export const identityAccessTokenServiceFactory = ({ } const identityOrgMembership = await identityOrgMembershipDAL.findOne({ - identityId: identityAccessToken.identityId + actorIdentityId: identityAccessToken.identityId }); if (!identityOrgMembership) { @@ -219,7 +219,7 @@ export const identityAccessTokenServiceFactory = ({ await validateAccessTokenExp({ ...identityAccessToken, accessTokenNumUses }); await accessTokenQueue.updateIdentityAccessTokenStatus(identityAccessToken.id, Number(accessTokenNumUses) + 1); - return { ...identityAccessToken, orgId: identityOrgMembership.orgId }; + return { ...identityAccessToken, orgId: identityOrgMembership.scopeOrgId }; }; return { renewAccessToken, revokeAccessToken, fnValidateIdentityAccessToken }; diff --git a/backend/src/services/identity-alicloud-auth/identity-alicloud-auth-service.ts b/backend/src/services/identity-alicloud-auth/identity-alicloud-auth-service.ts index fb3f597c2..d78616662 100644 --- a/backend/src/services/identity-alicloud-auth/identity-alicloud-auth-service.ts +++ b/backend/src/services/identity-alicloud-auth/identity-alicloud-auth-service.ts @@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { AxiosError } from "axios"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { @@ -18,7 +18,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { logger } from "@app/lib/logger"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TOrgDALFactory } from "../org/org-dal"; @@ -39,7 +39,7 @@ type TIdentityAliCloudAuthServiceFactoryDep = { TIdentityAliCloudAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete" >; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; licenseService: Pick; permissionService: Pick; orgDAL: Pick; @@ -50,7 +50,7 @@ export type TIdentityAliCloudAuthServiceFactory = ReturnType { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.ALICLOUD_AUTH, @@ -153,7 +154,13 @@ export const identityAliCloudAuthServiceFactory = ({ }: TAttachAliCloudAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) { @@ -169,13 +176,13 @@ export const identityAliCloudAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -196,7 +203,7 @@ export const identityAliCloudAuthServiceFactory = ({ const identityAliCloudAuth = await identityAliCloudAuthDAL.transaction(async (tx) => { const doc = await identityAliCloudAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, type: "iam", allowedArns, accessTokenMaxTTL, @@ -208,7 +215,7 @@ export const identityAliCloudAuthServiceFactory = ({ ); return doc; }); - return { ...identityAliCloudAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityAliCloudAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateAliCloudAuth = async ({ @@ -223,7 +230,13 @@ export const identityAliCloudAuthServiceFactory = ({ actor, actorOrgId }: TUpdateAliCloudAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) { @@ -245,13 +258,13 @@ export const identityAliCloudAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -279,11 +292,17 @@ export const identityAliCloudAuthServiceFactory = ({ : undefined }); - return { ...updatedAliCloudAuth, orgId: identityMembershipOrg.orgId }; + return { ...updatedAliCloudAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const getAliCloudAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAliCloudAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) { @@ -297,12 +316,12 @@ export const identityAliCloudAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...alicloudIdentityAuth, orgId: identityMembershipOrg.orgId }; + return { ...alicloudIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityAliCloudAuth = async ({ @@ -312,7 +331,13 @@ export const identityAliCloudAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeAliCloudAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.ALICLOUD_AUTH)) { throw new BadRequestError({ @@ -322,7 +347,7 @@ export const identityAliCloudAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -330,13 +355,13 @@ export const identityAliCloudAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -360,7 +385,7 @@ export const identityAliCloudAuthServiceFactory = ({ const deletedAliCloudAuth = await identityAliCloudAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.ALICLOUD_AUTH }, tx); - return { ...deletedAliCloudAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedAliCloudAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityAliCloudAuth; }; diff --git a/backend/src/services/identity-aws-auth/identity-aws-auth-service.ts b/backend/src/services/identity-aws-auth/identity-aws-auth-service.ts index 53443adbe..20cb178c5 100644 --- a/backend/src/services/identity-aws-auth/identity-aws-auth-service.ts +++ b/backend/src/services/identity-aws-auth/identity-aws-auth-service.ts @@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability"; import axios from "axios"; import RE2 from "re2"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -18,7 +18,7 @@ import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedEr import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; @@ -37,7 +37,7 @@ import { type TIdentityAwsAuthServiceFactoryDep = { identityAccessTokenDAL: Pick; identityAwsAuthDAL: Pick; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; licenseService: Pick; permissionService: Pick; orgDAL: Pick; @@ -82,7 +82,7 @@ function isValidAwsRegion(region: string | null): boolean { export const identityAwsAuthServiceFactory = ({ identityAccessTokenDAL, identityAwsAuthDAL, - identityOrgMembershipDAL, + membershipIdentityDAL, licenseService, permissionService, orgDAL @@ -93,7 +93,10 @@ export const identityAwsAuthServiceFactory = ({ throw new NotFoundError({ message: "AWS auth method not found for identity, did you configure AWS auth?" }); } - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityAwsAuth.identityId }); + const identityMembershipOrg = await membershipIdentityDAL.findOne({ + actorIdentityId: identityAwsAuth.identityId, + scope: AccessScope.Organization + }); if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" }); const headers: TAwsGetCallerIdentityHeaders = JSON.parse(Buffer.from(iamRequestHeaders, "base64").toString()); @@ -156,7 +159,7 @@ export const identityAwsAuthServiceFactory = ({ } const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.AWS_AUTH, @@ -229,7 +232,13 @@ export const identityAwsAuthServiceFactory = ({ }: TAttachAwsAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) { @@ -245,13 +254,13 @@ export const identityAwsAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -272,7 +281,7 @@ export const identityAwsAuthServiceFactory = ({ const identityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => { const doc = await identityAwsAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, type: "iam", stsEndpoint, allowedPrincipalArns, @@ -286,7 +295,7 @@ export const identityAwsAuthServiceFactory = ({ ); return doc; }); - return { ...identityAwsAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityAwsAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateAwsAuth = async ({ @@ -303,7 +312,13 @@ export const identityAwsAuthServiceFactory = ({ actor, actorOrgId }: TUpdateAwsAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) { @@ -324,13 +339,13 @@ export const identityAwsAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -360,11 +375,17 @@ export const identityAwsAuthServiceFactory = ({ : undefined }); - return { ...updatedAwsAuth, orgId: identityMembershipOrg.orgId }; + return { ...updatedAwsAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const getAwsAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAwsAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) { @@ -378,12 +399,12 @@ export const identityAwsAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...awsIdentityAuth, orgId: identityMembershipOrg.orgId }; + return { ...awsIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityAwsAuth = async ({ @@ -393,7 +414,13 @@ export const identityAwsAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeAwsAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AWS_AUTH)) { throw new BadRequestError({ @@ -403,7 +430,7 @@ export const identityAwsAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -411,13 +438,13 @@ export const identityAwsAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -441,7 +468,7 @@ export const identityAwsAuthServiceFactory = ({ const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.AWS_AUTH }, tx); - return { ...deletedAwsAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedAwsAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityAwsAuth; }; diff --git a/backend/src/services/identity-azure-auth/identity-azure-auth-service.ts b/backend/src/services/identity-azure-auth/identity-azure-auth-service.ts index 0ca27c041..485cc26da 100644 --- a/backend/src/services/identity-azure-auth/identity-azure-auth-service.ts +++ b/backend/src/services/identity-azure-auth/identity-azure-auth-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -15,7 +15,7 @@ import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedEr import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; @@ -34,7 +34,7 @@ type TIdentityAzureAuthServiceFactoryDep = { TIdentityAzureAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete" >; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; identityAccessTokenDAL: Pick; permissionService: Pick; licenseService: Pick; @@ -45,7 +45,7 @@ export type TIdentityAzureAuthServiceFactory = ReturnType { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.AZURE_AUTH, @@ -142,7 +145,13 @@ export const identityAzureAuthServiceFactory = ({ }: TAttachAzureAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) { @@ -157,13 +166,13 @@ export const identityAzureAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -184,7 +193,7 @@ export const identityAzureAuthServiceFactory = ({ const identityAzureAuth = await identityAzureAuthDAL.transaction(async (tx) => { const doc = await identityAzureAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, tenantId, resource, allowedServicePrincipalIds, @@ -198,7 +207,7 @@ export const identityAzureAuthServiceFactory = ({ return doc; }); - return { ...identityAzureAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityAzureAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateAzureAuth = async ({ @@ -215,7 +224,13 @@ export const identityAzureAuthServiceFactory = ({ actor, actorOrgId }: TUpdateAzureAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) { throw new BadRequestError({ @@ -235,13 +250,13 @@ export const identityAzureAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -273,12 +288,18 @@ export const identityAzureAuthServiceFactory = ({ return { ...updatedAzureAuth, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }; }; const getAzureAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAzureAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) { throw new BadRequestError({ @@ -291,13 +312,13 @@ export const identityAzureAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...identityAzureAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityAzureAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityAzureAuth = async ({ @@ -307,7 +328,13 @@ export const identityAzureAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeAzureAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.AZURE_AUTH)) { throw new BadRequestError({ @@ -317,7 +344,7 @@ export const identityAzureAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -325,12 +352,12 @@ export const identityAzureAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -353,7 +380,7 @@ export const identityAzureAuthServiceFactory = ({ const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.AZURE_AUTH }, tx); - return { ...deletedAzureAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedAzureAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityAzureAuth; }; diff --git a/backend/src/services/identity-gcp-auth/identity-gcp-auth-service.ts b/backend/src/services/identity-gcp-auth/identity-gcp-auth-service.ts index fccffdc89..bf0f1c069 100644 --- a/backend/src/services/identity-gcp-auth/identity-gcp-auth-service.ts +++ b/backend/src/services/identity-gcp-auth/identity-gcp-auth-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -15,7 +15,7 @@ import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedEr import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; @@ -32,7 +32,7 @@ import { type TIdentityGcpAuthServiceFactoryDep = { identityGcpAuthDAL: Pick; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; identityAccessTokenDAL: Pick; permissionService: Pick; licenseService: Pick; @@ -43,7 +43,7 @@ export type TIdentityGcpAuthServiceFactory = ReturnType { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.GCP_AUTH, @@ -182,7 +185,13 @@ export const identityGcpAuthServiceFactory = ({ }: TAttachGcpAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) { @@ -198,13 +207,13 @@ export const identityGcpAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -225,7 +234,7 @@ export const identityGcpAuthServiceFactory = ({ const identityGcpAuth = await identityGcpAuthDAL.transaction(async (tx) => { const doc = await identityGcpAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, type, allowedServiceAccounts, allowedProjects, @@ -239,7 +248,7 @@ export const identityGcpAuthServiceFactory = ({ ); return doc; }); - return { ...identityGcpAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityGcpAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateGcpAuth = async ({ @@ -257,7 +266,13 @@ export const identityGcpAuthServiceFactory = ({ actor, actorOrgId }: TUpdateGcpAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) { @@ -278,13 +293,13 @@ export const identityGcpAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -317,12 +332,18 @@ export const identityGcpAuthServiceFactory = ({ return { ...updatedGcpAuth, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }; }; const getGcpAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetGcpAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) { @@ -336,13 +357,13 @@ export const identityGcpAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...identityGcpAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityGcpAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityGcpAuth = async ({ @@ -352,7 +373,13 @@ export const identityGcpAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeGcpAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.GCP_AUTH)) { @@ -363,7 +390,7 @@ export const identityGcpAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -371,12 +398,12 @@ export const identityGcpAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -399,7 +426,7 @@ export const identityGcpAuthServiceFactory = ({ const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.GCP_AUTH }, tx); - return { ...deletedGcpAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedGcpAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityGcpAuth; }; diff --git a/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts b/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts index 5d5af5841..38d59cd5a 100644 --- a/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts +++ b/backend/src/services/identity-jwt-auth/identity-jwt-auth-service.ts @@ -3,7 +3,7 @@ import https from "https"; import jwt from "jsonwebtoken"; import { JwksClient } from "jwks-rsa"; -import { IdentityAuthMethod, TIdentityJwtAuthsUpdate } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod, TIdentityJwtAuthsUpdate } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -25,7 +25,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { getValueByDot } from "@app/lib/template/dot-access"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TKmsServiceFactory } from "../kms/kms-service"; @@ -44,7 +44,7 @@ import { type TIdentityJwtAuthServiceFactoryDep = { identityJwtAuthDAL: TIdentityJwtAuthDALFactory; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; identityAccessTokenDAL: Pick; permissionService: Pick; licenseService: Pick; @@ -56,7 +56,7 @@ export type TIdentityJwtAuthServiceFactory = ReturnType { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH, @@ -275,10 +276,14 @@ export const identityJwtAuthServiceFactory = ({ }: TAttachJwtAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); - if (!identityMembershipOrg) { - if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); - } + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); + if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) { throw new BadRequestError({ message: "Failed to add JWT Auth to already configured identity" @@ -292,14 +297,14 @@ export const identityJwtAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -333,7 +338,7 @@ export const identityJwtAuthServiceFactory = ({ const identityJwtAuth = await identityJwtAuthDAL.transaction(async (tx) => { const doc = await identityJwtAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, configurationType, jwksUrl, encryptedJwksCaCert, @@ -352,7 +357,7 @@ export const identityJwtAuthServiceFactory = ({ return doc; }); - return { ...identityJwtAuth, orgId: identityMembershipOrg.orgId, jwksCaCert, publicKeys }; + return { ...identityJwtAuth, orgId: identityMembershipOrg.scopeOrgId, jwksCaCert, publicKeys }; }; const updateJwtAuth = async ({ @@ -374,7 +379,13 @@ export const identityJwtAuthServiceFactory = ({ actor, actorOrgId }: TUpdateJwtAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) { @@ -395,14 +406,14 @@ export const identityJwtAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -465,14 +476,20 @@ export const identityJwtAuthServiceFactory = ({ return { ...updatedJwtAuth, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, jwksCaCert: decryptedJwksCaCert, publicKeys: decryptedPublicKeys }; }; const getJwtAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetJwtAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) { @@ -484,7 +501,7 @@ export const identityJwtAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -505,14 +522,20 @@ export const identityJwtAuthServiceFactory = ({ return { ...identityJwtAuth, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, jwksCaCert: decryptedJwksCaCert, publicKeys: decryptedPublicKeys }; }; const revokeJwtAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TRevokeJwtAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) { throw new NotFoundError({ message: "Failed to find identity" }); } @@ -526,7 +549,7 @@ export const identityJwtAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -535,13 +558,13 @@ export const identityJwtAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -564,7 +587,7 @@ export const identityJwtAuthServiceFactory = ({ const deletedJwtAuth = await identityJwtAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.JWT_AUTH }, tx); - return { ...deletedJwtAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedJwtAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityJwtAuth; diff --git a/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts b/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts index 021312fb1..c61d46677 100644 --- a/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts +++ b/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts @@ -3,7 +3,7 @@ import axios, { AxiosError } from "axios"; import https from "https"; import RE2 from "re2"; -import { IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas"; import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal"; import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service"; import { TGatewayV2DALFactory } from "@app/ee/services/gateway-v2/gateway-v2-dal"; @@ -29,7 +29,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { logger } from "@app/lib/logger"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TKmsServiceFactory } from "../kms/kms-service"; @@ -53,7 +53,7 @@ type TIdentityKubernetesAuthServiceFactoryDep = { "create" | "findOne" | "transaction" | "updateById" | "delete" >; identityAccessTokenDAL: Pick; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; permissionService: Pick; licenseService: Pick; kmsService: Pick; @@ -70,7 +70,7 @@ const GATEWAY_AUTH_DEFAULT_HOST = "https://kubernetes.default.svc.cluster.local" export const identityKubernetesAuthServiceFactory = ({ identityKubernetesAuthDAL, - identityOrgMembershipDAL, + membershipIdentityDAL, identityAccessTokenDAL, permissionService, licenseService, @@ -175,8 +175,9 @@ export const identityKubernetesAuthServiceFactory = ({ }); } - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ - identityId: identityKubernetesAuth.identityId + const identityMembershipOrg = await membershipIdentityDAL.findOne({ + actorIdentityId: identityKubernetesAuth.identityId, + scope: AccessScope.Organization }); if (!identityMembershipOrg) { throw new NotFoundError({ @@ -186,7 +187,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); let caCert = ""; @@ -429,7 +430,7 @@ export const identityKubernetesAuthServiceFactory = ({ } const identityAccessToken = await identityKubernetesAuthDAL.transaction(async (tx) => { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH, @@ -499,7 +500,13 @@ export const identityKubernetesAuthServiceFactory = ({ }: TAttachKubernetesAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) { @@ -515,13 +522,13 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -541,8 +548,8 @@ export const identityKubernetesAuthServiceFactory = ({ let isGatewayV1 = true; if (gatewayId) { - const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId }); - const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId }); + const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.scopeOrgId }); + const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: identityMembershipOrg.scopeOrgId }); if (!gateway && !gatewayV2) { throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found` @@ -556,7 +563,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission: orgPermission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -568,13 +575,13 @@ export const identityKubernetesAuthServiceFactory = ({ const { encryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const identityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => { const doc = await identityKubernetesAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, kubernetesHost, tokenReviewMode, allowedNamespaces, @@ -596,7 +603,7 @@ export const identityKubernetesAuthServiceFactory = ({ return doc; }); - return { ...identityKubernetesAuth, caCert, tokenReviewerJwt, orgId: identityMembershipOrg.orgId }; + return { ...identityKubernetesAuth, caCert, tokenReviewerJwt, orgId: identityMembershipOrg.scopeOrgId }; }; const updateKubernetesAuth = async ({ @@ -618,7 +625,13 @@ export const identityKubernetesAuthServiceFactory = ({ actor, actorOrgId }: TUpdateKubernetesAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) { @@ -640,13 +653,13 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -666,8 +679,8 @@ export const identityKubernetesAuthServiceFactory = ({ let isGatewayV1 = true; if (gatewayId) { - const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId }); - const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId }); + const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.scopeOrgId }); + const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: identityMembershipOrg.scopeOrgId }); if (!gateway && !gatewayV2) { throw new NotFoundError({ @@ -682,7 +695,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission: orgPermission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -714,7 +727,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); if (caCert !== undefined) { @@ -745,7 +758,7 @@ export const identityKubernetesAuthServiceFactory = ({ return { ...updatedKubernetesAuth, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, caCert: updatedCACert, tokenReviewerJwt: updatedTokenReviewerJwt }; @@ -758,7 +771,13 @@ export const identityKubernetesAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TGetKubernetesAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId }); @@ -775,7 +794,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -783,7 +802,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); let caCert = ""; @@ -802,7 +821,7 @@ export const identityKubernetesAuthServiceFactory = ({ ...identityKubernetesAuth, caCert, tokenReviewerJwt, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, gatewayId: identityKubernetesAuth.gatewayId ?? identityKubernetesAuth.gatewayV2Id }; }; @@ -814,7 +833,13 @@ export const identityKubernetesAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeKubernetesAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) { @@ -825,7 +850,7 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -833,12 +858,12 @@ export const identityKubernetesAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -860,7 +885,7 @@ export const identityKubernetesAuthServiceFactory = ({ const revokedIdentityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => { const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.KUBERNETES_AUTH }, tx); - return { ...deletedKubernetesAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedKubernetesAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityKubernetesAuth; }; diff --git a/backend/src/services/identity-ldap-auth/identity-ldap-auth-service.ts b/backend/src/services/identity-ldap-auth/identity-ldap-auth-service.ts index 6f672e167..a545ca930 100644 --- a/backend/src/services/identity-ldap-auth/identity-ldap-auth-service.ts +++ b/backend/src/services/identity-ldap-auth/identity-ldap-auth-service.ts @@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import slugify from "@sindresorhus/slugify"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TIdentityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template"; import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; @@ -32,7 +32,7 @@ import { logger } from "@app/lib/logger"; import { ActorType, AuthTokenType } from "../auth/auth-type"; import { TIdentityDALFactory } from "../identity/identity-dal"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TKmsServiceFactory } from "../kms/kms-service"; @@ -56,7 +56,7 @@ type TIdentityLdapAuthServiceFactoryDep = { TIdentityLdapAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete" >; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; licenseService: Pick; permissionService: Pick; kmsService: TKmsServiceFactory; @@ -80,7 +80,7 @@ export const identityLdapAuthServiceFactory = ({ identityAccessTokenDAL, identityDAL, identityLdapAuthDAL, - identityOrgMembershipDAL, + membershipIdentityDAL, licenseService, permissionService, kmsService, @@ -92,7 +92,10 @@ export const identityLdapAuthServiceFactory = ({ const identity = await identityDAL.findOne({ id: identityId }); if (!identity) throw new NotFoundError({ message: `Identity with ID '${identityId}' not found` }); - const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: identity.id }); + const identityOrgMembership = await membershipIdentityDAL.findOne({ + actorIdentityId: identity.id, + scope: AccessScope.Organization + }); if (!identityOrgMembership) throw new NotFoundError({ message: `Identity with ID '${identityId}' not found` }); const ldapAuth = await identityLdapAuthDAL.findOne({ identityId: identity.id }); @@ -104,7 +107,7 @@ export const identityLdapAuthServiceFactory = ({ const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityOrgMembership.orgId + orgId: identityOrgMembership.scopeOrgId }); const bindDN = decryptor({ cipherTextBlob: ldapAuth.encryptedBindDN }).toString(); @@ -115,7 +118,7 @@ export const identityLdapAuthServiceFactory = ({ const ldapConfig = { id: ldapAuth.id, - organization: identityOrgMembership.orgId, + organization: identityOrgMembership.scopeOrgId, url: ldapAuth.url, bindDN, bindPass, @@ -147,7 +150,10 @@ export const identityLdapAuthServiceFactory = ({ }; const login = async ({ identityId }: TLoginLdapAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.findOne({ + actorIdentityId: identityId, + scope: AccessScope.Organization + }); if (!identityMembershipOrg) { throw new UnauthorizedError({ @@ -163,7 +169,7 @@ export const identityLdapAuthServiceFactory = ({ }); } - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); if (!plan.ldap) { throw new BadRequestError({ message: @@ -172,7 +178,7 @@ export const identityLdapAuthServiceFactory = ({ } const identityAccessToken = await identityLdapAuthDAL.transaction(async (tx) => { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH, @@ -240,7 +246,13 @@ export const identityLdapAuthServiceFactory = ({ }: TAttachLdapAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) { @@ -256,7 +268,7 @@ export const identityLdapAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -269,7 +281,7 @@ export const identityLdapAuthServiceFactory = ({ ); } - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); if (!plan.ldap) { throw new BadRequestError({ @@ -299,11 +311,11 @@ export const identityLdapAuthServiceFactory = ({ const identityLdapAuth = await identityLdapAuthDAL.transaction(async (tx) => { const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const template = templateId - ? await identityAuthTemplateDAL.findByIdAndOrgId(templateId, identityMembershipOrg.orgId) + ? await identityAuthTemplateDAL.findByIdAndOrgId(templateId, identityMembershipOrg.scopeOrgId) : undefined; let ldapConfig: { bindDN: string; bindPass: string; searchBase: string; url: string; ldapCaCertificate?: string }; @@ -357,7 +369,7 @@ export const identityLdapAuthServiceFactory = ({ const doc = await identityLdapAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, encryptedBindDN, encryptedBindPass, searchBase: ldapConfig.searchBase, @@ -379,7 +391,7 @@ export const identityLdapAuthServiceFactory = ({ ); return doc; }); - return { ...identityLdapAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityLdapAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateLdapAuth = async ({ @@ -405,7 +417,13 @@ export const identityLdapAuthServiceFactory = ({ lockoutDurationSeconds, lockoutCounterResetSeconds }: TUpdateLdapAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) { @@ -426,7 +444,7 @@ export const identityLdapAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -439,7 +457,7 @@ export const identityLdapAuthServiceFactory = ({ ); } - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); if (!plan.ldap) { throw new BadRequestError({ @@ -468,11 +486,11 @@ export const identityLdapAuthServiceFactory = ({ const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const template = templateId - ? await identityAuthTemplateDAL.findByIdAndOrgId(templateId, identityMembershipOrg.orgId) + ? await identityAuthTemplateDAL.findByIdAndOrgId(templateId, identityMembershipOrg.scopeOrgId) : undefined; let config: { bindDN?: string; @@ -558,11 +576,17 @@ export const identityLdapAuthServiceFactory = ({ lockoutCounterResetSeconds }); - return { ...updatedLdapAuth, orgId: identityMembershipOrg.orgId }; + return { ...updatedLdapAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const getLdapAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetLdapAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) { @@ -576,14 +600,14 @@ export const identityLdapAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const bindDN = decryptor({ cipherTextBlob: ldapIdentityAuth.encryptedBindDN }).toString(); @@ -593,7 +617,7 @@ export const identityLdapAuthServiceFactory = ({ : undefined; ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...ldapIdentityAuth, orgId: identityMembershipOrg.orgId, bindDN, bindPass, ldapCaCertificate }; + return { ...ldapIdentityAuth, orgId: identityMembershipOrg.scopeOrgId, bindDN, bindPass, ldapCaCertificate }; }; const revokeIdentityLdapAuth = async ({ @@ -603,7 +627,13 @@ export const identityLdapAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeLdapAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) { throw new BadRequestError({ @@ -613,7 +643,7 @@ export const identityLdapAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -621,13 +651,13 @@ export const identityLdapAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -651,7 +681,7 @@ export const identityLdapAuthServiceFactory = ({ const [deletedLdapAuth] = await identityLdapAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.LDAP_AUTH }, tx); - return { ...deletedLdapAuth, orgId: identityMembershipOrg.orgId }; + return { ...deletedLdapAuth, orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityLdapAuth; }; @@ -740,7 +770,13 @@ export const identityLdapAuthServiceFactory = ({ actorOrgId, actorAuthMethod }: TClearLdapAuthLockoutsDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.LDAP_AUTH)) { @@ -752,7 +788,7 @@ export const identityLdapAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -762,7 +798,7 @@ export const identityLdapAuthServiceFactory = ({ pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:*` }); - return { deleted, identityId, orgId: identityMembershipOrg.orgId }; + return { deleted, identityId, orgId: identityMembershipOrg.scopeOrgId }; }; return { diff --git a/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts b/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts index 260734b55..dabd79195 100644 --- a/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts +++ b/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts @@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability"; import { AxiosError } from "axios"; import RE2 from "re2"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { @@ -19,7 +19,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { logger } from "@app/lib/logger"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; @@ -37,7 +37,7 @@ import { TOrgDALFactory } from "../org/org-dal"; type TIdentityOciAuthServiceFactoryDep = { identityAccessTokenDAL: Pick; identityOciAuthDAL: Pick; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; licenseService: Pick; permissionService: Pick; orgDAL: Pick; @@ -48,7 +48,7 @@ export type TIdentityOciAuthServiceFactory = ReturnType.oraclecloud.com" format. @@ -95,7 +98,7 @@ export const identityOciAuthServiceFactory = ({ // Generate the token const identityAccessToken = await identityOciAuthDAL.transaction(async (tx) => { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH, @@ -157,7 +160,13 @@ export const identityOciAuthServiceFactory = ({ }: TAttachOciAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) { @@ -173,13 +182,13 @@ export const identityOciAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -200,7 +209,7 @@ export const identityOciAuthServiceFactory = ({ const identityOciAuth = await identityOciAuthDAL.transaction(async (tx) => { const doc = await identityOciAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, type: "iam", tenancyOcid, allowedUsernames, @@ -213,7 +222,7 @@ export const identityOciAuthServiceFactory = ({ ); return doc; }); - return { ...identityOciAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityOciAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateOciAuth = async ({ @@ -229,7 +238,13 @@ export const identityOciAuthServiceFactory = ({ actor, actorOrgId }: TUpdateOciAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) { @@ -250,13 +265,13 @@ export const identityOciAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -285,11 +300,17 @@ export const identityOciAuthServiceFactory = ({ : undefined }); - return { ...updatedOciAuth, orgId: identityMembershipOrg.orgId }; + return { ...updatedOciAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const getOciAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetOciAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) { @@ -303,12 +324,12 @@ export const identityOciAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...ociIdentityAuth, orgId: identityMembershipOrg.orgId }; + return { ...ociIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityOciAuth = async ({ @@ -318,7 +339,13 @@ export const identityOciAuthServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeOciAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OCI_AUTH)) { throw new BadRequestError({ @@ -328,7 +355,7 @@ export const identityOciAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -336,8 +363,8 @@ export const identityOciAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -366,7 +393,7 @@ export const identityOciAuthServiceFactory = ({ const deletedOciAuth = await identityOciAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.OCI_AUTH }, tx); - return { ...deletedOciAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedOciAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityOciAuth; }; diff --git a/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts b/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts index d5ff33907..f88bfa94f 100644 --- a/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts +++ b/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts @@ -4,7 +4,7 @@ import https from "https"; import jwt from "jsonwebtoken"; import { JwksClient } from "jwks-rsa"; -import { IdentityAuthMethod, TIdentityOidcAuthsUpdate } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod, TIdentityOidcAuthsUpdate } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -26,7 +26,7 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { getValueByDot } from "@app/lib/template/dot-access"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TKmsServiceFactory } from "../kms/kms-service"; @@ -44,7 +44,7 @@ import { type TIdentityOidcAuthServiceFactoryDep = { identityOidcAuthDAL: TIdentityOidcAuthDALFactory; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; identityAccessTokenDAL: Pick; permissionService: Pick; licenseService: Pick; @@ -56,7 +56,7 @@ export type TIdentityOidcAuthServiceFactory = ReturnType { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH, @@ -248,7 +249,13 @@ export const identityOidcAuthServiceFactory = ({ isActorSuperAdmin }: TAttachOidcAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) { if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); } @@ -265,14 +272,14 @@ export const identityOidcAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -292,13 +299,13 @@ export const identityOidcAuthServiceFactory = ({ const { encryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const identityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => { const doc = await identityOidcAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, oidcDiscoveryUrl, encryptedCaCertificate: encryptor({ plainText: Buffer.from(caCert) }).cipherTextBlob, boundIssuer, @@ -315,7 +322,7 @@ export const identityOidcAuthServiceFactory = ({ ); return doc; }); - return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert }; + return { ...identityOidcAuth, orgId: identityMembershipOrg.scopeOrgId, caCert }; }; const updateOidcAuth = async ({ @@ -336,7 +343,13 @@ export const identityOidcAuthServiceFactory = ({ actor, actorOrgId }: TUpdateOidcAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) { @@ -357,14 +370,14 @@ export const identityOidcAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -399,7 +412,7 @@ export const identityOidcAuthServiceFactory = ({ const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); if (caCert !== undefined) { @@ -413,13 +426,19 @@ export const identityOidcAuthServiceFactory = ({ return { ...updatedOidcAuth, - orgId: identityMembershipOrg.orgId, + orgId: identityMembershipOrg.scopeOrgId, caCert: updatedCACert }; }; const getOidcAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetOidcAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.OIDC_AUTH)) { @@ -431,7 +450,7 @@ export const identityOidcAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -441,18 +460,24 @@ export const identityOidcAuthServiceFactory = ({ const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const caCert = identityOidcAuth.encryptedCaCertificate ? decryptor({ cipherTextBlob: identityOidcAuth.encryptedCaCertificate }).toString() : ""; - return { ...identityOidcAuth, orgId: identityMembershipOrg.orgId, caCert }; + return { ...identityOidcAuth, orgId: identityMembershipOrg.scopeOrgId, caCert }; }; const revokeOidcAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TRevokeOidcAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) { throw new NotFoundError({ message: "Failed to find identity" }); } @@ -466,7 +491,7 @@ export const identityOidcAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -475,13 +500,13 @@ export const identityOidcAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -505,7 +530,7 @@ export const identityOidcAuthServiceFactory = ({ const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.OIDC_AUTH }, tx); - return { ...deletedOidcAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedOidcAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityOidcAuth; diff --git a/backend/src/services/identity-project/identity-project-service.ts b/backend/src/services/identity-project/identity-project-service.ts index 99b5741e6..8cbed7d44 100644 --- a/backend/src/services/identity-project/identity-project-service.ts +++ b/backend/src/services/identity-project/identity-project-service.ts @@ -1,46 +1,22 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas"; -import { - constructPermissionErrorMessage, - validatePrivilegeChangeOperation -} from "@app/ee/services/permission/permission-fns"; +import { AccessScope, ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; -import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors"; -import { groupBy } from "@app/lib/fn"; -import { ms } from "@app/lib/ms"; +import { NotFoundError } from "@app/lib/errors"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; -import { TOrgDALFactory } from "../org/org-dal"; -import { TProjectDALFactory } from "../project/project-dal"; -import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types"; -import { TProjectRoleDALFactory } from "../project-role/project-role-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityProjectDALFactory } from "./identity-project-dal"; -import { TIdentityProjectMembershipRoleDALFactory } from "./identity-project-membership-role-dal"; import { - TCreateProjectIdentityDTO, - TDeleteProjectIdentityDTO, TGetProjectIdentityByIdentityIdDTO, TGetProjectIdentityByMembershipIdDTO, - TListProjectIdentityDTO, - TUpdateProjectIdentityDTO + TListProjectIdentityDTO } from "./identity-project-types"; type TIdentityProjectServiceFactoryDep = { identityProjectDAL: TIdentityProjectDALFactory; - orgDAL: Pick; - identityProjectMembershipRoleDAL: Pick< - TIdentityProjectMembershipRoleDALFactory, - "create" | "transaction" | "insertMany" | "delete" - >; - projectDAL: Pick; - projectRoleDAL: Pick; - identityOrgMembershipDAL: Pick; - permissionService: Pick< - TPermissionServiceFactory, - "getProjectPermission" | "getProjectPermissionByRoles" | "invalidateProjectPermissionCache" - >; + permissionService: Pick; + membershipIdentityDAL: TMembershipIdentityDALFactory; }; export type TIdentityProjectServiceFactory = ReturnType; @@ -48,275 +24,8 @@ export type TIdentityProjectServiceFactory = ReturnType { - const createProjectIdentity = async ({ - identityId, - actor, - actorId, - actorOrgId, - actorAuthMethod, - projectId, - roles - }: TCreateProjectIdentityDTO) => { - const { permission } = await permissionService.getProjectPermission({ - actor, - actorId, - projectId, - actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any - }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionIdentityActions.Create, - subject(ProjectPermissionSub.Identity, { - identityId - }) - ); - - const existingIdentity = await identityProjectDAL.findOne({ identityId, projectId }); - if (existingIdentity) - throw new BadRequestError({ - message: `Identity with ID ${identityId} already exists in project with ID ${projectId}` - }); - - const project = await projectDAL.findById(projectId); - const identityOrgMembership = await identityOrgMembershipDAL.findOne({ - identityId, - orgId: project.orgId - }); - if (!identityOrgMembership) - throw new NotFoundError({ - message: `Failed to find identity with ID ${identityId}` - }); - - const providedRolePermissionDetails = await permissionService.getProjectPermissionByRoles( - roles.map((el) => el.role).filter((el) => el !== ProjectMembershipRole.NoAccess), - projectId - ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId); - for await (const { permission: rolePermission } of providedRolePermissionDetails) { - const permissionBoundary = validatePrivilegeChangeOperation( - shouldUseNewPrivilegeSystem, - ProjectPermissionIdentityActions.GrantPrivileges, - ProjectPermissionSub.Identity, - permission, - rolePermission - ); - if (!permissionBoundary.isValid) - throw new PermissionBoundaryError({ - message: constructPermissionErrorMessage( - "Failed to assign to role", - shouldUseNewPrivilegeSystem, - ProjectPermissionIdentityActions.GrantPrivileges, - ProjectPermissionSub.Identity - ), - details: { missingPermissions: permissionBoundary.missingPermissions } - }); - } - - // validate custom roles input - const customInputRoles = roles.filter( - ({ role }) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole) - ); - const hasCustomRole = Boolean(customInputRoles.length); - const customRoles = hasCustomRole - ? await projectRoleDAL.find({ - projectId, - $in: { slug: customInputRoles.map(({ role }) => role) } - }) - : []; - if (customRoles.length !== customInputRoles.length) - throw new NotFoundError({ message: "One or more custom project roles not found" }); - - const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug); - const projectIdentity = await identityProjectDAL.transaction(async (tx) => { - const identityProjectMembership = await identityProjectDAL.create( - { - identityId, - projectId: project.id - }, - tx - ); - const sanitizedProjectMembershipRoles = roles.map((inputRole) => { - const isCustomRole = Boolean(customRolesGroupBySlug?.[inputRole.role]?.[0]); - if (!inputRole.isTemporary) { - return { - projectMembershipId: identityProjectMembership.id, - role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role, - customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null - }; - } - - // check cron or relative here later for now its just relative - const relativeTimeInMs = ms(inputRole.temporaryRange); - return { - projectMembershipId: identityProjectMembership.id, - role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role, - customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null, - isTemporary: true, - temporaryMode: ProjectUserMembershipTemporaryMode.Relative, - temporaryRange: inputRole.temporaryRange, - temporaryAccessStartTime: new Date(inputRole.temporaryAccessStartTime), - temporaryAccessEndTime: new Date(new Date(inputRole.temporaryAccessStartTime).getTime() + relativeTimeInMs) - }; - }); - - const identityRoles = await identityProjectMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx); - return { ...identityProjectMembership, roles: identityRoles }; - }); - - await permissionService.invalidateProjectPermissionCache(projectId); - - return projectIdentity; - }; - - const updateProjectIdentity = async ({ - projectId, - identityId, - roles, - actor, - actorId, - actorAuthMethod, - actorOrgId - }: TUpdateProjectIdentityDTO) => { - const { permission } = await permissionService.getProjectPermission({ - actor, - actorId, - projectId, - actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any - }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionIdentityActions.Edit, - subject(ProjectPermissionSub.Identity, { identityId }) - ); - - const projectIdentity = await identityProjectDAL.findOne({ identityId, projectId }); - if (!projectIdentity) - throw new NotFoundError({ - message: `Identity with ID ${identityId} doesn't exists in project with ID ${projectId}` - }); - - const providedRolePermissionDetails = await permissionService.getProjectPermissionByRoles( - roles.map((el) => el.role).filter((el) => el !== ProjectMembershipRole.NoAccess), - projectId - ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId); - for await (const { permission: rolePermission } of providedRolePermissionDetails) { - const permissionBoundary = validatePrivilegeChangeOperation( - shouldUseNewPrivilegeSystem, - ProjectPermissionIdentityActions.GrantPrivileges, - ProjectPermissionSub.Identity, - permission, - rolePermission - ); - - if (!permissionBoundary.isValid) - throw new PermissionBoundaryError({ - message: constructPermissionErrorMessage( - "Failed to change role", - shouldUseNewPrivilegeSystem, - ProjectPermissionIdentityActions.GrantPrivileges, - ProjectPermissionSub.Identity - ), - details: { missingPermissions: permissionBoundary.missingPermissions } - }); - } - - // validate custom roles input - const customInputRoles = roles.filter( - ({ role }) => - !Object.values(ProjectMembershipRole) - // we don't want to include custom in this check; - // this unintentionally enables setting slug to custom which is reserved - .filter((r) => r !== ProjectMembershipRole.Custom) - .includes(role as ProjectMembershipRole.Admin) - ); - const hasCustomRole = Boolean(customInputRoles.length); - const customRoles = hasCustomRole - ? await projectRoleDAL.find({ - projectId, - $in: { slug: customInputRoles.map(({ role }) => role) } - }) - : []; - if (customRoles.length !== customInputRoles.length) - throw new NotFoundError({ message: "One or more custom project roles not found" }); - - const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug); - - const sanitizedProjectMembershipRoles = roles.map((inputRole) => { - const isCustomRole = Boolean(customRolesGroupBySlug?.[inputRole.role]?.[0]); - if (!inputRole.isTemporary) { - return { - projectMembershipId: projectIdentity.id, - role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role, - customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null - }; - } - - // check cron or relative here later for now its just relative - const relativeTimeInMs = ms(inputRole.temporaryRange); - return { - projectMembershipId: projectIdentity.id, - role: isCustomRole ? ProjectMembershipRole.Custom : inputRole.role, - customRoleId: customRolesGroupBySlug[inputRole.role] ? customRolesGroupBySlug[inputRole.role][0].id : null, - isTemporary: true, - temporaryMode: ProjectUserMembershipTemporaryMode.Relative, - temporaryRange: inputRole.temporaryRange, - temporaryAccessStartTime: new Date(inputRole.temporaryAccessStartTime), - temporaryAccessEndTime: new Date(new Date(inputRole.temporaryAccessStartTime).getTime() + relativeTimeInMs) - }; - }); - - const updatedRoles = await identityProjectMembershipRoleDAL.transaction(async (tx) => { - await identityProjectMembershipRoleDAL.delete({ projectMembershipId: projectIdentity.id }, tx); - return identityProjectMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx); - }); - - await permissionService.invalidateProjectPermissionCache(projectId); - - return updatedRoles; - }; - - const deleteProjectIdentity = async ({ - identityId, - actorId, - actor, - actorOrgId, - actorAuthMethod, - projectId - }: TDeleteProjectIdentityDTO) => { - const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId }); - if (!identityProjectMembership) { - throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); - } - - const { permission } = await permissionService.getProjectPermission({ - actor, - actorId, - projectId, - actorAuthMethod, - actorOrgId, - actionProjectType: ActionProjectType.Any - }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionIdentityActions.Delete, - subject(ProjectPermissionSub.Identity, { identityId }) - ); - - const [deletedIdentity] = await identityProjectDAL.delete({ identityId, projectId }); - - await permissionService.invalidateProjectPermissionCache(projectId); - - return deletedIdentity; - }; - const listProjectIdentities = async ({ projectId, actor, @@ -392,9 +101,13 @@ export const identityProjectServiceFactory = ({ actorAuthMethod, actorOrgId }: TGetProjectIdentityByMembershipIdDTO) => { - const membership = await identityProjectDAL.findOne({ id: identityMembershipId }); + const membership = await membershipIdentityDAL.findOne({ + id: identityMembershipId, + scope: AccessScope.Project, + scopeOrgId: actorOrgId + }); - if (!membership) { + if (!membership || !membership.scopeProjectId || !membership.actorIdentityId) { throw new NotFoundError({ message: `Project membership with ID '${identityMembershipId}' not found` }); @@ -403,7 +116,7 @@ export const identityProjectServiceFactory = ({ const { permission } = await permissionService.getProjectPermission({ actor, actorId, - projectId: membership.projectId, + projectId: membership.scopeProjectId, actorAuthMethod, actorOrgId, actionProjectType: ActionProjectType.Any @@ -411,20 +124,17 @@ export const identityProjectServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionIdentityActions.Read, - subject(ProjectPermissionSub.Identity, { identityId: membership.identityId }) + subject(ProjectPermissionSub.Identity, { identityId: membership.actorIdentityId }) ); - const [identityMembership] = await identityProjectDAL.findByProjectId(membership.projectId, { - identityId: membership.identityId + const [identityMembership] = await identityProjectDAL.findByProjectId(membership.scopeProjectId, { + identityId: membership.actorIdentityId }); return identityMembership; }; return { - createProjectIdentity, - updateProjectIdentity, - deleteProjectIdentity, listProjectIdentities, getProjectIdentityByIdentityId, getProjectIdentityByMembershipId diff --git a/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-service.ts b/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-service.ts index 74ab525a0..9d26a8be8 100644 --- a/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-service.ts +++ b/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -16,7 +16,7 @@ import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedEr import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TKmsServiceFactory } from "../kms/kms-service"; @@ -31,7 +31,7 @@ type TIdentityTlsCertAuthServiceFactoryDep = { TIdentityTlsCertAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete" >; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; licenseService: Pick; permissionService: Pick; kmsService: Pick; @@ -50,7 +50,7 @@ const parseSubjectDetails = (data: string) => { export const identityTlsCertAuthServiceFactory = ({ identityAccessTokenDAL, identityTlsCertAuthDAL, - identityOrgMembershipDAL, + membershipIdentityDAL, licenseService, permissionService, kmsService, @@ -64,8 +64,9 @@ export const identityTlsCertAuthServiceFactory = ({ }); } - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ - identityId: identityTlsCertAuth.identityId + const identityMembershipOrg = await membershipIdentityDAL.findOne({ + actorIdentityId: identityTlsCertAuth.identityId, + scope: AccessScope.Organization }); if (!identityMembershipOrg) { @@ -76,7 +77,7 @@ export const identityTlsCertAuthServiceFactory = ({ const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const caCertificate = decryptor({ @@ -121,7 +122,7 @@ export const identityTlsCertAuthServiceFactory = ({ // Generate the token const identityAccessToken = await identityTlsCertAuthDAL.transaction(async (tx) => { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH, @@ -183,7 +184,13 @@ export const identityTlsCertAuthServiceFactory = ({ }) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) { @@ -199,13 +206,13 @@ export const identityTlsCertAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -225,13 +232,13 @@ export const identityTlsCertAuthServiceFactory = ({ const { encryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const identityTlsCertAuth = await identityTlsCertAuthDAL.transaction(async (tx) => { const doc = await identityTlsCertAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, accessTokenMaxTTL, allowedCommonNames, accessTokenTTL, @@ -243,7 +250,7 @@ export const identityTlsCertAuthServiceFactory = ({ ); return doc; }); - return { ...identityTlsCertAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityTlsCertAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateTlsCertAuth: TIdentityTlsCertAuthServiceFactory["updateTlsCertAuth"] = async ({ @@ -259,7 +266,13 @@ export const identityTlsCertAuthServiceFactory = ({ actor, actorOrgId }) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) { @@ -281,13 +294,13 @@ export const identityTlsCertAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -306,7 +319,7 @@ export const identityTlsCertAuthServiceFactory = ({ }); const { encryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); const updatedTlsCertAuth = await identityTlsCertAuthDAL.updateById(identityTlsCertAuth.id, { @@ -322,7 +335,7 @@ export const identityTlsCertAuthServiceFactory = ({ : undefined }); - return { ...updatedTlsCertAuth, orgId: identityMembershipOrg.orgId }; + return { ...updatedTlsCertAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const getTlsCertAuth: TIdentityTlsCertAuthServiceFactory["getTlsCertAuth"] = async ({ @@ -332,7 +345,13 @@ export const identityTlsCertAuthServiceFactory = ({ actorAuthMethod, actorOrgId }) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) { @@ -346,21 +365,21 @@ export const identityTlsCertAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }); let caCertificate = ""; if (identityAuth.encryptedCaCertificate) { caCertificate = decryptor({ cipherTextBlob: identityAuth.encryptedCaCertificate }).toString(); } - return { ...identityAuth, caCertificate, orgId: identityMembershipOrg.orgId }; + return { ...identityAuth, caCertificate, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeTlsCertAuth: TIdentityTlsCertAuthServiceFactory["revokeTlsCertAuth"] = async ({ @@ -370,7 +389,13 @@ export const identityTlsCertAuthServiceFactory = ({ actorAuthMethod, actorOrgId }) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TLS_CERT_AUTH)) { throw new BadRequestError({ @@ -380,7 +405,7 @@ export const identityTlsCertAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -388,13 +413,13 @@ export const identityTlsCertAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -418,7 +443,7 @@ export const identityTlsCertAuthServiceFactory = ({ const deletedTlsCertAuth = await identityTlsCertAuthDAL.delete({ identityId }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.TLS_CERT_AUTH }, tx); - return { ...deletedTlsCertAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedTlsCertAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityTlsCertAuth; }; diff --git a/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-types.ts b/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-types.ts index b7a08276b..3df5713e5 100644 --- a/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-types.ts +++ b/backend/src/services/identity-tls-cert-auth/identity-tls-cert-auth-types.ts @@ -1,4 +1,4 @@ -import { TIdentityAccessTokens, TIdentityOrgMemberships, TIdentityTlsCertAuths } from "@app/db/schemas"; +import { TIdentityAccessTokens, TMemberships, TIdentityTlsCertAuths } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; export type TLoginTlsCertAuthDTO = { @@ -40,7 +40,7 @@ export type TIdentityTlsCertAuthServiceFactory = { identityTlsCertAuth: TIdentityTlsCertAuths; accessToken: string; identityAccessToken: TIdentityAccessTokens; - identityMembershipOrg: TIdentityOrgMemberships; + identityMembershipOrg: TMemberships; }>; attachTlsCertAuth: (dto: TAttachTlsCertAuthDTO) => Promise; updateTlsCertAuth: (dto: TUpdateTlsCertAuthDTO) => Promise; diff --git a/backend/src/services/identity-token-auth/identity-token-auth-service.ts b/backend/src/services/identity-token-auth/identity-token-auth-service.ts index 9ef8d7216..1c0dd30e0 100644 --- a/backend/src/services/identity-token-auth/identity-token-auth-service.ts +++ b/backend/src/services/identity-token-auth/identity-token-auth-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { IdentityAuthMethod, TableName } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod, TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOrgDALFactory } from "../org/org-dal"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; @@ -15,7 +15,7 @@ import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/li import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; @@ -36,7 +36,7 @@ type TIdentityTokenAuthServiceFactoryDep = { TIdentityTokenAuthDALFactory, "transaction" | "create" | "findOne" | "updateById" | "delete" >; - identityOrgMembershipDAL: Pick; + membershipIdentityDAL: Pick; identityAccessTokenDAL: Pick< TIdentityAccessTokenDALFactory, "create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete" @@ -51,7 +51,7 @@ export type TIdentityTokenAuthServiceFactory = ReturnType { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { @@ -87,13 +93,13 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -114,7 +120,7 @@ export const identityTokenAuthServiceFactory = ({ const identityTokenAuth = await identityTokenAuthDAL.transaction(async (tx) => { const doc = await identityTokenAuthDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, accessTokenMaxTTL, accessTokenTTL, accessTokenNumUsesLimit, @@ -124,7 +130,7 @@ export const identityTokenAuthServiceFactory = ({ ); return doc; }); - return { ...identityTokenAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityTokenAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const updateTokenAuth = async ({ @@ -141,7 +147,13 @@ export const identityTokenAuthServiceFactory = ({ }: TUpdateTokenAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { @@ -163,13 +175,13 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -198,12 +210,18 @@ export const identityTokenAuthServiceFactory = ({ return { ...updatedTokenAuth, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }; }; const getTokenAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetTokenAuthDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { @@ -217,13 +235,13 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...identityTokenAuth, orgId: identityMembershipOrg.orgId }; + return { ...identityTokenAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityTokenAuth = async ({ @@ -236,7 +254,13 @@ export const identityTokenAuthServiceFactory = ({ }: TRevokeTokenAuthDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { @@ -247,7 +271,7 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -255,13 +279,13 @@ export const identityTokenAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -287,7 +311,7 @@ export const identityTokenAuthServiceFactory = ({ authMethod: IdentityAuthMethod.TOKEN_AUTH }); - return { ...deletedTokenAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedTokenAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityTokenAuth; }; @@ -303,7 +327,13 @@ export const identityTokenAuthServiceFactory = ({ }: TCreateTokenAuthTokenDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { @@ -314,7 +344,7 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -322,13 +352,13 @@ export const identityTokenAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.CreateToken, @@ -350,7 +380,7 @@ export const identityTokenAuthServiceFactory = ({ const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId }); const identityAccessToken = await identityTokenAuthDAL.transaction(async (tx) => { - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.TOKEN_AUTH, @@ -405,7 +435,13 @@ export const identityTokenAuthServiceFactory = ({ }: TGetTokenAuthTokensDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) { @@ -416,7 +452,7 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -448,7 +484,13 @@ export const identityTokenAuthServiceFactory = ({ }); if (!foundToken) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` }); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: foundToken.identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId: foundToken.identityId + }); if (!identityMembershipOrg) { throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` }); } @@ -462,7 +504,7 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -470,12 +512,12 @@ export const identityTokenAuthServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.CreateToken, @@ -529,8 +571,9 @@ export const identityTokenAuthServiceFactory = ({ await validateIdentityUpdateForSuperAdminPrivileges(identityAccessToken.identityId, isActorSuperAdmin); - const identityOrgMembership = await identityOrgMembershipDAL.findOne({ - identityId: identityAccessToken.identityId + const identityOrgMembership = await membershipIdentityDAL.findOne({ + actorIdentityId: identityAccessToken.identityId, + scope: AccessScope.Organization }); if (!identityOrgMembership) { @@ -540,7 +583,7 @@ export const identityTokenAuthServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityOrgMembership.orgId, + identityOrgMembership.scopeOrgId, actorAuthMethod, actorOrgId ); diff --git a/backend/src/services/identity-ua/identity-ua-service.ts b/backend/src/services/identity-ua/identity-ua-service.ts index f5cd5494d..563a3f897 100644 --- a/backend/src/services/identity-ua/identity-ua-service.ts +++ b/backend/src/services/identity-ua/identity-ua-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { IdentityAuthMethod } from "@app/db/schemas"; +import { AccessScope, IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { @@ -22,9 +22,9 @@ import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from import { logger } from "@app/lib/logger"; import { ActorType, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TOrgDALFactory } from "../org/org-dal"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; import { TIdentityUaClientSecretDALFactory } from "./identity-ua-client-secret-dal"; @@ -45,7 +45,7 @@ type TIdentityUaServiceFactoryDep = { identityUaDAL: TIdentityUaDALFactory; identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory; identityAccessTokenDAL: TIdentityAccessTokenDALFactory; - identityOrgMembershipDAL: TIdentityOrgDALFactory; + membershipIdentityDAL: TMembershipIdentityDALFactory; permissionService: Pick; licenseService: Pick; orgDAL: Pick; @@ -66,7 +66,7 @@ export const identityUaServiceFactory = ({ identityUaDAL, identityUaClientSecretDAL, identityAccessTokenDAL, - identityOrgMembershipDAL, + membershipIdentityDAL, permissionService, licenseService, orgDAL, @@ -100,7 +100,10 @@ export const identityUaServiceFactory = ({ }); } - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityUa.identityId }); + const identityMembershipOrg = await membershipIdentityDAL.findOne({ + actorIdentityId: identityUa.identityId, + scope: AccessScope.Organization + }); if (!identityMembershipOrg) { throw new UnauthorizedError({ message: "Invalid credentials" @@ -226,7 +229,7 @@ export const identityUaServiceFactory = ({ const identityAccessToken = await identityUaDAL.transaction(async (tx) => { const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx); - await identityOrgMembershipDAL.updateById( + await membershipIdentityDAL.updateById( identityMembershipOrg.id, { lastLoginAuthMethod: IdentityAuthMethod.UNIVERSAL_AUTH, @@ -298,7 +301,13 @@ export const identityUaServiceFactory = ({ }: TAttachUaDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin); - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -314,13 +323,13 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => { if ( !plan.ipAllowlisting && @@ -357,7 +366,7 @@ export const identityUaServiceFactory = ({ const identityUa = await identityUaDAL.transaction(async (tx) => { const doc = await identityUaDAL.create( { - identityId: identityMembershipOrg.identityId, + identityId: identityMembershipOrg.identity.id, clientId: crypto.nativeCrypto.randomUUID(), clientSecretTrustedIps: JSON.stringify(reformattedClientSecretTrustedIps), accessTokenMaxTTL, @@ -374,7 +383,7 @@ export const identityUaServiceFactory = ({ ); return doc; }); - return { ...identityUa, orgId: identityMembershipOrg.orgId }; + return { ...identityUa, orgId: identityMembershipOrg.scopeOrgId }; }; const updateUniversalAuth = async ({ @@ -394,7 +403,13 @@ export const identityUaServiceFactory = ({ lockoutDurationSeconds, lockoutCounterResetSeconds }: TUpdateUaDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); const uaIdentityAuth = await identityUaDAL.findOne({ identityId }); @@ -418,13 +433,13 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); - const plan = await licenseService.getPlan(identityMembershipOrg.orgId); + const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId); const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => { if ( !plan.ipAllowlisting && @@ -474,11 +489,17 @@ export const identityUaServiceFactory = ({ lockoutDurationSeconds, lockoutCounterResetSeconds }); - return { ...updatedUaAuth, orgId: identityMembershipOrg.orgId }; + return { ...updatedUaAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const getIdentityUniversalAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetUaDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); const uaIdentityAuth = await identityUaDAL.findOne({ identityId }); @@ -495,12 +516,12 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); - return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId }; + return { ...uaIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeIdentityUniversalAuth = async ({ @@ -510,7 +531,13 @@ export const identityUaServiceFactory = ({ actorAuthMethod, actorOrgId }: TRevokeUaDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -521,7 +548,7 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -529,12 +556,12 @@ export const identityUaServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.RevokeAuth, @@ -555,7 +582,7 @@ export const identityUaServiceFactory = ({ const revokedIdentityUniversalAuth = await identityUaDAL.transaction(async (tx) => { const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx); - return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.orgId }; + return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; }); return revokedIdentityUniversalAuth; }; @@ -570,7 +597,13 @@ export const identityUaServiceFactory = ({ description, numUsesLimit }: TCreateUaClientSecretDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -582,7 +615,7 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -590,12 +623,12 @@ export const identityUaServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.CreateToken, @@ -618,7 +651,7 @@ export const identityUaServiceFactory = ({ const clientSecret = crypto.randomBytes(32).toString("hex"); const clientSecretHash = await crypto.hashing().createHash(clientSecret, appCfg.SALT_ROUNDS); - const identityUaAuth = await identityUaDAL.findOne({ identityId: identityMembershipOrg.identityId }); + const identityUaAuth = await identityUaDAL.findOne({ identityId: identityMembershipOrg.identity.id }); if (!identityUaAuth) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); const identityUaClientSecret = await identityUaClientSecretDAL.create({ @@ -634,7 +667,7 @@ export const identityUaServiceFactory = ({ return { clientSecret, clientSecretData: identityUaClientSecret, - orgId: identityMembershipOrg.orgId + orgId: identityMembershipOrg.scopeOrgId }; }; @@ -645,7 +678,13 @@ export const identityUaServiceFactory = ({ actorAuthMethod, identityId }: TGetUaClientSecretsDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -656,7 +695,7 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -664,13 +703,13 @@ export const identityUaServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.GetToken, @@ -697,7 +736,7 @@ export const identityUaServiceFactory = ({ identityUAId: identityUniversalAuth.id, isClientSecretRevoked: false }); - return { clientSecrets, orgId: identityMembershipOrg.orgId }; + return { clientSecrets, orgId: identityMembershipOrg.scopeOrgId }; }; const getUniversalAuthClientSecretById = async ({ @@ -708,7 +747,13 @@ export const identityUaServiceFactory = ({ actorAuthMethod, clientSecretId }: TGetUniversalAuthClientSecretByIdDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -726,7 +771,7 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -734,12 +779,12 @@ export const identityUaServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.GetToken, @@ -758,7 +803,7 @@ export const identityUaServiceFactory = ({ details: { missingPermissions: permissionBoundary.missingPermissions } }); - return { ...clientSecret, identityId, orgId: identityMembershipOrg.orgId }; + return { ...clientSecret, identityId, orgId: identityMembershipOrg.scopeOrgId }; }; const revokeUniversalAuthClientSecret = async ({ @@ -769,7 +814,13 @@ export const identityUaServiceFactory = ({ actorAuthMethod, clientSecretId }: TRevokeUaClientSecretDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -787,7 +838,7 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -795,13 +846,13 @@ export const identityUaServiceFactory = ({ const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, - identityMembershipOrg.identityId, - identityMembershipOrg.orgId, + identityMembershipOrg.identity.id, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); - const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.orgId); + const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId); const permissionBoundary = validatePrivilegeChangeOperation( shouldUseNewPrivilegeSystem, OrgPermissionIdentityActions.DeleteToken, @@ -825,7 +876,7 @@ export const identityUaServiceFactory = ({ isClientSecretRevoked: true }); - return { ...updatedClientSecret, identityId, orgId: identityMembershipOrg.orgId }; + return { ...updatedClientSecret, identityId, orgId: identityMembershipOrg.scopeOrgId }; }; const clearUniversalAuthLockouts = async ({ @@ -835,7 +886,13 @@ export const identityUaServiceFactory = ({ actorOrgId, actorAuthMethod }: TClearUaLockoutsDTO) => { - const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); + const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId + }); if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.UNIVERSAL_AUTH)) { @@ -847,7 +904,7 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId, + identityMembershipOrg.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -857,7 +914,7 @@ export const identityUaServiceFactory = ({ pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.UNIVERSAL_AUTH}:*` }); - return { deleted, identityId, orgId: identityMembershipOrg.orgId }; + return { deleted, identityId, orgId: identityMembershipOrg.scopeOrgId }; }; return { diff --git a/backend/src/services/identity/identity-org-dal.ts b/backend/src/services/identity/identity-org-dal.ts index 0df740356..7cd606032 100644 --- a/backend/src/services/identity/identity-org-dal.ts +++ b/backend/src/services/identity/identity-org-dal.ts @@ -608,7 +608,7 @@ export const identityOrgDALFactory = (db: TDbClient) => { } : undefined, identity: { - id: identityId, + id: identityId as string, name: identityName, hasDeleteProtection, authMethods: buildAuthMethods({ diff --git a/backend/src/services/identity/identity-service.ts b/backend/src/services/identity/identity-service.ts index 514f5fd9e..5c89b9902 100644 --- a/backend/src/services/identity/identity-service.ts +++ b/backend/src/services/identity/identity-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError } from "@casl/ability"; -import { OrgMembershipRole, TableName, TRoles } from "@app/db/schemas"; +import { AccessScope, OrgMembershipRole, TableName, TRoles } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { @@ -26,11 +26,15 @@ import { TSearchOrgIdentitiesByOrgIdDTO, TUpdateIdentityDTO } from "./identity-types"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; type TIdentityServiceFactoryDep = { identityDAL: TIdentityDALFactory; identityMetadataDAL: TIdentityMetadataDALFactory; identityOrgMembershipDAL: TIdentityOrgDALFactory; + membershipIdentityDAL: TMembershipIdentityDALFactory; + membershipRoleDAL: TMembershipRoleDALFactory; identityProjectDAL: Pick; permissionService: Pick; licenseService: Pick; @@ -48,7 +52,9 @@ export const identityServiceFactory = ({ permissionService, licenseService, keyStore, - orgDAL + orgDAL, + membershipIdentityDAL, + membershipRoleDAL }: TIdentityServiceFactoryDep) => { const createIdentity = async ({ name, @@ -99,12 +105,20 @@ export const identityServiceFactory = ({ const identity = await identityDAL.transaction(async (tx) => { const newIdentity = await identityDAL.create({ name, hasDeleteProtection }, tx); - await identityOrgMembershipDAL.create( + const membership = await membershipIdentityDAL.create( { - identityId: newIdentity.id, - orgId, + scope: AccessScope.Organization, + actorIdentityId: newIdentity.id, + scopeOrgId: orgId + }, + tx + ); + + await membershipRoleDAL.create( + { + membershipId: membership.id, role: isCustomRole ? OrgMembershipRole.Custom : role, - roleId: rolePermissionDetails?.role?.id + customRoleId: rolePermissionDetails?.role?.id }, tx ); @@ -151,13 +165,17 @@ export const identityServiceFactory = ({ }: TUpdateIdentityDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(id, isActorSuperAdmin); - const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); + const identityOrgMembership = await membershipIdentityDAL.findOne({ + actorIdentityId: id, + scope: AccessScope.Organization, + scopeOrgId: actorOrgId + }); if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` }); const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityOrgMembership.orgId, + identityOrgMembership.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -197,11 +215,14 @@ export const identityServiceFactory = ({ : await identityDAL.findById(id, tx); if (role) { - await identityOrgMembershipDAL.updateById( - identityOrgMembership.id, + await membershipRoleDAL.delete({ membershipId: identityOrgMembership.id }, tx); + await membershipRoleDAL.update( + { + membershipId: identityOrgMembership.id + }, { role: customRole ? OrgMembershipRole.Custom : role, - roleId: customRole?.id || null + customRoleId: customRole?.id || null }, tx ); @@ -213,12 +234,12 @@ export const identityServiceFactory = ({ }> = []; if (metadata) { - await identityMetadataDAL.delete({ orgId: identityOrgMembership.orgId, identityId: id }, tx); + await identityMetadataDAL.delete({ orgId: identityOrgMembership.scopeOrgId, identityId: id }, tx); if (metadata.length) { const rowsToInsert = metadata.map(({ key, value }) => ({ identityId: newIdentity.id, - orgId: identityOrgMembership.orgId, + orgId: identityOrgMembership.scopeOrgId, key, value })); @@ -233,12 +254,14 @@ export const identityServiceFactory = ({ }; }); - return { ...identity, orgId: identityOrgMembership.orgId }; + return { ...identity, orgId: identityOrgMembership.scopeOrgId }; }; const getIdentityById = async ({ id, actor, actorId, actorOrgId, actorAuthMethod }: TGetIdentityByIdDTO) => { const doc = await identityOrgMembershipDAL.find({ - [`${TableName.IdentityOrgMembership}.identityId` as "identityId"]: id + [`${TableName.Membership}.actorIdentityId` as "actorIdentityId"]: id, + scope: AccessScope.Organization, + scopeOrgId: actorOrgId }); const identity = doc[0]; if (!identity) throw new NotFoundError({ message: `Failed to find identity with id ${id}` }); @@ -252,6 +275,7 @@ export const identityServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); + // TODO(simp): check this in identity service const activeLockouts = await keyStore.getKeysByPattern(`lockout:identity:${id}:*`); const activeLockoutAuthMethods = new Set(); @@ -283,14 +307,19 @@ export const identityServiceFactory = ({ isActorSuperAdmin }: TDeleteIdentityDTO) => { await validateIdentityUpdateForSuperAdminPrivileges(id, isActorSuperAdmin); - - const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); + const identityOrgMembership = await membershipIdentityDAL.getIdentityById({ + scopeData: { + scope: AccessScope.Organization, + orgId: actorOrgId + }, + identityId: id + }); if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` }); const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityOrgMembership.orgId, + identityOrgMembership.scopeOrgId, actorAuthMethod, actorOrgId ); @@ -302,9 +331,9 @@ export const identityServiceFactory = ({ const deletedIdentity = await identityDAL.deleteById(id); - await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.orgId); + await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.scopeOrgId); - return { ...deletedIdentity, orgId: identityOrgMembership.orgId }; + return { ...deletedIdentity, orgId: identityOrgMembership.scopeOrgId }; }; const listOrgIdentities = async ({ @@ -323,7 +352,8 @@ export const identityServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); const identityMemberships = await identityOrgMembershipDAL.find({ - [`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId, + [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId, + scope: AccessScope.Organization, limit, offset, orderBy, @@ -332,7 +362,7 @@ export const identityServiceFactory = ({ }); const totalCount = await identityOrgMembershipDAL.countAllOrgIdentities({ - [`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId, + [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId, search }); @@ -373,13 +403,17 @@ export const identityServiceFactory = ({ actorAuthMethod, actorOrgId }: TListProjectIdentitiesByIdentityIdDTO) => { - const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId }); + const identityOrgMembership = await membershipIdentityDAL.findOne({ + actorIdentityId: identityId, + scope: AccessScope.Organization, + scopeOrgId: actorOrgId + }); if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` }); const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityOrgMembership.orgId, + identityOrgMembership.scopeOrgId, actorAuthMethod, actorOrgId ); diff --git a/backend/src/services/membership-group/membership-group-service.ts b/backend/src/services/membership-group/membership-group-service.ts index ba32d9950..6a396e4e5 100644 --- a/backend/src/services/membership-group/membership-group-service.ts +++ b/backend/src/services/membership-group/membership-group-service.ts @@ -269,7 +269,7 @@ export const membershipGroupServiceFactory = ({ [SearchResourceOperators.$contains]: dto.data.groupName } : undefined, - role: dto.data.roles.length + role: dto.data.roles?.length ? { [SearchResourceOperators.$in]: dto.data.roles } diff --git a/backend/src/services/org-admin/org-admin-service.ts b/backend/src/services/org-admin/org-admin-service.ts index 995afadc1..1a1cefee6 100644 --- a/backend/src/services/org-admin/org-admin-service.ts +++ b/backend/src/services/org-admin/org-admin-service.ts @@ -1,26 +1,25 @@ import { ForbiddenError } from "@casl/ability"; -import { ProjectMembershipRole, ProjectVersion } from "@app/db/schemas"; +import { AccessScope, ProjectMembershipRole, ProjectVersion } from "@app/db/schemas"; import { OrgPermissionAdminConsoleAction, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; import { TNotificationServiceFactory } from "../notification/notification-service"; import { NotificationType } from "../notification/notification-types"; import { TProjectDALFactory } from "../project/project-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; -import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { TAccessProjectDTO, TListOrgProjectsDTO } from "./org-admin-types"; type TOrgAdminServiceFactoryDep = { permissionService: Pick; projectDAL: Pick; - projectMembershipDAL: Pick< - TProjectMembershipDALFactory, - "findOne" | "create" | "transaction" | "delete" | "findAllProjectMembers" - >; - projectUserMembershipRoleDAL: Pick; + projectMembershipDAL: Pick; + membershipUserDAL: TMembershipUserDALFactory; + membershipRoleDAL: TMembershipRoleDALFactory; smtpService: Pick; notificationService: Pick; }; @@ -31,9 +30,10 @@ export const orgAdminServiceFactory = ({ permissionService, projectDAL, projectMembershipDAL, - projectUserMembershipRoleDAL, smtpService, - notificationService + notificationService, + membershipUserDAL, + membershipRoleDAL }: TOrgAdminServiceFactoryDep) => { const listOrgProjects = async ({ actor, @@ -98,17 +98,18 @@ export const orgAdminServiceFactory = ({ } // check already there exist a membership if there return it - const projectMembership = await projectMembershipDAL.findOne({ - projectId, - userId: actorId + const projectMembership = await membershipUserDAL.findOne({ + scopeProjectId: projectId, + scope: AccessScope.Project, + actorUserId: actorId }); if (projectMembership) { // reset and make the user admin - await projectMembershipDAL.transaction(async (tx) => { - await projectUserMembershipRoleDAL.delete({ projectMembershipId: projectMembership.id }, tx); - await projectUserMembershipRoleDAL.create( + await membershipUserDAL.transaction(async (tx) => { + await membershipRoleDAL.delete({ membershipId: projectMembership.id }, tx); + await membershipRoleDAL.create( { - projectMembershipId: projectMembership.id, + membershipId: projectMembership.id, role: ProjectMembershipRole.Admin }, tx @@ -117,18 +118,16 @@ export const orgAdminServiceFactory = ({ return { isExistingMember: true, membership: projectMembership }; } - const updatedMembership = await projectMembershipDAL.transaction(async (tx) => { - const newProjectMembership = await projectMembershipDAL.create( + const updatedMembership = await membershipUserDAL.transaction(async (tx) => { + const newProjectMembership = await membershipUserDAL.create( { - projectId, - userId: actorId + scopeProjectId: projectId, + actorUserId: actorId, + scope: AccessScope.Project }, tx ); - await projectUserMembershipRoleDAL.create( - { projectMembershipId: newProjectMembership.id, role: ProjectMembershipRole.Admin }, - tx - ); + await membershipRoleDAL.create({ membershipId: newProjectMembership.id, role: ProjectMembershipRole.Admin }, tx); return newProjectMembership; }); diff --git a/backend/src/services/org/org-role-service.ts b/backend/src/services/org/org-role-service.ts index 69b8da7a6..4afb6f791 100644 --- a/backend/src/services/org/org-role-service.ts +++ b/backend/src/services/org/org-role-service.ts @@ -15,7 +15,6 @@ import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-g import { TOrgDALFactory } from "@app/services/org/org-dal"; import { ActorAuthMethod, ActorType } from "../auth/auth-type"; -import { TOrgRoleDALFactory } from "./org-role-dal"; type TOrgRoleServiceFactoryDep = { orgRoleDAL: TOrgRoleDALFactory; diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index 3dc23fa54..74729326b 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -3,6 +3,7 @@ import slugify from "@sindresorhus/slugify"; import { Knex } from "knex"; import { + AccessScope, ActionProjectType, OrgMembershipRole, OrgMembershipStatus, @@ -31,7 +32,6 @@ import { } from "@app/ee/services/permission/permission-fns"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionMemberActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; -import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal"; import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal"; import { getConfig } from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; @@ -59,13 +59,14 @@ import { ActorAuthMethod, ActorType, AuthMethod, AuthModeJwtTokenPayload, AuthTo import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service"; import { TokenType } from "../auth-token/auth-token-types"; import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; import { TProjectDALFactory } from "../project/project-dal"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectKeyDALFactory } from "../project-key/project-key-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; -import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; -import { TProjectRoleDALFactory } from "../project-role/project-role-dal"; import { TReminderServiceFactory } from "../reminder/reminder-types"; +import { TRoleDALFactory } from "../role/role-dal"; import { TSecretDALFactory } from "../secret/secret-dal"; import { fnDeleteProjectSecretReminders } from "../secret/secret-fns"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; @@ -91,7 +92,6 @@ import { TUpgradePrivilegeSystemDTO, TVerifyUserToOrgDTO } from "./org-types"; -import { TRoleDALFactory } from "../role/role-dal"; type TOrgServiceFactoryDep = { userAliasDAL: Pick; @@ -105,26 +105,17 @@ type TOrgServiceFactoryDep = { groupDAL: TGroupDALFactory; projectDAL: TProjectDALFactory; identityMetadataDAL: Pick; + membershipUserDAL: TMembershipUserDALFactory; projectMembershipDAL: Pick< TProjectMembershipDALFactory, - | "findProjectMembershipsByUserId" - | "delete" - | "create" - | "find" - | "insertMany" - | "transaction" - | "findProjectMembershipsByUserIds" + "findProjectMembershipsByUserId" | "findProjectMembershipsByUserIds" >; projectKeyDAL: Pick; orgMembershipDAL: Pick< TOrgMembershipDALFactory, - | "findOrgMembershipById" - | "findOne" - | "findById" - | "findRecentInvitedMemberships" - | "updateById" - | "updateLastInvitedAtByIds" + "findOrgMembershipById" | "findRecentInvitedMemberships" | "updateLastInvitedAtByIds" >; + membershipRoleDAL: TMembershipRoleDALFactory; incidentContactDAL: TIncidentContactsDALFactory; samlConfigDAL: Pick; oidcConfigDAL: Pick; @@ -136,9 +127,6 @@ type TOrgServiceFactoryDep = { TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount" | "generateOrgCustomerId" | "removeOrgCustomer" >; - projectUserAdditionalPrivilegeDAL: Pick; - projectRoleDAL: Pick; - projectUserMembershipRoleDAL: Pick; projectBotService: Pick; loginService: Pick; reminderService: Pick; @@ -162,19 +150,18 @@ export const orgServiceFactory = ({ projectMembershipDAL, projectKeyDAL, orgMembershipDAL, - projectUserAdditionalPrivilegeDAL, tokenService, orgBotDAL, licenseService, - projectRoleDAL, samlConfigDAL, oidcConfigDAL, ldapConfigDAL, - projectUserMembershipRoleDAL, identityMetadataDAL, projectBotService, loginService, - reminderService + reminderService, + membershipRoleDAL, + membershipUserDAL }: TOrgServiceFactoryDep) => { /* * Get organization details by the organization id @@ -273,7 +260,7 @@ export const orgServiceFactory = ({ } if (actor === ActorType.IDENTITY) { - const workspaces = await projectDAL.findAllProjectsByIdentity(actorId); + const workspaces = await projectDAL.findIdentityProjects(actorId, orgId); return workspaces; } @@ -308,14 +295,21 @@ export const orgServiceFactory = ({ ); const createMembershipData = { - orgId, - userId: user.id, - role: OrgMembershipRole.Admin, + scopeOrgId: orgId, + scope: AccessScope.Organization, + actorUserId: user.id, status: OrgMembershipStatus.Accepted, isActive: true }; - await orgDAL.createMembership(createMembershipData, tx); + const membership = await orgDAL.createMembership(createMembershipData, tx); + await membershipRoleDAL.create( + { + membershipId: membership.id, + role: OrgMembershipRole.Admin + }, + tx + ); return { user, @@ -613,16 +607,23 @@ export const orgServiceFactory = ({ tx ); if (userId) { - await orgDAL.createMembership( + const membership = await orgDAL.createMembership( { - userId, - orgId: org.id, - role: OrgMembershipRole.Admin, + scope: AccessScope.Organization, + actorUserId: userId, + scopeOrgId: org.id, status: OrgMembershipStatus.Accepted, isActive: true }, tx ); + await membershipRoleDAL.create( + { + membershipId: membership.id, + role: OrgMembershipRole.Admin + }, + tx + ); } await orgBotDAL.create( { @@ -766,12 +767,16 @@ export const orgServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member); - const foundMembership = await orgMembershipDAL.findById(membershipId); + const foundMembership = await membershipUserDAL.findOne({ + id: membershipId, + scope: AccessScope.Organization, + scopeOrgId: actorOrgId + }); if (!foundMembership) throw new NotFoundError({ message: `Organization membership with ID ${membershipId} not found` }); - if (foundMembership.orgId !== orgId) + if (foundMembership.scopeOrgId !== orgId) throw new UnauthorizedError({ message: "Updated org member doesn't belong to the organization" }); - if (foundMembership.userId === userId) + if (foundMembership.scopeOrgId === userId) throw new UnauthorizedError({ message: "Cannot update own organization membership" }); const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole); @@ -793,8 +798,20 @@ export const orgServiceFactory = ({ const membership = await orgDAL.transaction(async (tx) => { const [updatedOrgMembership] = await orgDAL.updateMembership( { id: membershipId, scopeOrgId: orgId }, - { role: userRole, roleId: userRoleId, isActive } + { isActive }, + tx ); + if (userRole) { + await membershipRoleDAL.delete({ membershipId: updatedOrgMembership.id }, tx); + await membershipRoleDAL.create( + { + membershipId: updatedOrgMembership.id, + role: userRole, + customRoleId: userRoleId + }, + tx + ); + } if (metadata) { await identityMetadataDAL.delete({ userId: updatedOrgMembership.actorUserId, orgId }, tx); @@ -833,8 +850,9 @@ export const orgServiceFactory = ({ const org = await orgDAL.findOrgById(orgId); const [inviteeOrgMembership] = await orgDAL.findMembership({ - [`${TableName.OrgMembership}.orgId` as "orgId"]: orgId, - [`${TableName.OrgMembership}.id` as "id"]: membershipId + [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId, + [`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization, + [`${TableName.Membership}.id` as "id"]: membershipId }); if (inviteeOrgMembership.status !== OrgMembershipStatus.Invited) { @@ -845,7 +863,7 @@ export const orgServiceFactory = ({ const token = await tokenService.createTokenForUser({ type: TokenType.TOKEN_EMAIL_ORG_INVITATION, - userId: inviteeOrgMembership.userId, + userId: inviteeOrgMembership.actorUserId as string, orgId }); @@ -873,7 +891,7 @@ export const orgServiceFactory = ({ } }); - await orgMembershipDAL.updateById(inviteeOrgMembership.id, { + await membershipUserDAL.updateById(inviteeOrgMembership.id, { lastInvitedAt: new Date() }); @@ -991,8 +1009,9 @@ export const orgServiceFactory = ({ const [inviteeOrgMembership] = await orgDAL.findMembership( { - [`${TableName.OrgMembership}.orgId` as "orgId"]: orgId, - [`${TableName.OrgMembership}.userId` as "userId"]: inviteeUserId + [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId, + [`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization, + [`${TableName.Membership}.id` as "id"]: inviteeUserId }, { tx } ); @@ -1019,7 +1038,7 @@ export const orgServiceFactory = ({ let roleId; const orgRole = isCustomOrgRole ? OrgMembershipRole.Custom : organizationRoleSlug; if (isCustomOrgRole) { - const customRole = await orgRoleDAL.findOne({ slug: organizationRoleSlug, orgId }); + const customRole = await roleDAL.findOne({ slug: organizationRoleSlug, orgId }); if (!customRole) { throw new NotFoundError({ name: "InviteUser", @@ -1029,15 +1048,22 @@ export const orgServiceFactory = ({ roleId = customRole.id; } - await orgDAL.createMembership( + const membership = await orgDAL.createMembership( { - userId: inviteeUser.id, + actorUserId: inviteeUser.id, inviteEmail: inviteeEmail, - orgId, - role: orgRole, + scopeOrgId: orgId, status: OrgMembershipStatus.Invited, isActive: true, - roleId + scope: AccessScope.Organization + }, + tx + ); + await membershipRoleDAL.create( + { + membershipId: membership.id, + role: orgRole, + customRoleId: roleId }, tx ); @@ -1072,14 +1098,16 @@ export const orgServiceFactory = ({ ProjectPermissionMemberActions.Create, ProjectPermissionSub.Member ); - const existingMembers = await projectMembershipDAL.find( + const existingMembers = await membershipUserDAL.find( { - projectId: project.id, + scopeOrgId: project.orgId, + scope: AccessScope.Project, + scopeProjectId: project.id, $in: { userId: userIds } }, { tx } ); - const existingMembersGroupByUserId = groupBy(existingMembers, (i) => i.userId); + const existingMembersGroupByUserId = groupBy(existingMembers, (i) => i.actorUserId as string); const userWithEncryptionKeyInvitedToProject = userEncryptionKeys.filter( (user) => !existingMembersGroupByUserId?.[user.userId] ); @@ -1246,9 +1274,10 @@ export const orgServiceFactory = ({ } const [orgMembership] = await orgDAL.findMembership({ - [`${TableName.OrgMembership}.userId` as "userId"]: user.id, + [`${TableName.Membership}.actorUserId` as "actorUserId"]: user.id, + scope: AccessScope.Organization, status: OrgMembershipStatus.Invited, - [`${TableName.OrgMembership}.orgId` as "orgId"]: orgId + [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId }); if (!orgMembership) @@ -1261,7 +1290,7 @@ export const orgServiceFactory = ({ await tokenService.validateTokenForUser({ type: TokenType.TOKEN_EMAIL_ORG_INVITATION, userId: user.id, - orgId: orgMembership.orgId, + orgId: orgMembership.scopeOrgId, code }); @@ -1273,16 +1302,17 @@ export const orgServiceFactory = ({ // this means user has already completed signup process // isAccepted is set true when keys are exchanged await orgDAL.updateMembershipById(orgMembership.id, { - orgId, + scopeOrgId: orgId, status: OrgMembershipStatus.Accepted }); await licenseService.updateSubscriptionOrgMemberCount(orgId); return { user }; } + const membershipRole = await membershipRoleDAL.findOne({ membershipId: orgMembership.id }); if ( organization.authEnforced && - !(organization.bypassOrgAuthEnabled && orgMembership.role === OrgMembershipRole.Admin) + !(organization.bypassOrgAuthEnabled && membershipRole.role === OrgMembershipRole.Admin) ) { return { user }; } @@ -1331,7 +1361,13 @@ export const orgServiceFactory = ({ actorAuthMethod, actorOrgId }: TDeleteOrgMembershipDTO) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); + const { permission } = await permissionService.getOrgPermission( + ActorType.USER, + userId, + orgId, + actorAuthMethod, + actorOrgId + ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member); const deletedMembership = await deleteOrgMembershipFn({ @@ -1339,11 +1375,11 @@ export const orgServiceFactory = ({ orgId, orgDAL, projectMembershipDAL, - projectUserAdditionalPrivilegeDAL, projectKeyDAL, userAliasDAL, licenseService, - userId + userId, + membershipUserDAL }); return deletedMembership; @@ -1356,7 +1392,13 @@ export const orgServiceFactory = ({ actorAuthMethod, actorOrgId }: TDeleteOrgMembershipsDTO) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); + const { permission } = await permissionService.getOrgPermission( + ActorType.USER, + userId, + orgId, + actorAuthMethod, + actorOrgId + ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member); if (membershipIds.includes(userId)) { @@ -1368,11 +1410,11 @@ export const orgServiceFactory = ({ orgId, orgDAL, projectMembershipDAL, - projectUserAdditionalPrivilegeDAL, projectKeyDAL, userAliasDAL, licenseService, - userId + userId, + membershipUserDAL }); return deletedMemberships; @@ -1409,7 +1451,13 @@ export const orgServiceFactory = ({ actorAuthMethod: ActorAuthMethod, actorOrgId: string | undefined ) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); + const { permission } = await permissionService.getOrgPermission( + ActorType.USER, + userId, + orgId, + actorAuthMethod, + actorOrgId + ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount); const incidentContacts = await incidentContactDAL.findByOrgId(orgId); return incidentContacts; @@ -1422,7 +1470,13 @@ export const orgServiceFactory = ({ actorAuthMethod: ActorAuthMethod, actorOrgId: string | undefined ) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); + const { permission } = await permissionService.getOrgPermission( + ActorType.USER, + userId, + orgId, + actorAuthMethod, + actorOrgId + ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.IncidentAccount); const doesIncidentContactExist = await incidentContactDAL.findOne(orgId, { email }); if (doesIncidentContactExist) { @@ -1443,7 +1497,13 @@ export const orgServiceFactory = ({ actorAuthMethod: ActorAuthMethod, actorOrgId: string | undefined ) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); + const { permission } = await permissionService.getOrgPermission( + ActorType.USER, + userId, + orgId, + actorAuthMethod, + actorOrgId + ); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.IncidentAccount); const incidentContact = await incidentContactDAL.deleteById(id, orgId); @@ -1464,17 +1524,17 @@ export const orgServiceFactory = ({ await Promise.all( invitedUsers.map(async (invitedUser) => { - let org = orgCache[invitedUser.orgId]; + let org = orgCache[invitedUser.scopeOrgId]; if (!org) { - org = await orgDAL.findById(invitedUser.orgId); - orgCache[invitedUser.orgId] = org; + org = await orgDAL.findById(invitedUser.scopeOrgId); + orgCache[invitedUser.scopeOrgId] = org; } - if (!org || !invitedUser.userId) return; + if (!org || !invitedUser.actorUserId) return; const token = await tokenService.createTokenForUser({ type: TokenType.TOKEN_EMAIL_ORG_INVITATION, - userId: invitedUser.userId, + userId: invitedUser.actorUserId, orgId: org.id }); diff --git a/backend/src/services/project-key/project-key-service.ts b/backend/src/services/project-key/project-key-service.ts index a884d25bc..7faa11f2b 100644 --- a/backend/src/services/project-key/project-key-service.ts +++ b/backend/src/services/project-key/project-key-service.ts @@ -1,25 +1,25 @@ import { ForbiddenError } from "@casl/ability"; -import { ActionProjectType } from "@app/db/schemas"; +import { AccessScope, ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionMemberActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; -import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; import { TProjectKeyDALFactory } from "./project-key-dal"; import { TGetLatestProjectKeyDTO, TUploadProjectKeyDTO } from "./project-key-types"; type TProjectKeyServiceFactoryDep = { permissionService: TPermissionServiceFactory; projectKeyDAL: TProjectKeyDALFactory; - projectMembershipDAL: TProjectMembershipDALFactory; + membershipUserDAL: TMembershipUserDALFactory; }; export type TProjectKeyServiceFactory = ReturnType; export const projectKeyServiceFactory = ({ projectKeyDAL, - projectMembershipDAL, + membershipUserDAL, permissionService }: TProjectKeyServiceFactoryDep) => { const uploadProjectKeys = async ({ @@ -42,9 +42,10 @@ export const projectKeyServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member); - const receiverMembership = await projectMembershipDAL.findOne({ - userId: receiverId, - projectId + const receiverMembership = await membershipUserDAL.findOne({ + actorUserId: receiverId, + scopeProjectId: projectId, + scope: AccessScope.Project }); if (!receiverMembership) throw new BadRequestError({ diff --git a/backend/src/services/project/project-dal.ts b/backend/src/services/project/project-dal.ts index 2e50130e1..b3929e530 100644 --- a/backend/src/services/project/project-dal.ts +++ b/backend/src/services/project/project-dal.ts @@ -348,10 +348,11 @@ export const projectDALFactory = (db: TDbClient) => { .where(`${TableName.Groups}.orgId`, dto.orgId) .where(`${TableName.UserGroupMembership}.userId`, dto.actorId) .select(db.ref("id").withSchema(TableName.Groups)); - const userMembershipSubquery = db(TableName.Membership) + const membershipSubQuery = db(TableName.Membership) .where(`${TableName.Membership}.scope`, AccessScope.Project) .where((qb) => { if (dto.actor === ActorType.IDENTITY) { + void qb.where(`${TableName.Membership}.actorIdentityId`, dto.actorId); } else { void qb .where(`${TableName.Membership}.actorUserId`, dto.actorId) @@ -360,11 +361,8 @@ export const projectDALFactory = (db: TDbClient) => { }) .select("scopeProjectId"); - const identityMembershipSubQuery = db(TableName.Membership).where({ identityId: dto.actorId }).select("projectId"); - // Get the SQL strings for the subqueries - const userMembershipSql = userMembershipSubquery.toQuery(); - const identityMembershipSql = identityMembershipSubQuery.toQuery(); + const membershipSQL = membershipSubQuery.toQuery(); const query = db .replicaNode()(TableName.Project) @@ -372,26 +370,15 @@ export const projectDALFactory = (db: TDbClient) => { .select(selectAllTableCols(TableName.Project)) .select(db.raw("COUNT(*) OVER() AS count")) .select<(TProjects & { isMember: boolean; count: number })[]>( - dto.actor === ActorType.USER - ? db.raw( - ` - CASE - WHEN ${TableName.Project}.id IN (?) THEN TRUE - WHEN ${TableName.Project}.id IN (?) THEN TRUE - ELSE FALSE - END as "isMember" - `, - [db.raw(userMembershipSql)] - ) - : db.raw( - ` - CASE - WHEN ${TableName.Project}.id IN (?) THEN TRUE - ELSE FALSE - END as "isMember" - `, - [db.raw(identityMembershipSql)] - ) + db.raw( + ` + CASE + WHEN ${TableName.Project}.id IN (?) THEN TRUE + ELSE FALSE + END as "isMember" + `, + [db.raw(membershipSQL)] + ) ) .limit(limit) .offset(offset); diff --git a/backend/src/services/project/project-queue.ts b/backend/src/services/project/project-queue.ts index e557b7c96..9986f7ab9 100644 --- a/backend/src/services/project/project-queue.ts +++ b/backend/src/services/project/project-queue.ts @@ -1,5 +1,6 @@ /* eslint-disable no-await-in-loop */ import { + AccessScope, IntegrationAuthsSchema, ProjectMembershipRole, ProjectUpgradeStatus, @@ -34,14 +35,14 @@ import { TOrgServiceFactory } from "../org/org-service"; import { TProjectBotDALFactory } from "../project-bot/project-bot-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectKeyDALFactory } from "../project-key/project-key-dal"; -import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; -import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TSecretDALFactory } from "../secret/secret-dal"; import { TSecretVersionDALFactory } from "../secret/secret-version-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TUserDALFactory } from "../user/user-dal"; import { TProjectDALFactory } from "./project-dal"; import { assignWorkspaceKeysToMembers, createProjectKey } from "./project-fns"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; export type TProjectQueueFactory = ReturnType; @@ -55,13 +56,13 @@ type TProjectQueueFactoryDep = { secretApprovalSecretDAL: Pick; projectBotDAL: Pick; orgService: Pick; - projectMembershipDAL: Pick; - projectUserMembershipRoleDAL: Pick; integrationAuthDAL: TIntegrationAuthDALFactory; userDAL: Pick; projectEnvDAL: Pick; projectDAL: Pick; orgDAL: Pick; + membershipUserDAL: TMembershipUserDALFactory; + membershipRoleDAL: TMembershipRoleDALFactory; }; export const projectQueueFactory = ({ @@ -79,8 +80,8 @@ export const projectQueueFactory = ({ orgDAL, projectDAL, orgService, - projectMembershipDAL, - projectUserMembershipRoleDAL + membershipUserDAL, + membershipRoleDAL }: TProjectQueueFactoryDep) => { const upgradeProject = async (dto: TQueueJobTypes["upgrade-project-to-ghost"]["payload"]) => { await queueService.queue(QueueName.UpgradeProjectToGhost, QueueJobs.UpgradeProjectToGhost, dto, { @@ -227,17 +228,16 @@ export const projectQueueFactory = ({ ); // Create a membership for the ghost user - const projectMembership = await projectMembershipDAL.create( + const projectMembership = await membershipUserDAL.create( { - projectId: project.id, - userId: ghostUser.user.id + scopeProjectId: project.id, + scope: AccessScope.Project, + actorUserId: ghostUser.user.id, + scopeOrgId: project.orgId }, tx ); - await projectUserMembershipRoleDAL.create( - { projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin }, - tx - ); + await membershipRoleDAL.create({ membershipId: projectMembership.id, role: ProjectMembershipRole.Admin }, tx); // If a bot already exists, delete it if (existingBot) { @@ -272,8 +272,9 @@ export const projectQueueFactory = ({ for (const key of existingProjectKeys) { const user = await userDAL.findUserEncKeyByUserId(key.receiverId); const [orgMembership] = await orgDAL.findMembership({ - [`${TableName.OrgMembership}.userId` as "userId"]: key.receiverId, - [`${TableName.OrgMembership}.orgId` as "orgId"]: project.orgId + [`${TableName.Membership}.actorUserId` as "actorUserId"]: key.receiverId, + [`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: project.orgId, + [`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization }); if (!user) { diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index d2d92d86a..58b3c5395 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -3,6 +3,7 @@ import { PackRule, unpackRules } from "@casl/ability/extra"; import slugify from "@sindresorhus/slugify"; import { + AccessScope, ActionProjectType, ProjectMembershipRole, ProjectType, @@ -49,11 +50,11 @@ import { TCertificateDALFactory } from "../certificate/certificate-dal"; import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal"; import { expandInternalCa } from "../certificate-authority/certificate-authority-fns"; import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal"; -import { TGroupProjectDALFactory } from "../group-project/group-project-dal"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; -import { TIdentityProjectDALFactory } from "../identity-project/identity-project-dal"; -import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal"; import { TKmsServiceFactory } from "../kms/kms-service"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; +import { TMembershipGroupDALFactory } from "../membership-group/membership-group-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; import { validateMicrosoftTeamsChannelsSchema } from "../microsoft-teams/microsoft-teams-fns"; import { TMicrosoftTeamsIntegrationDALFactory } from "../microsoft-teams/microsoft-teams-integration-dal"; import { TProjectMicrosoftTeamsConfigDALFactory } from "../microsoft-teams/project-microsoft-teams-config-dal"; @@ -65,10 +66,9 @@ import { TPkiCollectionDALFactory } from "../pki-collection/pki-collection-dal"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; -import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; -import { TProjectRoleDALFactory } from "../project-role/project-role-dal"; import { getPredefinedRoles } from "../project-role/project-role-fns"; import { TReminderServiceFactory } from "../reminder/reminder-types"; +import { TRoleDALFactory } from "../role/role-dal"; import { TSecretDALFactory } from "../secret/secret-dal"; import { fnDeleteProjectSecretReminders } from "../secret/secret-fns"; import { ROOT_FOLDER_NAME, TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; @@ -125,7 +125,6 @@ export const DEFAULT_PROJECT_ENVS = [ type TProjectServiceFactoryDep = { projectDAL: TProjectDALFactory; - identityProjectDAL: Pick; projectSshConfigDAL: Pick; projectQueue: TProjectQueueFactory; userDAL: TUserDALFactory; @@ -134,13 +133,11 @@ type TProjectServiceFactoryDep = { secretDAL: Pick; secretV2BridgeDAL: Pick; projectEnvDAL: Pick; - identityOrgMembershipDAL: TIdentityOrgDALFactory; - identityProjectMembershipRoleDAL: Pick; - projectMembershipDAL: Pick< - TProjectMembershipDALFactory, - "create" | "findProjectGhostUser" | "findOne" | "delete" | "findAllProjectMembers" - >; - groupProjectDAL: Pick; + projectMembershipDAL: Pick; + membershipUserDAL: Pick; + membershipGroupDAL: Pick; + membershipIdentityDAL: Pick; + membershipRoleDAL: Pick; projectSlackConfigDAL: Pick< TProjectSlackConfigDALFactory, "findOne" | "transaction" | "updateById" | "create" | "delete" @@ -154,7 +151,6 @@ type TProjectServiceFactoryDep = { TMicrosoftTeamsIntegrationDALFactory, "findById" | "findByIdWithWorkflowIntegrationDetails" >; - projectUserMembershipRoleDAL: Pick; pkiSubscriberDAL: Pick; certificateAuthorityDAL: Pick; certificateDAL: Pick; @@ -172,7 +168,7 @@ type TProjectServiceFactoryDep = { smtpService: Pick; orgDAL: Pick; keyStore: Pick; - projectRoleDAL: Pick; + roleDAL: Pick; kmsService: Pick< TKmsServiceFactory, | "updateProjectSecretManagerKmsKey" @@ -201,20 +197,15 @@ export const projectServiceFactory = ({ orgDAL, userDAL, folderDAL, - identityOrgMembershipDAL, projectMembershipDAL, projectEnvDAL, licenseService, - projectUserMembershipRoleDAL, - projectRoleDAL, certificateAuthorityDAL, certificateDAL, certificateTemplateDAL, pkiCollectionDAL, pkiAlertDAL, pkiSubscriberDAL, - identityProjectDAL, - identityProjectMembershipRoleDAL, sshCertificateAuthorityDAL, sshCertificateAuthoritySecretDAL, sshCertificateDAL, @@ -228,10 +219,13 @@ export const projectServiceFactory = ({ slackIntegrationDAL, microsoftTeamsIntegrationDAL, projectTemplateService, - groupProjectDAL, smtpService, reminderService, - notificationService + notificationService, + membershipIdentityDAL, + membershipUserDAL, + membershipRoleDAL, + roleDAL }: TProjectServiceFactoryDep) => { /* * Create workspace. Make user the admin @@ -345,7 +339,7 @@ export const projectServiceFactory = ({ tx ); } - await projectRoleDAL.insertMany( + await roleDAL.insertMany( projectTemplate.packedRoles.map((role) => ({ ...role, permissions: JSON.stringify(role.permissions), @@ -374,15 +368,17 @@ export const projectServiceFactory = ({ } // Create a membership for the user - const userProjectMembership = await projectMembershipDAL.create( + const userProjectMembership = await membershipUserDAL.create( { - projectId: project.id, - userId: user.id + scopeProjectId: project.id, + actorUserId: user.id, + scope: AccessScope.Project, + scopeOrgId: project.orgId }, tx ); - await projectUserMembershipRoleDAL.create( - { projectMembershipId: userProjectMembership.id, role: ProjectMembershipRole.Admin }, + await membershipRoleDAL.create( + { membershipId: userProjectMembership.id, role: ProjectMembershipRole.Admin }, tx ); } @@ -390,10 +386,11 @@ export const projectServiceFactory = ({ // If the project is being created by an identity, add the identity to the project as an admin else if (actor === ActorType.IDENTITY) { // Find identity org membership - const identityOrgMembership = await identityOrgMembershipDAL.findOne( + const identityOrgMembership = await membershipIdentityDAL.findOne( { - identityId: actorId, - orgId: project.orgId + actorIdentityId: actorId, + scopeOrgId: project.orgId, + scope: AccessScope.Organization }, tx ); @@ -405,17 +402,19 @@ export const projectServiceFactory = ({ }); } - const identityProjectMembership = await identityProjectDAL.create( + const identityProjectMembership = await membershipIdentityDAL.create( { - identityId: actorId, - projectId: project.id + actorIdentityId: actorId, + scopeProjectId: project.id, + scope: AccessScope.Project, + scopeOrgId: project.orgId }, tx ); - await identityProjectMembershipRoleDAL.create( + await membershipRoleDAL.create( { - projectMembershipId: identityProjectMembership.id, + membershipId: identityProjectMembership.id, role: ProjectMembershipRole.Admin }, tx @@ -459,8 +458,11 @@ export const projectServiceFactory = ({ const deletedProject = await projectDAL.transaction(async (tx) => { // delete these so that project custom roles can be deleted in cascade effect // direct deletion of project without these will cause fk error - await projectMembershipDAL.delete({ projectId: project.id }, tx); - await groupProjectDAL.delete({ projectId: project.id }, tx); + // this will clean up all memberships + await membershipUserDAL.delete( + { scopeOrgId: project.orgId, scopeProjectId: project.id, scope: AccessScope.Project }, + tx + ); const delProject = await projectDAL.deleteById(project.id, tx); const projectGhostUser = await projectMembershipDAL.findProjectGhostUser(project.id, tx).catch(() => null); // akhilmhdh: before removing those kms checking any other project uses it @@ -521,13 +523,13 @@ export const projectServiceFactory = ({ // `includeRoles` is specifically used by organization admins when inviting new users to the organizations to avoid looping redundant api calls. ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member); - const customRoles = await projectRoleDAL.find({ + const customRoles = await roleDAL.find({ $in: { projectId: workspaces.map((workspace) => workspace.id) } }); - const workspaceMappedToRoles = groupBy(customRoles, (role) => role.projectId); + const workspaceMappedToRoles = groupBy(customRoles, (role) => role.projectId as string); const workspacesWithRoles = await Promise.all( workspaces.map(async (workspace) => { @@ -1872,7 +1874,7 @@ export const projectServiceFactory = ({ .filter((member) => member.roles.some((role) => role.role === ProjectMembershipRole.Admin)) .map((el) => el.user.email!); if (filteredProjectMembers.length === 0) { - const customRolesWithMemberCreate = await projectRoleDAL.find({ projectId }); + const customRolesWithMemberCreate = await roleDAL.find({ projectId }); const customRoleSlugsCanCreate = customRolesWithMemberCreate .filter((role) => { try { diff --git a/backend/src/services/role/org/org-role-factory.ts b/backend/src/services/role/org/org-role-factory.ts index a630538b8..d7c2fb12d 100644 --- a/backend/src/services/role/org/org-role-factory.ts +++ b/backend/src/services/role/org/org-role-factory.ts @@ -14,6 +14,7 @@ import { BadRequestError } from "@app/lib/errors"; import { TRoleScopeFactory } from "../role-types"; import { isCustomOrgRole } from "@app/services/org/org-role-fns"; +// TODO(simp): missing external group checking type TOrgRoleScopeFactoryDep = { permissionService: Pick; }; diff --git a/backend/src/services/role/role-service.ts b/backend/src/services/role/role-service.ts index 9c26ab9ae..fc7a66c58 100644 --- a/backend/src/services/role/role-service.ts +++ b/backend/src/services/role/role-service.ts @@ -1,4 +1,4 @@ -import { AccessScope, TableName } from "@app/db/schemas"; +import { AccessScope, ActionProjectType, TableName } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; @@ -13,20 +13,34 @@ import { TDeleteRoleDTO, TGetRoleByIdDTO, TGetRoleBySlugDTO, + TGetUserPermissionDTO, TListRoleDTO, TUpdateRoleDTO } from "./role-types"; import { TProjectDALFactory } from "../project/project-dal"; +import { packRules } from "@casl/ability/extra"; +import { requestContext } from "@fastify/request-context"; +import { TIdentityDALFactory } from "../identity/identity-dal"; +import { TUserDALFactory } from "../user/user-dal"; +import { ActorType } from "../auth/auth-type"; type TRoleServiceFactoryDep = { roleDAL: TRoleDALFactory; + identityDAL: Pick; + userDAL: Pick; permissionService: Pick; projectDAL: Pick; }; export type TRoleServiceFactory = ReturnType; -export const roleServiceFactory = ({ roleDAL, permissionService, projectDAL }: TRoleServiceFactoryDep) => { +export const roleServiceFactory = ({ + roleDAL, + permissionService, + projectDAL, + identityDAL, + userDAL +}: TRoleServiceFactoryDep) => { const orgRoleFactory = newOrgRoleFactory({ permissionService }); @@ -186,12 +200,65 @@ export const roleServiceFactory = ({ roleDAL, permissionService, projectDAL }: T return { ...role, [scope.key]: scope.value, permissions: unpackPermissions(role.permissions) }; }; + const getUserPermission = async (dto: TGetUserPermissionDTO) => { + if (dto.scopeData.scope === AccessScope.Organization) { + const { permission, memberships } = await permissionService.getOrgPermission( + dto.permission.type, + dto.permission.id, + dto.permission.orgId, + dto.permission.authMethod, + dto.permission.orgId + ); + return { permissions: packRules(permission.rules), memberships, assumedPrivilegeDetails: undefined }; + } + + if (dto.scopeData.scope === AccessScope.Project) { + const { permission, memberships } = await permissionService.getProjectPermission({ + actor: dto.permission.type, + actorId: dto.permission.id, + actionProjectType: ActionProjectType.Any, + actorAuthMethod: dto.permission.authMethod, + projectId: dto.scopeData.projectId, + actorOrgId: dto.permission.orgId + }); + + const assumedPrivilegeDetailsCtx = requestContext.get("assumedPrivilegeDetails"); + const isAssumingPrivilege = assumedPrivilegeDetailsCtx?.projectId === dto.scopeData.projectId; + const assumedPrivilegeDetails = isAssumingPrivilege + ? { + actorId: assumedPrivilegeDetailsCtx?.actorId, + actorType: assumedPrivilegeDetailsCtx?.actorType, + actorName: "", + actorEmail: "" + } + : undefined; + + if (assumedPrivilegeDetails?.actorType === ActorType.IDENTITY) { + const identityDetails = await identityDAL.findById(assumedPrivilegeDetails.actorId); + if (!identityDetails) + throw new NotFoundError({ message: `Identity with ID ${assumedPrivilegeDetails.actorId} not found` }); + assumedPrivilegeDetails.actorName = identityDetails.name; + } else if (assumedPrivilegeDetails?.actorType === ActorType.USER) { + const userDetails = await userDAL.findById(assumedPrivilegeDetails?.actorId); + if (!userDetails) + throw new NotFoundError({ message: `User with ID ${assumedPrivilegeDetails.actorId} not found` }); + assumedPrivilegeDetails.actorName = `${userDetails?.firstName} ${userDetails?.lastName || ""}`; + assumedPrivilegeDetails.actorEmail = userDetails?.email || ""; + } + + return { permissions: packRules(permission.rules), memberships, assumedPrivilegeDetails }; + } + + throw new BadRequestError({ message: "Invalid scope defined" }); + }; + return { createRole, updateRole, deleteRole, listRoles, getRoleById, - getRoleBySlug + getRoleBySlug, + getUserPermission }; }; diff --git a/backend/src/services/role/role-types.ts b/backend/src/services/role/role-types.ts index 01f694422..522b805eb 100644 --- a/backend/src/services/role/role-types.ts +++ b/backend/src/services/role/role-types.ts @@ -71,3 +71,8 @@ export type TGetRoleBySlugDTO = { slug: string; }; }; + +export type TGetUserPermissionDTO = { + permission: OrgServiceActor; + scopeData: AccessScopeData; +}; diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index ed794a664..bca645836 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -4,6 +4,7 @@ import { AxiosError } from "axios"; import { Knex } from "knex"; import { + AccessScope, ProjectMembershipRole, ProjectType, ProjectUpgradeStatus, @@ -50,7 +51,6 @@ import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectKeyDALFactory } from "../project-key/project-key-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; -import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TReminderServiceFactory } from "../reminder/reminder-types"; import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal"; import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema"; @@ -76,6 +76,8 @@ import { TRemoveSecretReminderDTO, TSyncSecretsDTO } from "./secret-types"; +import { TMembershipDALFactory } from "../membership/membership-dal"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; export type TSecretQueueFactory = ReturnType; type TSecretQueueFactoryDep = { @@ -92,7 +94,9 @@ type TSecretQueueFactoryDep = { projectDAL: TProjectDALFactory; projectBotDAL: TProjectBotDALFactory; projectKeyDAL: Pick; - projectMembershipDAL: Pick; + projectMembershipDAL: Pick; + membershipUserDAL: Pick; + membershipRoleDAL: Pick; smtpService: TSmtpService; secretVersionDAL: TSecretVersionDALFactory; secretBlindIndexDAL: TSecretBlindIndexDALFactory; @@ -110,7 +114,6 @@ type TSecretQueueFactoryDep = { keyStore: Pick; auditLogService: Pick; orgService: Pick; - projectUserMembershipRoleDAL: Pick; resourceMetadataDAL: Pick; folderCommitService: Pick; secretSyncQueue: Pick; @@ -173,14 +176,15 @@ export const secretQueueFactory = ({ keyStore, auditLogService, orgService, - projectUserMembershipRoleDAL, projectKeyDAL, resourceMetadataDAL, secretSyncQueue, folderCommitService, reminderService, eventBusService, - licenseService + licenseService, + membershipUserDAL, + membershipRoleDAL }: TSecretQueueFactoryDep) => { const integrationMeter = opentelemetry.metrics.getMeter("Integrations"); const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", { @@ -1165,17 +1169,16 @@ export const secretQueueFactory = ({ // if project v1 create the project ghost user if (project.version === ProjectVersion.V1) { const ghostUser = await orgService.addGhostUser(project.orgId, tx); - const projectMembership = await projectMembershipDAL.create( + const projectMembership = await membershipUserDAL.create( { - userId: ghostUser.user.id, - projectId: project.id + actorUserId: ghostUser.user.id, + scopeOrgId: project.orgId, + scope: AccessScope.Project, + scopeProjectId: project.id }, tx ); - await projectUserMembershipRoleDAL.create( - { projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin }, - tx - ); + await membershipRoleDAL.create({ membershipId: projectMembership.id, role: ProjectMembershipRole.Admin }, tx); const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({ publicKey: ghostUser.keys.publicKey, diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index 8897a1b3f..bf15500f3 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -1,6 +1,7 @@ import { CronJob } from "cron"; import { + AccessScope, IdentityAuthMethod, OrgMembershipRole, OrgMembershipStatus, @@ -29,7 +30,6 @@ import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { TAuthLoginFactory } from "../auth/auth-login-service"; import { ActorType, AuthMethod, AuthTokenType } from "../auth/auth-type"; -import { TIdentityOrgDALFactory } from "../identity/identity-org-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityTokenAuthDALFactory } from "../identity-token-auth/identity-token-auth-dal"; @@ -37,10 +37,12 @@ import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns"; import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal"; import { TKmsServiceFactory } from "../kms/kms-service"; import { RootKeyEncryptionStrategy } from "../kms/kms-types"; +import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; +import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; import { TMicrosoftTeamsServiceFactory } from "../microsoft-teams/microsoft-teams-service"; import { TOrgDALFactory } from "../org/org-dal"; import { TOrgServiceFactory } from "../org/org-service"; -import { TOrgMembershipDALFactory } from "../org-membership/org-membership-dal"; import { TUserDALFactory } from "../user/user-dal"; import { TUserAliasDALFactory } from "../user-alias/user-alias-dal"; import { UserAliasType } from "../user-alias/user-alias-types"; @@ -64,11 +66,12 @@ type TSuperAdminServiceFactoryDep = { identityDAL: TIdentityDALFactory; identityTokenAuthDAL: TIdentityTokenAuthDALFactory; identityAccessTokenDAL: TIdentityAccessTokenDALFactory; - identityOrgMembershipDAL: TIdentityOrgDALFactory; orgDAL: TOrgDALFactory; - orgMembershipDAL: TOrgMembershipDALFactory; serverCfgDAL: TSuperAdminDALFactory; userDAL: TUserDALFactory; + membershipUserDAL: TMembershipUserDALFactory; + membershipIdentityDAL: TMembershipIdentityDALFactory; + membershipRoleDAL: TMembershipRoleDALFactory; userAliasDAL: Pick; authService: Pick; kmsService: Pick; @@ -127,7 +130,6 @@ export const superAdminServiceFactory = ({ userDAL, identityDAL, orgDAL, - orgMembershipDAL, userAliasDAL, authService, orgService, @@ -137,11 +139,13 @@ export const superAdminServiceFactory = ({ licenseService, identityAccessTokenDAL, identityTokenAuthDAL, - identityOrgMembershipDAL, microsoftTeamsService, invalidateCacheQueue, smtpService, - tokenService + tokenService, + membershipIdentityDAL, + membershipUserDAL, + membershipRoleDAL }: TSuperAdminServiceFactoryDep) => { const initServerCfg = async () => { // TODO(akhilmhdh): bad pattern time less change this later to me itself @@ -589,10 +593,17 @@ export const superAdminServiceFactory = ({ const { identity, credentials } = await identityDAL.transaction(async (tx) => { const newIdentity = await identityDAL.create({ name: "Instance Admin Identity" }, tx); - await identityOrgMembershipDAL.create( + const membership = await membershipIdentityDAL.create( { - identityId: newIdentity.id, - orgId: organization.id, + actorIdentityId: newIdentity.id, + scopeOrgId: organization.id, + scope: AccessScope.Project + }, + tx + ); + await membershipRoleDAL.create( + { + membershipId: membership.id, role: OrgMembershipRole.Admin }, tx @@ -834,18 +845,23 @@ export const superAdminServiceFactory = ({ }); } - await orgDAL.createMembership( + const membership = await orgDAL.createMembership( { - userId: inviteeUser.id, + actorUserId: inviteeUser.id, + scope: AccessScope.Organization, inviteEmail: inviteeEmail, - orgId: org.id, - role: OrgMembershipRole.Admin, + scopeOrgId: org.id, status: inviteeUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited, isActive: true }, tx ); + await membershipRoleDAL.create({ + membershipId: membership.id, + role: OrgMembershipRole.Admin + }); + users.push(inviteeUser); } @@ -914,20 +930,25 @@ export const superAdminServiceFactory = ({ actorType: ActorType ) => { if (actorType === ActorType.USER) { - const orgMembership = await orgMembershipDAL.findById(membershipId); + const orgMembership = await membershipUserDAL.findOne({ + scope: AccessScope.Organization, + id: membershipId, + scopeOrgId: organizationId + }); if (!orgMembership) { throw new NotFoundError({ name: "Organization Membership", message: "Organization membership not found" }); } - if (orgMembership.userId === actorId) { + if (orgMembership.actorUserId === actorId) { throw new BadRequestError({ message: "You cannot remove yourself from the organization from the instance management panel." }); } } - const [organizationMembership] = await orgMembershipDAL.delete({ - orgId: organizationId, + const [organizationMembership] = await membershipUserDAL.delete({ + scopeOrgId: organizationId, + scope: AccessScope.Organization, id: membershipId }); return organizationMembership; @@ -946,25 +967,46 @@ export const superAdminServiceFactory = ({ throw new NotFoundError({ message: `Could not organization with ID "${orgId}"` }); } - const existingOrgMembership = await orgMembershipDAL.findOne({ userId: serverAdmin.id, orgId }); + const existingOrgMembership = await membershipUserDAL.findOne({ + actorUserId: serverAdmin.id, + scopeOrgId: org.id, + scope: AccessScope.Organization + }); if (existingOrgMembership) { throw new BadRequestError({ message: `You are already a part of the organization with ID ${orgId}` }); } - const orgMembership = await orgDAL.createMembership({ - userId: serverAdmin.id, - orgId: org.id, - role: OrgMembershipRole.Admin, - status: OrgMembershipStatus.Accepted, - isActive: true + const orgMembership = await orgDAL.transaction(async (tx) => { + const membership = await orgDAL.createMembership( + { + actorUserId: serverAdmin.id, + scopeOrgId: org.id, + status: OrgMembershipStatus.Accepted, + isActive: true, + scope: AccessScope.Organization + }, + tx + ); + await membershipRoleDAL.create( + { + membershipId: membership.id, + role: OrgMembershipRole.Admin + }, + tx + ); + return membership; }); return orgMembership; }; const resendOrgInvite = async ({ organizationId, membershipId }: TResendOrgInviteDTO, actor: OrgServiceActor) => { - const orgMembership = await orgMembershipDAL.findOne({ id: membershipId, orgId: organizationId }); + const orgMembership = await membershipUserDAL.findOne({ + id: membershipId, + scopeOrgId: organizationId, + scope: AccessScope.Organization + }); if (!orgMembership) { throw new NotFoundError({ name: "Organization Membership", message: "Organization membership not found" }); @@ -976,7 +1018,7 @@ export const superAdminServiceFactory = ({ }); } - if (!orgMembership.userId) { + if (!orgMembership.actorUserId) { throw new NotFoundError({ message: "Cannot find user associated with Org Membership." }); } @@ -984,15 +1026,15 @@ export const superAdminServiceFactory = ({ throw new BadRequestError({ message: "No invite email associated with user." }); } - const org = await orgDAL.findOrgById(orgMembership.orgId); + const org = await orgDAL.findOrgById(orgMembership.scopeOrgId); const appCfg = getConfig(); const serverAdmin = await userDAL.findById(actor.id); const token = await tokenService.createTokenForUser({ type: TokenType.TOKEN_EMAIL_ORG_INVITATION, - userId: orgMembership.userId, - orgId: orgMembership.orgId + userId: orgMembership.actorUserId, + orgId: orgMembership.scopeOrgId }); await smtpService.sendMail({ @@ -1004,7 +1046,7 @@ export const superAdminServiceFactory = ({ inviterUsername: serverAdmin?.email, organizationName: org?.name, email: orgMembership.inviteEmail, - organizationId: orgMembership.orgId, + organizationId: orgMembership.scopeOrgId, token, callback_url: `${appCfg.SITE_URL}/signupinvite` } diff --git a/backend/src/services/user/user-service.ts b/backend/src/services/user/user-service.ts index f5210290c..b54eab8ef 100644 --- a/backend/src/services/user/user-service.ts +++ b/backend/src/services/user/user-service.ts @@ -1,6 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { Knex } from "knex"; +import { AccessScope } from "@app/db/schemas"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { crypto } from "@app/lib/crypto"; @@ -9,12 +10,11 @@ import { logger } from "@app/lib/logger"; import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service"; import { TokenType } from "@app/services/auth-token/auth-token-types"; import { TOrgDALFactory } from "@app/services/org/org-dal"; -import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { AuthMethod, AuthTokenType } from "../auth/auth-type"; import { TGroupProjectDALFactory } from "../group-project/group-project-dal"; -import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; +import { TMembershipUserDALFactory } from "../membership-user/membership-user-dal"; import { TUserAliasDALFactory } from "../user-alias/user-alias-dal"; import { TUserDALFactory } from "./user-dal"; import { TListUserGroupsDTO, TUpdateUserEmailDTO, TUpdateUserMfaDTO } from "./user-types"; @@ -37,9 +37,8 @@ type TUserServiceFactoryDep = { >; groupProjectDAL: Pick; orgDAL: Pick; - orgMembershipDAL: Pick; + membershipUserDAL: Pick; tokenService: Pick; - projectMembershipDAL: Pick; smtpService: Pick; permissionService: TPermissionServiceFactory; userAliasDAL: Pick; @@ -50,8 +49,7 @@ export type TUserServiceFactory = ReturnType; export const userServiceFactory = ({ userDAL, orgDAL, - orgMembershipDAL, - projectMembershipDAL, + membershipUserDAL, groupProjectDAL, tokenService, smtpService, @@ -183,13 +181,19 @@ export const userServiceFactory = ({ }; const checkUserScimRestriction = async (userId: string, tx?: Knex) => { - const userOrgs = await orgMembershipDAL.find({ userId }, { tx }); + const userOrgs = await membershipUserDAL.find( + { + actorUserId: userId, + scope: AccessScope.Organization + }, + { tx } + ); if (userOrgs.length === 0) { return false; } - const orgIds = userOrgs.map((membership) => membership.orgId); + const orgIds = userOrgs.map((membership) => membership.scopeOrgId); const organizations = await orgDAL.find({ $in: { id: orgIds } }, { tx }); return organizations.some((org) => org.scimEnabled); @@ -397,9 +401,10 @@ export const userServiceFactory = ({ }; const getUserProjectFavorites = async (userId: string, orgId: string) => { - const orgMembership = await orgMembershipDAL.findOne({ - userId, - orgId + const orgMembership = await membershipUserDAL.findOne({ + scope: AccessScope.Organization, + actorUserId: userId, + scopeOrgId: orgId }); if (!orgMembership) { @@ -412,9 +417,10 @@ export const userServiceFactory = ({ }; const updateUserProjectFavorites = async (userId: string, orgId: string, projectIds: string[]) => { - const orgMembership = await orgMembershipDAL.findOne({ - userId, - orgId + const orgMembership = await membershipUserDAL.findOne({ + scope: AccessScope.Organization, + actorUserId: userId, + scopeOrgId: orgId }); if (!orgMembership) { @@ -423,18 +429,20 @@ export const userServiceFactory = ({ }); } - const matchingUserProjectMemberships = await projectMembershipDAL.find({ - userId, + const matchingUserProjectMemberships = await membershipUserDAL.find({ + scope: AccessScope.Project, + scopeOrgId: orgId, + actorUserId: userId, $in: { - projectId: projectIds + scopeProjectId: projectIds } }); const memberProjectFavorites = matchingUserProjectMemberships.map( - (projectMembership) => projectMembership.projectId + (projectMembership) => projectMembership.scopeProjectId as string ); - const updatedOrgMembership = await orgMembershipDAL.updateById(orgMembership.id, { + const updatedOrgMembership = await membershipUserDAL.updateById(orgMembership.id, { projectFavorites: memberProjectFavorites });