diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index 6082e382f..9f13d0ddd 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -559,6 +559,8 @@ export const authLoginServiceFactory = ({ const membershipRole = (await membershipRoleDAL.findOne({ membershipId: orgMembership.id })).role; + let rootOrg = selectedOrg; + if (isSubOrganization) { if (!selectedOrg.rootOrgId) { throw new BadRequestError({ @@ -566,6 +568,13 @@ export const authLoginServiceFactory = ({ }); } + rootOrg = await orgDAL.findById(selectedOrg.rootOrgId); + if (!rootOrg) { + throw new BadRequestError({ + message: "Invalid sub-organization" + }); + } + // Check user membership in the root organization const rootOrgMembership = await membershipUserDAL.findOne({ actorUserId: user.id, @@ -582,18 +591,18 @@ export const authLoginServiceFactory = ({ } if ( - selectedOrg.authEnforced && + rootOrg.authEnforced && !isAuthMethodSaml(decodedToken.authMethod) && decodedToken.authMethod !== AuthMethod.OIDC && - !(selectedOrg.bypassOrgAuthEnabled && membershipRole === OrgMembershipRole.Admin) + !(rootOrg.bypassOrgAuthEnabled && membershipRole === OrgMembershipRole.Admin) ) { throw new BadRequestError({ message: "Login with the auth method required by your organization." }); } - if (selectedOrg.googleSsoAuthEnforced && decodedToken.authMethod !== AuthMethod.GOOGLE) { - const canBypass = selectedOrg.bypassOrgAuthEnabled && membershipRole === OrgMembershipRole.Admin; + if (rootOrg.googleSsoAuthEnforced && decodedToken.authMethod !== AuthMethod.GOOGLE) { + const canBypass = rootOrg.bypassOrgAuthEnabled && membershipRole === OrgMembershipRole.Admin; if (!canBypass) { throw new ForbiddenRequestError({ @@ -604,13 +613,13 @@ export const authLoginServiceFactory = ({ } if (decodedToken.authMethod === AuthMethod.GOOGLE) { - await orgDAL.updateById(selectedOrg.id, { + await orgDAL.updateById(rootOrg.id, { googleSsoAuthLastUsed: new Date() }); } - const shouldCheckMfa = selectedOrg.enforceMfa || user.isMfaEnabled; - const orgMfaMethod = selectedOrg.enforceMfa ? (selectedOrg.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined; + const shouldCheckMfa = rootOrg.enforceMfa || user.isMfaEnabled; + const orgMfaMethod = rootOrg.enforceMfa ? (rootOrg.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined; const userMfaMethod = user.isMfaEnabled ? (user.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined; const mfaMethod = orgMfaMethod ?? userMfaMethod; @@ -644,7 +653,7 @@ export const authLoginServiceFactory = ({ user, userAgent, ip: ipAddress, - organizationId: isSubOrganization ? selectedOrg.rootOrgId || "" : organizationId, + organizationId: isSubOrganization ? rootOrg.id : organizationId, subOrganizationId: isSubOrganization ? organizationId : undefined, isMfaVerified: decodedToken.isMfaVerified, mfaMethod: decodedToken.mfaMethod @@ -652,8 +661,8 @@ export const authLoginServiceFactory = ({ // In the event of this being a break-glass request (non-saml / non-oidc, when either is enforced) if ( - selectedOrg.authEnforced && - selectedOrg.bypassOrgAuthEnabled && + rootOrg.authEnforced && + rootOrg.bypassOrgAuthEnabled && !isAuthMethodSaml(decodedToken.authMethod) && decodedToken.authMethod !== AuthMethod.OIDC && decodedToken.authMethod !== AuthMethod.GOOGLE 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 c74326548..79b6ef525 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 @@ -85,23 +85,6 @@ export const identityAliCloudAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { const requestUrl = new URL("https://sts.aliyuncs.com"); @@ -124,6 +107,30 @@ export const identityAliCloudAuthServiceFactory = ({ }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + // Generate the token const identityAccessToken = await identityAliCloudAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( 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 fb95b95cf..b102362b3 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 @@ -123,23 +123,6 @@ export const identityAwsAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { const headers: TAwsGetCallerIdentityHeaders = JSON.parse(Buffer.from(iamRequestHeaders, "base64").toString()); const body: string = Buffer.from(iamRequestBody, "base64").toString(); @@ -207,6 +190,30 @@ export const identityAwsAuthServiceFactory = ({ } } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( identity.projectId 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 0157cc281..c205d76c8 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 @@ -79,23 +79,6 @@ export const identityAzureAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { const azureIdentity = await validateAzureIdentity({ tenantId: identityAzureAuth.tenantId, @@ -119,6 +102,30 @@ export const identityAzureAuthServiceFactory = ({ } } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityAzureAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( identity.projectId 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 b7db92c3a..471f5a542 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 @@ -77,23 +77,6 @@ export const identityGcpAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { let gcpIdentityDetails: TGcpIdentityDetails; switch (identityGcpAuth.type) { @@ -160,6 +143,30 @@ export const identityGcpAuthServiceFactory = ({ }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityGcpAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( identity.projectId 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 3ac0dddaa..2d68f10dd 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 @@ -91,22 +91,6 @@ export const identityJwtAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } try { const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, @@ -239,6 +223,30 @@ export const identityJwtAuthServiceFactory = ({ }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityJwtAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( identity.projectId 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 237687982..5d4021fef 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 @@ -203,23 +203,6 @@ export const identityKubernetesAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, @@ -480,6 +463,30 @@ export const identityKubernetesAuthServiceFactory = ({ }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityKubernetesAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( identity.projectId 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 1b82837b7..92b754e5a 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 @@ -172,23 +172,6 @@ export const identityLdapAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - const plan = await licenseService.getPlan(identity.orgId); if (!plan.ldap) { throw new BadRequestError({ @@ -196,6 +179,29 @@ export const identityLdapAuthServiceFactory = ({ "Failed to login to identity due to plan restriction. Upgrade plan to login to use LDAP authentication." }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } try { const identityAccessToken = await identityLdapAuthDAL.transaction(async (tx) => { 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 e17aa6ce6..18f01e83e 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 @@ -81,23 +81,6 @@ export const identityOciAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { // Validate OCI host format. Ensures that the host is in "identity..oraclecloud.com" format. if (!headers.host || !new RE2("^identity\\.([a-z]{2}-[a-z]+-[1-9])\\.oraclecloud\\.com$").test(headers.host)) { @@ -130,6 +113,30 @@ export const identityOciAuthServiceFactory = ({ }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + // Generate the token const identityAccessToken = await identityOciAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( 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 06b385523..1e033d662 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 @@ -92,23 +92,6 @@ export const identityOidcAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, @@ -308,6 +291,30 @@ export const identityOidcAuthServiceFactory = ({ }); } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( identity.projectId 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 38b2bee73..bfa4db14d 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 @@ -90,23 +90,6 @@ export const identityTlsCertAuthServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { const { decryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.Organization, @@ -153,6 +136,30 @@ export const identityTlsCertAuthServiceFactory = ({ } } + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + // Generate the token const identityAccessToken = await identityTlsCertAuthDAL.transaction(async (tx) => { await membershipIdentityDAL.update( 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 f36e8df5c..bda66e6e1 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 @@ -514,16 +514,23 @@ export const identityTokenAuthServiceFactory = ({ if (!isSubOrgIdentity) { const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; } } diff --git a/backend/src/services/identity-ua/identity-ua-service.ts b/backend/src/services/identity-ua/identity-ua-service.ts index 74d5c7d0f..d47b93049 100644 --- a/backend/src/services/identity-ua/identity-ua-service.ts +++ b/backend/src/services/identity-ua/identity-ua-service.ts @@ -96,23 +96,6 @@ export const identityUaServiceFactory = ({ // If the identity is a sub-org identity, then the scope is always the org.id, and if it's a root org identity, then we need to resolve the scope if a subOrganizationName is specified let subOrganizationId = isSubOrgIdentity ? org.id : null; - if (subOrganizationName) { - if (!isSubOrgIdentity) { - const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); - - if (subOrg) { - const subOrgMembership = await membershipIdentityDAL.findOne({ - scope: AccessScope.Organization, - actorIdentityId: identity.id, - scopeOrgId: subOrg.id - }); - if (subOrgMembership) { - subOrganizationId = subOrg.id; - } - } - } - } - try { checkIPAgainstBlocklist({ ipAddress: ip, @@ -251,6 +234,30 @@ export const identityUaServiceFactory = ({ accessTokenMaxTTL: 1000000000 }; + if (subOrganizationName) { + if (!isSubOrgIdentity) { + const subOrg = await orgDAL.findOne({ rootOrgId: org.id, slug: subOrganizationName }); + + if (!subOrg) { + throw new NotFoundError({ message: `Sub organization with name ${subOrganizationName} not found` }); + } + + const subOrgMembership = await membershipIdentityDAL.findOne({ + scope: AccessScope.Organization, + actorIdentityId: identity.id, + scopeOrgId: subOrg.id + }); + + if (!subOrgMembership) { + throw new UnauthorizedError({ + message: `Identity not authorized to access sub organization ${subOrganizationName}` + }); + } + + subOrganizationId = subOrg.id; + } + } + const identityAccessToken = await identityUaDAL.transaction(async (tx) => { const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx); await membershipIdentityDAL.update( diff --git a/frontend/src/hooks/api/projects/query-keys.tsx b/frontend/src/hooks/api/projects/query-keys.tsx index 48830f9e2..6f8ebf970 100644 --- a/frontend/src/hooks/api/projects/query-keys.tsx +++ b/frontend/src/hooks/api/projects/query-keys.tsx @@ -3,14 +3,16 @@ import { WorkflowIntegrationPlatform } from "../workflowIntegrations/types"; import { TListProjectIdentitiesDTO, TSearchProjectsDTO } from "./types"; export const projectKeys = { - getProjectById: (projectId: string) => ["projects", { projectId }] as const, + allProjectQueries: () => ["projects"] as const, + getProjectById: (projectId: string) => + [...projectKeys.allProjectQueries(), { projectId }] as const, getProjectSecrets: (projectId: string) => [{ projectId }, "project-secrets"] as const, getProjectIndexStatus: (projectId: string) => [{ projectId }, "project-index-status"] as const, getProjectUpgradeStatus: (projectId: string) => [{ projectId }, "project-upgrade-status"], getProjectMemberships: (orgId: string) => [{ orgId }, "project-memberships"], getProjectAuthorization: (projectId: string) => [{ projectId }, "project-authorizations"], getProjectIntegrations: (projectId: string) => [{ projectId }, "project-integrations"], - getAllUserProjects: () => ["projects"] as const, + getAllUserProjects: () => [...projectKeys.allProjectQueries()] as const, getProjectAuditLogs: (projectId: string) => [{ projectId }, "project-audit-logs"] as const, getProjectUsers: ( projectId: string, diff --git a/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx b/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx index 1ed5cd0c3..950f606a6 100644 --- a/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx +++ b/frontend/src/layouts/OrganizationLayout/components/NavBar/Navbar.tsx @@ -221,12 +221,12 @@ export const Navbar = () => { SecurityClient.setToken(token); SecurityClient.setProviderAuthToken(""); queryClient.removeQueries({ queryKey: authKeys.getAuthToken }); - queryClient.removeQueries({ queryKey: projectKeys.getAllUserProjects() }); queryClient.removeQueries({ queryKey: subOrgQuery.queryKey }); await queryClient.refetchQueries({ queryKey: authKeys.getAuthToken }); await navigateUserToOrg({ navigate, organizationId, navigateTo }); + queryClient.removeQueries({ queryKey: projectKeys.allProjectQueries() }); if (onSuccess) { await onSuccess();