mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-03 14:26:02 +00:00
feat: complete project identities
This commit is contained in:
@@ -171,7 +171,11 @@ const buildAdminPermissionRules = () => {
|
||||
ProjectPermissionIdentityActions.Delete,
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||
ProjectPermissionIdentityActions.AssumePrivileges
|
||||
ProjectPermissionIdentityActions.AssumePrivileges,
|
||||
ProjectPermissionIdentityActions.GetToken,
|
||||
ProjectPermissionIdentityActions.CreateToken,
|
||||
ProjectPermissionIdentityActions.DeleteToken,
|
||||
ProjectPermissionIdentityActions.RevokeAuth
|
||||
],
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
|
||||
@@ -65,7 +65,11 @@ export enum ProjectPermissionIdentityActions {
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
GrantPrivileges = "grant-privileges",
|
||||
AssumePrivileges = "assume-privileges"
|
||||
AssumePrivileges = "assume-privileges",
|
||||
RevokeAuth = "revoke-auth",
|
||||
CreateToken = "create-token",
|
||||
GetToken = "get-token",
|
||||
DeleteToken = "delete-token"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionMemberActions {
|
||||
|
||||
@@ -757,7 +757,7 @@ export const ORG_IDENTITY_MEMBERSHIP = {
|
||||
LIST_IDENTITY_MEMBERSHIPS: {
|
||||
offset: "The offset to start from. If you enter 10, it will start from the 10th identity membership.",
|
||||
limit: "The number of identity memberships to return.",
|
||||
identityName: "The text string that identity membership names will be filtered by.",
|
||||
identityName: "",
|
||||
roles: "The role slugs to filter identity memberships by."
|
||||
},
|
||||
GET_IDENTITY_MEMBERSHIP_BY_ID: {
|
||||
@@ -765,7 +765,8 @@ export const ORG_IDENTITY_MEMBERSHIP = {
|
||||
},
|
||||
LIST_AVAILABLE_IDENTITIES: {
|
||||
offset: "The offset to start from. If you enter 10, it will start from the 10th identity.",
|
||||
limit: "The number of identities to return."
|
||||
limit: "The number of identities to return.",
|
||||
identityName: "The text string that identity membership names will be filtered by."
|
||||
}
|
||||
} as const;
|
||||
|
||||
@@ -1002,7 +1003,8 @@ export const PROJECT_IDENTITY_MEMBERSHIP = {
|
||||
LIST_AVAILABLE_IDENTITIES: {
|
||||
projectId: "The ID of the project to list available identities for.",
|
||||
offset: "The offset to start from. If you enter 10, it will start from the 10th identity.",
|
||||
limit: "The number of identities to return."
|
||||
limit: "The number of identities to return.",
|
||||
identityName: "The text string that identity membership names will be filtered by."
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { fastifyRequestContext } from "@fastify/request-context";
|
||||
import fastify from "fastify";
|
||||
import { Cluster, Redis } from "ioredis";
|
||||
import { Knex } from "knex";
|
||||
import { monitorEventLoopDelay } from "perf_hooks";
|
||||
|
||||
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
|
||||
@@ -1666,7 +1666,8 @@ export const registerRoutes = async (
|
||||
identityMetadataDAL,
|
||||
licenseService,
|
||||
permissionService,
|
||||
identityDAL: identityV2DAL
|
||||
identityDAL: identityV2DAL,
|
||||
keyStore
|
||||
});
|
||||
|
||||
const identityProjectService = identityProjectServiceFactory({
|
||||
|
||||
@@ -430,7 +430,8 @@ export const registerIdentityOrgMembershipRouter = async (server: FastifyZodProv
|
||||
.max(100)
|
||||
.default(20)
|
||||
.describe(ORG_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit)
|
||||
.optional()
|
||||
.optional(),
|
||||
identityName: z.string().describe(ORG_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.identityName).optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -447,7 +448,8 @@ export const registerIdentityOrgMembershipRouter = async (server: FastifyZodProv
|
||||
},
|
||||
data: {
|
||||
offset: req.query.offset,
|
||||
limit: req.query.limit
|
||||
limit: req.query.limit,
|
||||
identityName: req.query.identityName
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+11
-3
@@ -293,7 +293,7 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
|
||||
temporaryAccessEndTime: z.date().nullable().optional()
|
||||
})
|
||||
),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, projectId: true, orgId: true }).extend({
|
||||
authMethods: z.array(z.string())
|
||||
}),
|
||||
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
||||
@@ -362,7 +362,9 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
|
||||
temporaryAccessEndTime: z.date().nullable().optional()
|
||||
})
|
||||
),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
|
||||
lastLoginAuthMethod: z.string().nullable().optional(),
|
||||
lastLoginTime: z.date().nullable().optional(),
|
||||
identity: IdentitiesSchema.pick({ name: true, id: true, projectId: true, orgId: true }).extend({
|
||||
authMethods: z.array(z.string())
|
||||
}),
|
||||
project: SanitizedProjectSchema.pick({ name: true, id: true })
|
||||
@@ -469,6 +471,11 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
|
||||
.max(100)
|
||||
.default(20)
|
||||
.describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit)
|
||||
.optional(),
|
||||
identityName: z
|
||||
.string()
|
||||
.trim()
|
||||
.describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.identityName)
|
||||
.optional()
|
||||
}),
|
||||
response: {
|
||||
@@ -487,7 +494,8 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
|
||||
},
|
||||
data: {
|
||||
offset: req.query.offset,
|
||||
limit: req.query.limit
|
||||
limit: req.query.limit,
|
||||
identityName: req.query.identityName
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ import { registerIdentityLdapAuthRouter } from "./identity-ldap-auth-router";
|
||||
import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router";
|
||||
import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router";
|
||||
import { registerIdentityOrgMembershipRouter } from "./identity-org-membership-router";
|
||||
import { registerIdentityProjectMembershipRouter } from "./identity-project-router";
|
||||
import { registerIdentityProjectMembershipRouter } from "./identity-project-membership-router";
|
||||
import { registerIdentityRouter } from "./identity-router";
|
||||
import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router";
|
||||
import { registerIdentityTokenAuthRouter } from "./identity-token-auth-router";
|
||||
|
||||
@@ -21,6 +21,8 @@ const sanitizedIdentitySchema = IdentitiesSchema.pick({
|
||||
updatedAt: true,
|
||||
hasDeleteProtection: true
|
||||
}).extend({
|
||||
activeLockoutAuthMethods: z.string().array().optional(),
|
||||
authMethods: z.string().array().optional(),
|
||||
metadata: z
|
||||
.object({
|
||||
key: z.string(),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
@@ -51,7 +52,7 @@ type TIdentityAliCloudAuthServiceFactoryDep = {
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
|
||||
@@ -105,7 +106,18 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
// Generate the token
|
||||
const identityAccessToken = await identityAliCloudAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.ALICLOUD_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -214,16 +226,34 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -300,16 +330,31 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -362,15 +407,31 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
|
||||
const alicloudIdentityAuth = await identityAliCloudAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
return { ...alicloudIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -397,45 +458,61 @@ export const identityAliCloudAuthServiceFactory = ({
|
||||
message: "The identity does not have Alibaba Cloud auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke Alibaba Cloud auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke Alibaba Cloud auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityAliCloudAuth = await identityAliCloudAuthDAL.transaction(async (tx) => {
|
||||
const deletedAliCloudAuth = await identityAliCloudAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.ALICLOUD_AUTH }, tx);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import axios from "axios";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -50,7 +52,7 @@ type TIdentityAwsAuthServiceFactoryDep = {
|
||||
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
|
||||
@@ -179,7 +181,18 @@ export const identityAwsAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.AWS_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -300,16 +313,34 @@ export const identityAwsAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -389,16 +420,31 @@ export const identityAwsAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -453,15 +499,31 @@ export const identityAwsAuthServiceFactory = ({
|
||||
|
||||
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
return { ...awsIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -488,45 +550,60 @@ export const identityAwsAuthServiceFactory = ({
|
||||
message: "The identity does not have aws auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke aws auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke aws auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
const revokedIdentityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => {
|
||||
const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.AWS_AUTH }, tx);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -46,7 +47,7 @@ type TIdentityAzureAuthServiceFactoryDep = {
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
@@ -99,7 +100,18 @@ export const identityAzureAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityAzureAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.AZURE_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -205,16 +217,34 @@ export const identityAzureAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -293,16 +323,31 @@ export const identityAzureAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -359,16 +404,31 @@ export const identityAzureAuthServiceFactory = ({
|
||||
|
||||
const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
return { ...identityAzureAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -395,43 +455,59 @@ export const identityAzureAuthServiceFactory = ({
|
||||
message: "The identity does not have azure auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke azure auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke azure auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityAzureAuth = await identityAzureAuthDAL.transaction(async (tx) => {
|
||||
const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.AZURE_AUTH }, tx);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -44,7 +45,7 @@ type TIdentityGcpAuthServiceFactoryDep = {
|
||||
identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
@@ -139,7 +140,18 @@ export const identityGcpAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityGcpAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.GCP_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -246,16 +258,34 @@ export const identityGcpAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -336,16 +366,31 @@ export const identityGcpAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -404,16 +449,31 @@ export const identityGcpAuthServiceFactory = ({
|
||||
|
||||
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
return { ...identityGcpAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -441,43 +501,58 @@ export const identityGcpAuthServiceFactory = ({
|
||||
message: "The identity does not have gcp auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke gcp auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke gcp auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityGcpAuth = await identityGcpAuthDAL.transaction(async (tx) => {
|
||||
const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.GCP_AUTH }, tx);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import https from "https";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { JwksClient } from "jwks-rsa";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope, TIdentityJwtAuthsUpdate } from "@app/db/schemas";
|
||||
import {
|
||||
AccessScope,
|
||||
ActionProjectType,
|
||||
IdentityAuthMethod,
|
||||
OrganizationActionScope,
|
||||
TIdentityJwtAuthsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -12,6 +18,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -50,7 +57,7 @@ type TIdentityJwtAuthServiceFactoryDep = {
|
||||
identityJwtAuthDAL: TIdentityJwtAuthDALFactory;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
@@ -213,8 +220,22 @@ export const identityJwtAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityJwtAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH, lastLoginTime: new Date() },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -322,16 +343,35 @@ export const identityJwtAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
@@ -435,17 +475,32 @@ export const identityJwtAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -534,17 +589,32 @@ export const identityJwtAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const identityJwtAuth = await identityJwtAuthDAL.findOne({ identityId });
|
||||
|
||||
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
@@ -586,45 +656,60 @@ export const identityJwtAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke jwt auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke jwt auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
const revokedIdentityJwtAuth = await identityJwtAuthDAL.transaction(async (tx) => {
|
||||
const deletedJwtAuth = await identityJwtAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.JWT_AUTH }, tx);
|
||||
|
||||
+145
-66
@@ -1,4 +1,4 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import https from "https";
|
||||
@@ -6,6 +6,7 @@ import RE2 from "re2";
|
||||
|
||||
import {
|
||||
AccessScope,
|
||||
ActionProjectType,
|
||||
IdentityAuthMethod,
|
||||
OrganizationActionScope,
|
||||
TIdentityKubernetesAuthsUpdate
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -69,7 +71,7 @@ type TIdentityKubernetesAuthServiceFactoryDep = {
|
||||
>;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
gatewayService: TGatewayServiceFactory;
|
||||
@@ -448,8 +450,22 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityKubernetesAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH, lastLoginTime: new Date() },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -563,16 +579,34 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -699,16 +733,31 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
@@ -846,16 +895,31 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: identityMembershipOrg.scopeOrgId
|
||||
@@ -906,43 +970,58 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
message: "The identity does not have kubernetes auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke kubernetes auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke kubernetes auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
const revokedIdentityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => {
|
||||
const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.KUBERNETES_AUTH }, tx);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } 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";
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
@@ -61,7 +62,7 @@ type TIdentityLdapAuthServiceFactoryDep = {
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
kmsService: TKmsServiceFactory;
|
||||
identityDAL: Pick<TIdentityDALFactory, "findById" | "findOne">;
|
||||
identityAuthTemplateDAL: TIdentityAuthTemplateDALFactory;
|
||||
@@ -177,8 +178,22 @@ export const identityLdapAuthServiceFactory = ({
|
||||
try {
|
||||
const identityAccessToken = await identityLdapAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH, lastLoginTime: new Date() },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -290,7 +305,7 @@ export const identityLdapAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
@@ -298,10 +313,30 @@ export const identityLdapAuthServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission: projectPermission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(projectPermission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
if (templateId) {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
|
||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||
);
|
||||
@@ -470,7 +505,7 @@ export const identityLdapAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
const { permission: orgPermission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
@@ -478,10 +513,30 @@ export const identityLdapAuthServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission: projectPermission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(projectPermission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Edit,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
if (templateId) {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
|
||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||
);
|
||||
@@ -630,14 +685,31 @@ export const identityLdapAuthServiceFactory = ({
|
||||
|
||||
const ldapIdentityAuth = await identityLdapAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
@@ -650,7 +722,6 @@ export const identityLdapAuthServiceFactory = ({
|
||||
? decryptor({ cipherTextBlob: ldapIdentityAuth.encryptedLdapCaCertificate }).toString()
|
||||
: undefined;
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
return { ...ldapIdentityAuth, orgId: identityMembershipOrg.scopeOrgId, bindDN, bindPass, ldapCaCertificate };
|
||||
};
|
||||
|
||||
@@ -677,45 +748,62 @@ export const identityLdapAuthServiceFactory = ({
|
||||
message: "The identity does not have LDAP Auth attached"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke LDAP auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke LDAP auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityLdapAuth = await identityLdapAuthDAL.transaction(async (tx) => {
|
||||
const [deletedLdapAuth] = await identityLdapAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.LDAP_AUTH }, tx);
|
||||
@@ -824,15 +912,31 @@ export const identityLdapAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const deleted = await keyStore.deleteItems({
|
||||
pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:*`
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import { AxiosError } from "axios";
|
||||
import RE2 from "re2";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
@@ -49,7 +50,7 @@ type TIdentityOciAuthServiceFactoryDep = {
|
||||
identityOciAuthDAL: Pick<TIdentityOciAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
|
||||
@@ -110,8 +111,22 @@ export const identityOciAuthServiceFactory = ({
|
||||
// Generate the token
|
||||
const identityAccessToken = await identityOciAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH, lastLoginTime: new Date() },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -217,15 +232,34 @@ export const identityOciAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
@@ -304,15 +338,31 @@ export const identityOciAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
@@ -367,15 +417,31 @@ export const identityOciAuthServiceFactory = ({
|
||||
|
||||
const ociIdentityAuth = await identityOciAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
return { ...ociIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -402,45 +468,62 @@ export const identityOciAuthServiceFactory = ({
|
||||
message: "The identity does not have OCI auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke OCI auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke OCI auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityOciAuth = await identityOciAuthDAL.transaction(async (tx) => {
|
||||
const deletedOciAuth = await identityOciAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.OCI_AUTH }, tx);
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import axios from "axios";
|
||||
import https from "https";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { JwksClient } from "jwks-rsa";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope, TIdentityOidcAuthsUpdate } from "@app/db/schemas";
|
||||
import {
|
||||
AccessScope,
|
||||
ActionProjectType,
|
||||
IdentityAuthMethod,
|
||||
OrganizationActionScope,
|
||||
TIdentityOidcAuthsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -13,6 +19,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -51,7 +58,7 @@ type TIdentityOidcAuthServiceFactoryDep = {
|
||||
identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
@@ -266,8 +273,22 @@ export const identityOidcAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH, lastLoginTime: new Date() },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -379,16 +400,35 @@ export const identityOidcAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
@@ -481,16 +521,32 @@ export const identityOidcAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
@@ -565,15 +621,31 @@ export const identityOidcAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
|
||||
|
||||
@@ -610,46 +682,62 @@ export const identityOidcAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke oidc auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke oidc auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
|
||||
const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.OIDC_AUTH }, tx);
|
||||
|
||||
@@ -293,7 +293,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity),
|
||||
db.ref("id").as("identityId").withSchema(TableName.Identity),
|
||||
db.ref("name").as("identityName").withSchema(TableName.Identity),
|
||||
db.ref("orgId").as("identityOrgId").withSchema(TableName.Identity),
|
||||
db.ref("projectId").as("identityProjectId").withSchema(TableName.Identity),
|
||||
db.ref("id").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
|
||||
db.ref("lastLoginTime").withSchema(TableName.Membership),
|
||||
db.ref("role").withSchema(TableName.MembershipRole),
|
||||
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
|
||||
db.ref("customRoleId").withSchema(TableName.MembershipRole),
|
||||
@@ -334,6 +338,8 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
parentMapper: ({
|
||||
identityId,
|
||||
identityName,
|
||||
identityOrgId,
|
||||
identityProjectId,
|
||||
uaId,
|
||||
alicloudId,
|
||||
awsId,
|
||||
@@ -346,7 +352,9 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
id,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
projectName
|
||||
projectName,
|
||||
lastLoginAuthMethod,
|
||||
lastLoginTime
|
||||
}) => ({
|
||||
id,
|
||||
identityId,
|
||||
@@ -355,6 +363,8 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
identity: {
|
||||
id: identityId,
|
||||
name: identityName,
|
||||
projectId: identityProjectId,
|
||||
orgId: identityOrgId,
|
||||
authMethods: buildAuthMethods({
|
||||
uaId,
|
||||
alicloudId,
|
||||
@@ -367,6 +377,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
tokenId
|
||||
})
|
||||
},
|
||||
// TODO: scott - not sure why these aren't properly typed?
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
lastLoginAuthMethod,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
lastLoginTime,
|
||||
project: {
|
||||
id: projectId,
|
||||
name: projectName
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
@@ -43,7 +44,7 @@ type TIdentityTlsCertAuthServiceFactoryDep = {
|
||||
>;
|
||||
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
@@ -130,8 +131,22 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
// Generate the token
|
||||
const identityAccessToken = await identityTlsCertAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH, lastLoginTime: new Date() },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
@@ -237,15 +252,34 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
@@ -329,15 +363,31 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
@@ -404,15 +454,32 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
|
||||
const identityAuth = await identityTlsCertAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: identityMembershipOrg.scopeOrgId
|
||||
@@ -448,44 +515,61 @@ export const identityTlsCertAuthServiceFactory = ({
|
||||
message: "The identity does not have TLS Certificate auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission, memberships } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const shouldUseNewPrivilegeSystem = Boolean(memberships?.[0]?.shouldUseNewPrivilegeSystem);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke TLS Certificate auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission, memberships } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const shouldUseNewPrivilegeSystem = Boolean(memberships?.[0]?.shouldUseNewPrivilegeSystem);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke TLS Certificate auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityTlsCertAuth = await identityTlsCertAuthDAL.transaction(async (tx) => {
|
||||
const deletedTlsCertAuth = await identityTlsCertAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.TLS_CERT_AUTH }, tx);
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope, TableName } from "@app/db/schemas";
|
||||
import {
|
||||
AccessScope,
|
||||
ActionProjectType,
|
||||
IdentityAuthMethod,
|
||||
OrganizationActionScope,
|
||||
TableName
|
||||
} from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -8,6 +14,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import {
|
||||
@@ -49,7 +56,7 @@ type TIdentityTokenAuthServiceFactoryDep = {
|
||||
TIdentityAccessTokenDALFactory,
|
||||
"create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete"
|
||||
>;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
};
|
||||
@@ -101,15 +108,34 @@ export const identityTokenAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
@@ -187,15 +213,31 @@ export const identityTokenAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
@@ -251,15 +293,31 @@ export const identityTokenAuthServiceFactory = ({
|
||||
|
||||
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
return { ...identityTokenAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
@@ -291,44 +349,61 @@ export const identityTokenAuthServiceFactory = ({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke token auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke token auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const revokedIdentityTokenAuth = await identityTokenAuthDAL.transaction(async (tx) => {
|
||||
const deletedTokenAuth = await identityTokenAuthDAL.delete({ identityId }, tx);
|
||||
await identityAccessTokenDAL.delete({
|
||||
@@ -367,45 +442,61 @@ export const identityTokenAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create token for identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.CreateToken,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create token for identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
|
||||
|
||||
const identity = await identityDAL.findById(identityTokenAuth.identityId);
|
||||
@@ -413,7 +504,18 @@ export const identityTokenAuthServiceFactory = ({
|
||||
|
||||
const identityAccessToken = await identityTokenAuthDAL.transaction(async (tx) => {
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{ lastLoginAuthMethod: IdentityAuthMethod.TOKEN_AUTH, lastLoginTime: new Date() },
|
||||
tx
|
||||
);
|
||||
@@ -478,15 +580,32 @@ export const identityTokenAuthServiceFactory = ({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const tokens = await identityAccessTokenDAL.find(
|
||||
{
|
||||
@@ -531,43 +650,60 @@ export const identityTokenAuthServiceFactory = ({
|
||||
message: "The identity does not have Token Auth"
|
||||
});
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to update token for identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.CreateToken,
|
||||
subject(ProjectPermissionSub.Identity, { identityId: identityMembershipOrg.identity.id })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to update token for identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const [token] = await identityAccessTokenDAL.update(
|
||||
{
|
||||
authMethod: IdentityAuthMethod.TOKEN_AUTH,
|
||||
@@ -603,24 +739,43 @@ export const identityTokenAuthServiceFactory = ({
|
||||
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityAccessToken.identityId, isActorSuperAdmin);
|
||||
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
scope: AccessScope.Organization
|
||||
const identityOrgMembership = await membershipIdentityDAL.getIdentityById({
|
||||
scopeData: {
|
||||
scope: AccessScope.Organization,
|
||||
orgId: actorOrgId
|
||||
},
|
||||
identityId: identityAccessToken.identityId
|
||||
});
|
||||
|
||||
if (!identityOrgMembership) {
|
||||
throw new NotFoundError({ message: `Failed to find identity with ID ${identityAccessToken.identityId}` });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityOrgMembership.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityOrgMembership.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityOrgMembership.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId: identityOrgMembership.identity.id })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityOrgMembership.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const [revokedToken] = await identityAccessTokenDAL.update(
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
|
||||
import { AccessScope, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { AccessScope, ActionProjectType, IdentityAuthMethod, OrganizationActionScope } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
validatePrivilegeChangeOperation
|
||||
} from "@app/ee/services/permission/permission-fns";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
@@ -51,7 +52,7 @@ type TIdentityUaServiceFactoryDep = {
|
||||
identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory;
|
||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||
membershipIdentityDAL: TMembershipIdentityDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findById">;
|
||||
keyStore: Pick<
|
||||
@@ -231,7 +232,18 @@ export const identityUaServiceFactory = ({
|
||||
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx);
|
||||
await membershipIdentityDAL.update(
|
||||
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id },
|
||||
identity.projectId
|
||||
? {
|
||||
scope: AccessScope.Project,
|
||||
scopeOrgId: identity.orgId,
|
||||
scopeProjectId: identity.projectId,
|
||||
actorIdentityId: identity.id
|
||||
}
|
||||
: {
|
||||
scope: AccessScope.Organization,
|
||||
scopeOrgId: identity.orgId,
|
||||
actorIdentityId: identity.id
|
||||
},
|
||||
{
|
||||
lastLoginAuthMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
|
||||
lastLoginTime: new Date()
|
||||
@@ -351,16 +363,35 @@ export const identityUaServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Create,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => {
|
||||
@@ -467,15 +498,31 @@ export const identityUaServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
|
||||
const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => {
|
||||
@@ -554,15 +601,31 @@ export const identityUaServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
return { ...uaIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -590,43 +653,59 @@ export const identityUaServiceFactory = ({
|
||||
if (identityMembershipOrg.identity.orgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke universal auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.RevokeAuth,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke universal auth of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.RevokeAuth,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
const revokedIdentityUniversalAuth = await identityUaDAL.transaction(async (tx) => {
|
||||
const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx);
|
||||
return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.scopeOrgId };
|
||||
@@ -662,43 +741,61 @@ export const identityUaServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create client secret for identity.",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.CreateToken,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to create client secret for identity.",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.CreateToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
const appCfg = getConfig();
|
||||
const clientSecret = crypto.randomBytes(32).toString("hex");
|
||||
const clientSecretHash = await crypto.hashing().createHash(clientSecret, appCfg.SALT_ROUNDS);
|
||||
@@ -748,43 +845,59 @@ export const identityUaServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to get identity client secret with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.GetToken,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to get identity client secret with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
|
||||
const identityUniversalAuth = await identityUaDAL.findOne({
|
||||
identityId
|
||||
});
|
||||
@@ -828,43 +941,57 @@ export const identityUaServiceFactory = ({
|
||||
const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id });
|
||||
if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to read identity client secret of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.GetToken,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid)
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to read identity client secret of identity with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.GetToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
return { ...clientSecret, identityId, orgId: identityMembershipOrg.scopeOrgId };
|
||||
};
|
||||
|
||||
@@ -900,45 +1027,63 @@ export const identityUaServiceFactory = ({
|
||||
const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id });
|
||||
if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.DeleteToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid) {
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke identity client secret with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.DeleteToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
}
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.DeleteToken,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionIdentityActions.Delete,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission({
|
||||
actor: ActorType.IDENTITY,
|
||||
actorId: identityMembershipOrg.identity.id,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
scope: OrganizationActionScope.Any
|
||||
});
|
||||
|
||||
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(identityMembershipOrg.scopeOrgId);
|
||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.DeleteToken,
|
||||
OrgPermissionSubjects.Identity,
|
||||
permission,
|
||||
rolePermission
|
||||
);
|
||||
if (!permissionBoundary.isValid) {
|
||||
throw new PermissionBoundaryError({
|
||||
message: constructPermissionErrorMessage(
|
||||
"Failed to revoke identity client secret with more privileged role",
|
||||
shouldUseNewPrivilegeSystem,
|
||||
OrgPermissionIdentityActions.DeleteToken,
|
||||
OrgPermissionSubjects.Identity
|
||||
),
|
||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||
});
|
||||
}
|
||||
}
|
||||
const updatedClientSecret = await identityUaClientSecretDAL.updateById(clientSecretId, {
|
||||
isClientSecretRevoked: true
|
||||
});
|
||||
@@ -971,16 +1116,31 @@ export const identityUaServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
if (identityMembershipOrg.identity.projectId) {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actionProjectType: ActionProjectType.Any,
|
||||
actor,
|
||||
actorId,
|
||||
projectId: identityMembershipOrg.identity.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
subject(ProjectPermissionSub.Identity, { identityId })
|
||||
);
|
||||
} else {
|
||||
const { permission } = await permissionService.getOrgPermission({
|
||||
scope: OrganizationActionScope.Any,
|
||||
actor,
|
||||
actorId,
|
||||
orgId: identityMembershipOrg.scopeOrgId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||
}
|
||||
const deleted = await keyStore.deleteItems({
|
||||
pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.UNIVERSAL_AUTH}:*`
|
||||
});
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
|
||||
export const getIdentityActiveLockoutAuthMethods = async (
|
||||
identityId: string,
|
||||
keyStore: Pick<TKeyStoreFactory, "getKeysByPattern" | "getItem">
|
||||
) => {
|
||||
const activeLockouts = await keyStore.getKeysByPattern(`lockout:identity:${identityId}:*`);
|
||||
|
||||
const activeLockoutAuthMethods = new Set<string>();
|
||||
for await (const key of activeLockouts) {
|
||||
const parts = key.split(":");
|
||||
if (parts.length > 3) {
|
||||
const lockoutRaw = await keyStore.getItem(key);
|
||||
if (lockoutRaw) {
|
||||
const lockout = JSON.parse(lockoutRaw) as { lockedOut: boolean };
|
||||
if (lockout.lockedOut) {
|
||||
activeLockoutAuthMethods.add(parts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(activeLockoutAuthMethods);
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { getIdentityActiveLockoutAuthMethods } from "@app/services/identity-v2/identity-fns";
|
||||
|
||||
import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal";
|
||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||
@@ -24,6 +26,7 @@ type TScopedIdentityV2ServiceFactoryDep = {
|
||||
membershipIdentityDAL: TMembershipIdentityDALFactory;
|
||||
membershipRoleDAL: TMembershipRoleDALFactory;
|
||||
identityMetadataDAL: TIdentityMetadataDALFactory;
|
||||
keyStore: Pick<TKeyStoreFactory, "getKeysByPattern" | "getItem">;
|
||||
};
|
||||
|
||||
export type TScopedIdentityV2ServiceFactory = ReturnType<typeof identityV2ServiceFactory>;
|
||||
@@ -34,7 +37,8 @@ export const identityV2ServiceFactory = ({
|
||||
licenseService,
|
||||
membershipIdentityDAL,
|
||||
membershipRoleDAL,
|
||||
identityMetadataDAL
|
||||
identityMetadataDAL,
|
||||
keyStore
|
||||
}: TScopedIdentityV2ServiceFactoryDep) => {
|
||||
const orgFactory = newOrgIdentityFactory({
|
||||
permissionService
|
||||
@@ -217,7 +221,9 @@ export const identityV2ServiceFactory = ({
|
||||
const identity = await identityDAL.getIdentityById(dto.scopeData, dto.selector.identityId);
|
||||
if (!identity) throw new NotFoundError({ message: `Identity with id ${dto.selector.identityId} not found` });
|
||||
|
||||
return { identity };
|
||||
const activeLockoutAuthMethods = await getIdentityActiveLockoutAuthMethods(identity.id, keyStore);
|
||||
|
||||
return { identity: { ...identity, activeLockoutAuthMethods } };
|
||||
};
|
||||
|
||||
const listIdentities = async (dto: TListIdentityV2DTO) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { AccessScope, ActionProjectType } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
@@ -47,7 +47,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
ProjectPermissionSub.Identity
|
||||
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
|
||||
);
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Delete,
|
||||
ProjectPermissionSub.Identity
|
||||
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
|
||||
);
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
ProjectPermissionSub.Identity
|
||||
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -159,6 +159,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
.join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.whereNull(`${TableName.Identity}.projectId`)
|
||||
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
|
||||
.select(
|
||||
selectAllTableCols(TableName.Membership),
|
||||
@@ -404,6 +405,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`)
|
||||
.whereNull(`${TableName.Identity}.projectId`)
|
||||
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||
.orderBy(
|
||||
|
||||
@@ -11,6 +11,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
import { getIdentityActiveLockoutAuthMethods } from "@app/services/identity-v2/identity-fns";
|
||||
|
||||
import { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
|
||||
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
|
||||
@@ -220,6 +221,13 @@ export const identityServiceFactory = ({
|
||||
}
|
||||
|
||||
const identityDetails = await identityDAL.findById(id);
|
||||
|
||||
console.log("has project id", identityDetails);
|
||||
|
||||
if (identityDetails.projectId) {
|
||||
throw new BadRequestError({ message: `Identity is managed by project` });
|
||||
}
|
||||
|
||||
const identity = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity =
|
||||
identityDetails.orgId === actorOrgId && (name || hasDeleteProtection)
|
||||
@@ -286,25 +294,11 @@ export const identityServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const activeLockouts = await keyStore.getKeysByPattern(`lockout:identity:${id}:*`);
|
||||
|
||||
const activeLockoutAuthMethods = new Set<string>();
|
||||
for await (const key of activeLockouts) {
|
||||
const parts = key.split(":");
|
||||
if (parts.length > 3) {
|
||||
const lockoutRaw = await keyStore.getItem(key);
|
||||
if (lockoutRaw) {
|
||||
const lockout = JSON.parse(lockoutRaw) as { lockedOut: boolean };
|
||||
if (lockout.lockedOut) {
|
||||
activeLockoutAuthMethods.add(parts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const activeLockoutAuthMethods = await getIdentityActiveLockoutAuthMethods(id, keyStore);
|
||||
|
||||
return {
|
||||
...identity,
|
||||
identity: { ...identity.identity, activeLockoutAuthMethods: Array.from(activeLockoutAuthMethods) }
|
||||
identity: { ...identity.identity, activeLockoutAuthMethods }
|
||||
};
|
||||
};
|
||||
|
||||
@@ -340,6 +334,10 @@ export const identityServiceFactory = ({
|
||||
if (identityOrgMembership.identity.hasDeleteProtection)
|
||||
throw new BadRequestError({ message: "Identity has delete protection" });
|
||||
|
||||
if (identityOrgMembership.identity.projectId) {
|
||||
throw new BadRequestError({ message: `Identity is managed by project` });
|
||||
}
|
||||
|
||||
if (identityOrgMembership.identity.orgId === actorOrgId) {
|
||||
const deletedIdentity = await identityDAL.deleteById(id);
|
||||
await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.scopeOrgId);
|
||||
|
||||
@@ -371,8 +371,10 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
const listAvailableIdentities = async (scopeData: AccessScopeData, rootOrgId: string) => {
|
||||
// TODO (akhil/scott): need to implement filters
|
||||
|
||||
try {
|
||||
const identitesConnectedToOrg = db
|
||||
const identitiesConnectedToOrg = db
|
||||
.replicaNode()(TableName.Membership)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
|
||||
@@ -389,6 +391,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`)
|
||||
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||
.whereNull(`${TableName.Identity}.projectId`)
|
||||
.where((qb) => {
|
||||
// if sub org pick from root and if project pick from org of project
|
||||
if (scopeData.scope === AccessScope.Organization) {
|
||||
@@ -397,7 +400,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
|
||||
void qb.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId);
|
||||
}
|
||||
})
|
||||
.whereNotIn(`${TableName.Membership}.actorIdentityId`, identitesConnectedToOrg)
|
||||
.whereNotIn(`${TableName.Membership}.actorIdentityId`, identitiesConnectedToOrg)
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.Identity),
|
||||
db.ref("name").withSchema(TableName.Identity),
|
||||
|
||||
@@ -340,7 +340,8 @@ export const membershipIdentityServiceFactory = ({
|
||||
|
||||
await factory.onListMembershipIdentityGuard(dto);
|
||||
|
||||
if (dto.permission.rootOrgId === dto.permission.orgId) return { identities: [] };
|
||||
if (scopeData.scope !== AccessScope.Project && dto.permission.rootOrgId === dto.permission.orgId)
|
||||
return { identities: [] };
|
||||
|
||||
const identities = await membershipIdentityDAL.listAvailableIdentities(dto.scopeData, dto.permission.rootOrgId);
|
||||
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { AccessScope, ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||
import {
|
||||
@@ -119,7 +119,7 @@ export const newProjectMembershipIdentityFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Edit,
|
||||
ProjectPermissionSub.Identity
|
||||
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
|
||||
);
|
||||
|
||||
const identityDetails = await identityDAL.findById(dto.selector.identityId);
|
||||
@@ -168,7 +168,7 @@ export const newProjectMembershipIdentityFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Delete,
|
||||
ProjectPermissionSub.Identity
|
||||
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
|
||||
);
|
||||
|
||||
const identityDetails = await identityDAL.findById(dto.selector.identityId);
|
||||
@@ -210,7 +210,7 @@ export const newProjectMembershipIdentityFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionIdentityActions.Read,
|
||||
ProjectPermissionSub.Identity
|
||||
subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user