feat: complete project identities

This commit is contained in:
Scott Wilson
2025-11-13 12:52:38 +05:30
committed by =
parent 1fe635678e
commit b32544e76c
111 changed files with 4820 additions and 2113 deletions
@@ -171,7 +171,11 @@ const buildAdminPermissionRules = () => {
ProjectPermissionIdentityActions.Delete, ProjectPermissionIdentityActions.Delete,
ProjectPermissionIdentityActions.Read, ProjectPermissionIdentityActions.Read,
ProjectPermissionIdentityActions.GrantPrivileges, ProjectPermissionIdentityActions.GrantPrivileges,
ProjectPermissionIdentityActions.AssumePrivileges ProjectPermissionIdentityActions.AssumePrivileges,
ProjectPermissionIdentityActions.GetToken,
ProjectPermissionIdentityActions.CreateToken,
ProjectPermissionIdentityActions.DeleteToken,
ProjectPermissionIdentityActions.RevokeAuth
], ],
ProjectPermissionSub.Identity ProjectPermissionSub.Identity
); );
@@ -65,7 +65,11 @@ export enum ProjectPermissionIdentityActions {
Edit = "edit", Edit = "edit",
Delete = "delete", Delete = "delete",
GrantPrivileges = "grant-privileges", GrantPrivileges = "grant-privileges",
AssumePrivileges = "assume-privileges" AssumePrivileges = "assume-privileges",
RevokeAuth = "revoke-auth",
CreateToken = "create-token",
GetToken = "get-token",
DeleteToken = "delete-token"
} }
export enum ProjectPermissionMemberActions { export enum ProjectPermissionMemberActions {
+5 -3
View File
@@ -757,7 +757,7 @@ export const ORG_IDENTITY_MEMBERSHIP = {
LIST_IDENTITY_MEMBERSHIPS: { LIST_IDENTITY_MEMBERSHIPS: {
offset: "The offset to start from. If you enter 10, it will start from the 10th identity membership.", 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.", 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." roles: "The role slugs to filter identity memberships by."
}, },
GET_IDENTITY_MEMBERSHIP_BY_ID: { GET_IDENTITY_MEMBERSHIP_BY_ID: {
@@ -765,7 +765,8 @@ export const ORG_IDENTITY_MEMBERSHIP = {
}, },
LIST_AVAILABLE_IDENTITIES: { LIST_AVAILABLE_IDENTITIES: {
offset: "The offset to start from. If you enter 10, it will start from the 10th identity.", 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; } as const;
@@ -1002,7 +1003,8 @@ export const PROJECT_IDENTITY_MEMBERSHIP = {
LIST_AVAILABLE_IDENTITIES: { LIST_AVAILABLE_IDENTITIES: {
projectId: "The ID of the project to list available identities for.", 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.", 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; } as const;
+1
View File
@@ -14,6 +14,7 @@ import { fastifyRequestContext } from "@fastify/request-context";
import fastify from "fastify"; import fastify from "fastify";
import { Cluster, Redis } from "ioredis"; import { Cluster, Redis } from "ioredis";
import { Knex } from "knex"; import { Knex } from "knex";
import { monitorEventLoopDelay } from "perf_hooks";
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
import { TKeyStoreFactory } from "@app/keystore/keystore"; import { TKeyStoreFactory } from "@app/keystore/keystore";
+2 -1
View File
@@ -1666,7 +1666,8 @@ export const registerRoutes = async (
identityMetadataDAL, identityMetadataDAL,
licenseService, licenseService,
permissionService, permissionService,
identityDAL: identityV2DAL identityDAL: identityV2DAL,
keyStore
}); });
const identityProjectService = identityProjectServiceFactory({ const identityProjectService = identityProjectServiceFactory({
@@ -430,7 +430,8 @@ export const registerIdentityOrgMembershipRouter = async (server: FastifyZodProv
.max(100) .max(100)
.default(20) .default(20)
.describe(ORG_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit) .describe(ORG_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit)
.optional() .optional(),
identityName: z.string().describe(ORG_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.identityName).optional()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -447,7 +448,8 @@ export const registerIdentityOrgMembershipRouter = async (server: FastifyZodProv
}, },
data: { data: {
offset: req.query.offset, offset: req.query.offset,
limit: req.query.limit limit: req.query.limit,
identityName: req.query.identityName
} }
}); });
@@ -293,7 +293,7 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
temporaryAccessEndTime: z.date().nullable().optional() 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()) authMethods: z.array(z.string())
}), }),
project: SanitizedProjectSchema.pick({ name: true, id: true }) project: SanitizedProjectSchema.pick({ name: true, id: true })
@@ -362,7 +362,9 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
temporaryAccessEndTime: z.date().nullable().optional() 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()) authMethods: z.array(z.string())
}), }),
project: SanitizedProjectSchema.pick({ name: true, id: true }) project: SanitizedProjectSchema.pick({ name: true, id: true })
@@ -469,6 +471,11 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
.max(100) .max(100)
.default(20) .default(20)
.describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit) .describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit)
.optional(),
identityName: z
.string()
.trim()
.describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.identityName)
.optional() .optional()
}), }),
response: { response: {
@@ -487,7 +494,8 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
}, },
data: { data: {
offset: req.query.offset, offset: req.query.offset,
limit: req.query.limit limit: req.query.limit,
identityName: req.query.identityName
} }
}); });
+1 -1
View File
@@ -34,7 +34,7 @@ import { registerIdentityLdapAuthRouter } from "./identity-ldap-auth-router";
import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router"; import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router";
import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router"; import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router";
import { registerIdentityOrgMembershipRouter } from "./identity-org-membership-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 { registerIdentityRouter } from "./identity-router";
import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router"; import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router";
import { registerIdentityTokenAuthRouter } from "./identity-token-auth-router"; import { registerIdentityTokenAuthRouter } from "./identity-token-auth-router";
@@ -21,6 +21,8 @@ const sanitizedIdentitySchema = IdentitiesSchema.pick({
updatedAt: true, updatedAt: true,
hasDeleteProtection: true hasDeleteProtection: true
}).extend({ }).extend({
activeLockoutAuthMethods: z.string().array().optional(),
authMethods: z.string().array().optional(),
metadata: z metadata: z
.object({ .object({
key: z.string(), key: z.string(),
@@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* 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 { requestContext } from "@fastify/request-context";
import { AxiosError } from "axios"; 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -11,6 +11,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request"; import { request } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
@@ -51,7 +52,7 @@ type TIdentityAliCloudAuthServiceFactoryDep = {
>; >;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -105,7 +106,18 @@ export const identityAliCloudAuthServiceFactory = ({
// Generate the token // Generate the token
const identityAccessToken = await identityAliCloudAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityAliCloudAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( 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, lastLoginAuthMethod: IdentityAuthMethod.ALICLOUD_AUTH,
lastLoginTime: new Date() lastLoginTime: new Date()
@@ -214,16 +226,34 @@ export const identityAliCloudAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
if ( if (
@@ -300,16 +330,31 @@ export const identityAliCloudAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
if ( if (
@@ -362,15 +407,31 @@ export const identityAliCloudAuthServiceFactory = ({
const alicloudIdentityAuth = await identityAliCloudAuthDAL.findOne({ identityId }); const alicloudIdentityAuth = await identityAliCloudAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...alicloudIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...alicloudIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -397,45 +458,61 @@ export const identityAliCloudAuthServiceFactory = ({
message: "The identity does not have Alibaba Cloud auth" message: "The identity does not have Alibaba Cloud auth"
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityAliCloudAuth = await identityAliCloudAuthDAL.transaction(async (tx) => {
const deletedAliCloudAuth = await identityAliCloudAuthDAL.delete({ identityId }, tx); const deletedAliCloudAuth = await identityAliCloudAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.ALICLOUD_AUTH }, 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 */ /* 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"; import { requestContext } from "@fastify/request-context";
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import axios from "axios"; import axios from "axios";
import RE2 from "re2"; 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -12,6 +13,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -50,7 +52,7 @@ type TIdentityAwsAuthServiceFactoryDep = {
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">; identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -179,7 +181,18 @@ export const identityAwsAuthServiceFactory = ({
const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( 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, lastLoginAuthMethod: IdentityAuthMethod.AWS_AUTH,
lastLoginTime: new Date() lastLoginTime: new Date()
@@ -300,16 +313,34 @@ export const identityAwsAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
actor, const { permission } = await permissionService.getProjectPermission({
actorId, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any 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({
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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
if ( if (
@@ -389,16 +420,31 @@ export const identityAwsAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
if ( if (
@@ -453,15 +499,31 @@ export const identityAwsAuthServiceFactory = ({
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId }); const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...awsIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...awsIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -488,45 +550,60 @@ export const identityAwsAuthServiceFactory = ({
message: "The identity does not have aws auth" message: "The identity does not have aws auth"
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => {
const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx); const deletedAwsAuth = await identityAwsAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.AWS_AUTH }, 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 { 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -9,6 +9,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -46,7 +47,7 @@ type TIdentityAzureAuthServiceFactoryDep = {
>; >;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -99,7 +100,18 @@ export const identityAzureAuthServiceFactory = ({
const identityAccessToken = await identityAzureAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityAzureAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( 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, lastLoginAuthMethod: IdentityAuthMethod.AZURE_AUTH,
lastLoginTime: new Date() lastLoginTime: new Date()
@@ -205,16 +217,34 @@ export const identityAzureAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
if ( if (
@@ -293,16 +323,31 @@ export const identityAzureAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
if ( if (
@@ -359,16 +404,31 @@ export const identityAzureAuthServiceFactory = ({
const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId }); const identityAzureAuth = await identityAzureAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...identityAzureAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...identityAzureAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -395,43 +455,59 @@ export const identityAzureAuthServiceFactory = ({
message: "The identity does not have azure auth" message: "The identity does not have azure auth"
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityAzureAuth = await identityAzureAuthDAL.transaction(async (tx) => {
const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx); const deletedAzureAuth = await identityAzureAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.AZURE_AUTH }, 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 { 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -9,6 +9,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -44,7 +45,7 @@ type TIdentityGcpAuthServiceFactoryDep = {
identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">; identityGcpAuthDAL: Pick<TIdentityGcpAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -139,7 +140,18 @@ export const identityGcpAuthServiceFactory = ({
const identityAccessToken = await identityGcpAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityGcpAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( 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, lastLoginAuthMethod: IdentityAuthMethod.GCP_AUTH,
lastLoginTime: new Date() lastLoginTime: new Date()
@@ -246,16 +258,34 @@ export const identityGcpAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
if ( if (
@@ -336,16 +366,31 @@ export const identityGcpAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
if ( if (
@@ -404,16 +449,31 @@ export const identityGcpAuthServiceFactory = ({
const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId }); const identityGcpAuth = await identityGcpAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...identityGcpAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...identityGcpAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -441,43 +501,58 @@ export const identityGcpAuthServiceFactory = ({
message: "The identity does not have gcp auth" message: "The identity does not have gcp auth"
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityGcpAuth = await identityGcpAuthDAL.transaction(async (tx) => {
const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx); const deletedGcpAuth = await identityGcpAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.GCP_AUTH }, 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 { requestContext } from "@fastify/request-context";
import https from "https"; import https from "https";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { JwksClient } from "jwks-rsa"; 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -12,6 +18,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -50,7 +57,7 @@ type TIdentityJwtAuthServiceFactoryDep = {
identityJwtAuthDAL: TIdentityJwtAuthDALFactory; identityJwtAuthDAL: TIdentityJwtAuthDALFactory;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
@@ -213,8 +220,22 @@ export const identityJwtAuthServiceFactory = ({
const identityAccessToken = await identityJwtAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityJwtAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( await membershipIdentityDAL.update(
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id }, identity.projectId
{ lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH, lastLoginTime: new Date() }, ? {
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 tx
); );
const newToken = await identityAccessTokenDAL.create( 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
if ( if (
@@ -534,17 +589,32 @@ export const identityJwtAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 identityJwtAuth = await identityJwtAuthDAL.findOne({ identityId });
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({ const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
@@ -586,45 +656,60 @@ export const identityJwtAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityJwtAuth = await identityJwtAuthDAL.transaction(async (tx) => {
const deletedJwtAuth = await identityJwtAuthDAL.delete({ identityId }, tx); const deletedJwtAuth = await identityJwtAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.JWT_AUTH }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.JWT_AUTH }, tx);
@@ -1,4 +1,4 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError, subject } from "@casl/ability";
import { requestContext } from "@fastify/request-context"; import { requestContext } from "@fastify/request-context";
import axios, { AxiosError } from "axios"; import axios, { AxiosError } from "axios";
import https from "https"; import https from "https";
@@ -6,6 +6,7 @@ import RE2 from "re2";
import { import {
AccessScope, AccessScope,
ActionProjectType,
IdentityAuthMethod, IdentityAuthMethod,
OrganizationActionScope, OrganizationActionScope,
TIdentityKubernetesAuthsUpdate TIdentityKubernetesAuthsUpdate
@@ -25,6 +26,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -69,7 +71,7 @@ type TIdentityKubernetesAuthServiceFactoryDep = {
>; >;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
gatewayService: TGatewayServiceFactory; gatewayService: TGatewayServiceFactory;
@@ -448,8 +450,22 @@ export const identityKubernetesAuthServiceFactory = ({
const identityAccessToken = await identityKubernetesAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityKubernetesAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( await membershipIdentityDAL.update(
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id }, identity.projectId
{ lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH, lastLoginTime: new Date() }, ? {
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 tx
); );
const newToken = await identityAccessTokenDAL.create( 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
if ( if (
@@ -699,16 +733,31 @@ export const identityKubernetesAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
if ( if (
@@ -846,16 +895,31 @@ export const identityKubernetesAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 { decryptor } = await kmsService.createCipherPairWithDataKey({ const { decryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization, type: KmsDataKey.Organization,
orgId: identityMembershipOrg.scopeOrgId orgId: identityMembershipOrg.scopeOrgId
@@ -906,43 +970,58 @@ export const identityKubernetesAuthServiceFactory = ({
message: "The identity does not have kubernetes auth" message: "The identity does not have kubernetes auth"
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityKubernetesAuth = await identityKubernetesAuthDAL.transaction(async (tx) => {
const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx); const deletedKubernetesAuth = await identityKubernetesAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.KUBERNETES_AUTH }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.KUBERNETES_AUTH }, tx);
@@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* 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 { requestContext } from "@fastify/request-context";
import slugify from "@sindresorhus/slugify"; 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 { TIdentityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template";
import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns"; import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
@@ -17,6 +17,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
@@ -61,7 +62,7 @@ type TIdentityLdapAuthServiceFactoryDep = {
>; >;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
kmsService: TKmsServiceFactory; kmsService: TKmsServiceFactory;
identityDAL: Pick<TIdentityDALFactory, "findById" | "findOne">; identityDAL: Pick<TIdentityDALFactory, "findById" | "findOne">;
identityAuthTemplateDAL: TIdentityAuthTemplateDALFactory; identityAuthTemplateDAL: TIdentityAuthTemplateDALFactory;
@@ -177,8 +178,22 @@ export const identityLdapAuthServiceFactory = ({
try { try {
const identityAccessToken = await identityLdapAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityLdapAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( await membershipIdentityDAL.update(
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id }, identity.projectId
{ lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH, lastLoginTime: new Date() }, ? {
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 tx
); );
const newToken = await identityAccessTokenDAL.create( 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" }); 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, scope: OrganizationActionScope.Any,
actor, actor,
actorId, actorId,
@@ -298,10 +313,30 @@ export const identityLdapAuthServiceFactory = ({
actorAuthMethod, actorAuthMethod,
actorOrgId 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) { if (templateId) {
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(orgPermission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates, OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate OrgPermissionSubjects.MachineIdentityAuthTemplate
); );
@@ -470,7 +505,7 @@ export const identityLdapAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); 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, scope: OrganizationActionScope.Any,
actor, actor,
actorId, actorId,
@@ -478,10 +513,30 @@ export const identityLdapAuthServiceFactory = ({
actorAuthMethod, actorAuthMethod,
actorOrgId 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) { if (templateId) {
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(orgPermission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates, OrgPermissionMachineIdentityAuthTemplateActions.AttachTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate OrgPermissionSubjects.MachineIdentityAuthTemplate
); );
@@ -630,14 +685,31 @@ export const identityLdapAuthServiceFactory = ({
const ldapIdentityAuth = await identityLdapAuthDAL.findOne({ identityId }); const ldapIdentityAuth = await identityLdapAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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({ const { decryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization, type: KmsDataKey.Organization,
@@ -650,7 +722,6 @@ export const identityLdapAuthServiceFactory = ({
? decryptor({ cipherTextBlob: ldapIdentityAuth.encryptedLdapCaCertificate }).toString() ? decryptor({ cipherTextBlob: ldapIdentityAuth.encryptedLdapCaCertificate }).toString()
: undefined; : undefined;
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
return { ...ldapIdentityAuth, orgId: identityMembershipOrg.scopeOrgId, bindDN, bindPass, ldapCaCertificate }; return { ...ldapIdentityAuth, orgId: identityMembershipOrg.scopeOrgId, bindDN, bindPass, ldapCaCertificate };
}; };
@@ -677,45 +748,62 @@ export const identityLdapAuthServiceFactory = ({
message: "The identity does not have LDAP Auth attached" 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({ if (identityMembershipOrg.identity.projectId) {
actor: ActorType.IDENTITY, const { permission } = await permissionService.getProjectPermission({
actorId: identityMembershipOrg.identity.id, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any 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 LDAP auth of identity with more privileged role",
shouldUseNewPrivilegeSystem,
OrgPermissionIdentityActions.RevokeAuth,
OrgPermissionSubjects.Identity
),
details: { missingPermissions: permissionBoundary.missingPermissions }
}); });
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 revokedIdentityLdapAuth = await identityLdapAuthDAL.transaction(async (tx) => {
const [deletedLdapAuth] = await identityLdapAuthDAL.delete({ identityId }, tx); const [deletedLdapAuth] = await identityLdapAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.LDAP_AUTH }, tx); await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.LDAP_AUTH }, tx);
@@ -824,15 +912,31 @@ export const identityLdapAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 deleted = await keyStore.deleteItems({ const deleted = await keyStore.deleteItems({
pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:*` pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.LDAP_AUTH}:*`
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* 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 { requestContext } from "@fastify/request-context";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import RE2 from "re2"; 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -12,6 +12,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request"; import { request } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
@@ -49,7 +50,7 @@ type TIdentityOciAuthServiceFactoryDep = {
identityOciAuthDAL: Pick<TIdentityOciAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">; identityOciAuthDAL: Pick<TIdentityOciAuthDALFactory, "findOne" | "transaction" | "create" | "updateById" | "delete">;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -110,8 +111,22 @@ export const identityOciAuthServiceFactory = ({
// Generate the token // Generate the token
const identityAccessToken = await identityOciAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityOciAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( await membershipIdentityDAL.update(
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id }, identity.projectId
{ lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH, lastLoginTime: new Date() }, ? {
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 tx
); );
const newToken = await identityAccessTokenDAL.create( 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
@@ -367,15 +417,31 @@ export const identityOciAuthServiceFactory = ({
const ociIdentityAuth = await identityOciAuthDAL.findOne({ identityId }); const ociIdentityAuth = await identityOciAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...ociIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...ociIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -402,45 +468,62 @@ export const identityOciAuthServiceFactory = ({
message: "The identity does not have OCI auth" 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({ if (identityMembershipOrg.identity.projectId) {
actor: ActorType.IDENTITY, const { permission } = await permissionService.getProjectPermission({
actorId: identityMembershipOrg.identity.id, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any actorAuthMethod,
}); actorOrgId
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 }
}); });
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 revokedIdentityOciAuth = await identityOciAuthDAL.transaction(async (tx) => {
const deletedOciAuth = await identityOciAuthDAL.delete({ identityId }, tx); const deletedOciAuth = await identityOciAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.OCI_AUTH }, 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 { requestContext } from "@fastify/request-context";
import axios from "axios"; import axios from "axios";
import https from "https"; import https from "https";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { JwksClient } from "jwks-rsa"; 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -13,6 +19,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -51,7 +58,7 @@ type TIdentityOidcAuthServiceFactoryDep = {
identityOidcAuthDAL: TIdentityOidcAuthDALFactory; identityOidcAuthDAL: TIdentityOidcAuthDALFactory;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">; identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create" | "delete">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
@@ -266,8 +273,22 @@ export const identityOidcAuthServiceFactory = ({
const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( await membershipIdentityDAL.update(
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id }, identity.projectId
{ lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH, lastLoginTime: new Date() }, ? {
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 tx
); );
const newToken = await identityAccessTokenDAL.create( 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
@@ -565,15 +621,31 @@ export const identityOidcAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId }); const identityOidcAuth = await identityOidcAuthDAL.findOne({ identityId });
@@ -610,46 +682,62 @@ export const identityOidcAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 revokedIdentityOidcAuth = await identityOidcAuthDAL.transaction(async (tx) => {
const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx); const deletedOidcAuth = await identityOidcAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.OIDC_AUTH }, 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("authMethod").as("identityAuthMethod").withSchema(TableName.Identity),
db.ref("id").as("identityId").withSchema(TableName.Identity), db.ref("id").as("identityId").withSchema(TableName.Identity),
db.ref("name").as("identityName").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("id").withSchema(TableName.Membership),
db.ref("lastLoginAuthMethod").withSchema(TableName.Membership),
db.ref("lastLoginTime").withSchema(TableName.Membership),
db.ref("role").withSchema(TableName.MembershipRole), db.ref("role").withSchema(TableName.MembershipRole),
db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"), db.ref("id").withSchema(TableName.MembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.MembershipRole), db.ref("customRoleId").withSchema(TableName.MembershipRole),
@@ -334,6 +338,8 @@ export const identityProjectDALFactory = (db: TDbClient) => {
parentMapper: ({ parentMapper: ({
identityId, identityId,
identityName, identityName,
identityOrgId,
identityProjectId,
uaId, uaId,
alicloudId, alicloudId,
awsId, awsId,
@@ -346,7 +352,9 @@ export const identityProjectDALFactory = (db: TDbClient) => {
id, id,
createdAt, createdAt,
updatedAt, updatedAt,
projectName projectName,
lastLoginAuthMethod,
lastLoginTime
}) => ({ }) => ({
id, id,
identityId, identityId,
@@ -355,6 +363,8 @@ export const identityProjectDALFactory = (db: TDbClient) => {
identity: { identity: {
id: identityId, id: identityId,
name: identityName, name: identityName,
projectId: identityProjectId,
orgId: identityOrgId,
authMethods: buildAuthMethods({ authMethods: buildAuthMethods({
uaId, uaId,
alicloudId, alicloudId,
@@ -367,6 +377,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
tokenId 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: { project: {
id: projectId, id: projectId,
name: projectName name: projectName
@@ -1,7 +1,7 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError, subject } from "@casl/ability";
import { requestContext } from "@fastify/request-context"; 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -9,6 +9,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
@@ -43,7 +44,7 @@ type TIdentityTlsCertAuthServiceFactoryDep = {
>; >;
membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">; membershipIdentityDAL: Pick<TMembershipIdentityDALFactory, "findOne" | "update" | "getIdentityById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -130,8 +131,22 @@ export const identityTlsCertAuthServiceFactory = ({
// Generate the token // Generate the token
const identityAccessToken = await identityTlsCertAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityTlsCertAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( await membershipIdentityDAL.update(
{ scope: AccessScope.Organization, scopeOrgId: identity.orgId, actorIdentityId: identity.id }, identity.projectId
{ lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH, lastLoginTime: new Date() }, ? {
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 tx
); );
const newToken = await identityAccessTokenDAL.create( 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
@@ -404,15 +454,32 @@ export const identityTlsCertAuthServiceFactory = ({
const identityAuth = await identityTlsCertAuthDAL.findOne({ identityId }); const identityAuth = await identityTlsCertAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 { decryptor } = await kmsService.createCipherPairWithDataKey({ const { decryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization, type: KmsDataKey.Organization,
orgId: identityMembershipOrg.scopeOrgId orgId: identityMembershipOrg.scopeOrgId
@@ -448,44 +515,61 @@ export const identityTlsCertAuthServiceFactory = ({
message: "The identity does not have TLS Certificate auth" 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({ if (identityMembershipOrg.identity.projectId) {
actor: ActorType.IDENTITY, const { permission } = await permissionService.getProjectPermission({
actorId: identityMembershipOrg.identity.id, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any actorAuthMethod,
}); actorOrgId
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 }
}); });
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 revokedIdentityTlsCertAuth = await identityTlsCertAuthDAL.transaction(async (tx) => {
const deletedTlsCertAuth = await identityTlsCertAuthDAL.delete({ identityId }, tx); const deletedTlsCertAuth = await identityTlsCertAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.TLS_CERT_AUTH }, 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -8,6 +14,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { import {
@@ -49,7 +56,7 @@ type TIdentityTokenAuthServiceFactoryDep = {
TIdentityAccessTokenDALFactory, TIdentityAccessTokenDALFactory,
"create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete" "create" | "find" | "update" | "findById" | "findOne" | "updateById" | "delete"
>; >;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
}; };
@@ -101,15 +108,34 @@ export const identityTokenAuthServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
@@ -251,15 +293,31 @@ export const identityTokenAuthServiceFactory = ({
const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId }); const identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...identityTokenAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...identityTokenAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -291,44 +349,61 @@ export const identityTokenAuthServiceFactory = ({
message: "The identity does not have Token Auth" 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({ if (identityMembershipOrg.identity.projectId) {
actor: ActorType.IDENTITY, const { permission } = await permissionService.getProjectPermission({
actorId: identityMembershipOrg.identity.id, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any 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 token auth of identity with more privileged role",
shouldUseNewPrivilegeSystem,
OrgPermissionIdentityActions.RevokeAuth,
OrgPermissionSubjects.Identity
),
details: { missingPermissions: permissionBoundary.missingPermissions }
}); });
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 revokedIdentityTokenAuth = await identityTokenAuthDAL.transaction(async (tx) => {
const deletedTokenAuth = await identityTokenAuthDAL.delete({ identityId }, tx); const deletedTokenAuth = await identityTokenAuthDAL.delete({ identityId }, tx);
await identityAccessTokenDAL.delete({ await identityAccessTokenDAL.delete({
@@ -367,45 +442,61 @@ export const identityTokenAuthServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 identityTokenAuth = await identityTokenAuthDAL.findOne({ identityId });
const identity = await identityDAL.findById(identityTokenAuth.identityId); const identity = await identityDAL.findById(identityTokenAuth.identityId);
@@ -413,7 +504,18 @@ export const identityTokenAuthServiceFactory = ({
const identityAccessToken = await identityTokenAuthDAL.transaction(async (tx) => { const identityAccessToken = await identityTokenAuthDAL.transaction(async (tx) => {
await membershipIdentityDAL.update( 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() }, { lastLoginAuthMethod: IdentityAuthMethod.TOKEN_AUTH, lastLoginTime: new Date() },
tx tx
); );
@@ -478,15 +580,32 @@ export const identityTokenAuthServiceFactory = ({
message: "The identity does not have Token Auth" message: "The identity does not have Token Auth"
}); });
} }
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any, if (identityMembershipOrg.identity.projectId) {
actor, const { permission } = await permissionService.getProjectPermission({
actorId, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId projectId: identityMembershipOrg.identity.projectId,
}); actorAuthMethod,
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); 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( const tokens = await identityAccessTokenDAL.find(
{ {
@@ -531,43 +650,60 @@ export const identityTokenAuthServiceFactory = ({
message: "The identity does not have Token Auth" 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({ if (identityMembershipOrg.identity.projectId) {
actor: ActorType.IDENTITY, const { permission } = await permissionService.getProjectPermission({
actorId: identityMembershipOrg.identity.id, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any actorAuthMethod,
}); actorOrgId
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 }
}); });
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( const [token] = await identityAccessTokenDAL.update(
{ {
authMethod: IdentityAuthMethod.TOKEN_AUTH, authMethod: IdentityAuthMethod.TOKEN_AUTH,
@@ -603,24 +739,43 @@ export const identityTokenAuthServiceFactory = ({
await validateIdentityUpdateForSuperAdminPrivileges(identityAccessToken.identityId, isActorSuperAdmin); await validateIdentityUpdateForSuperAdminPrivileges(identityAccessToken.identityId, isActorSuperAdmin);
const identityOrgMembership = await membershipIdentityDAL.findOne({ const identityOrgMembership = await membershipIdentityDAL.getIdentityById({
actorIdentityId: identityAccessToken.identityId, scopeData: {
scope: AccessScope.Organization scope: AccessScope.Organization,
orgId: actorOrgId
},
identityId: identityAccessToken.identityId
}); });
if (!identityOrgMembership) { if (!identityOrgMembership) {
throw new NotFoundError({ message: `Failed to find identity with ID ${identityAccessToken.identityId}` }); throw new NotFoundError({ message: `Failed to find identity with ID ${identityAccessToken.identityId}` });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityOrgMembership.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityOrgMembership.scopeOrgId, actorId,
actorAuthMethod, projectId: identityOrgMembership.identity.projectId,
actorOrgId actorAuthMethod,
}); actorOrgId
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity); });
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( 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 { 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 { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { import {
@@ -9,6 +9,7 @@ import {
validatePrivilegeChangeOperation validatePrivilegeChangeOperation
} from "@app/ee/services/permission/permission-fns"; } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; 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 { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
@@ -51,7 +52,7 @@ type TIdentityUaServiceFactoryDep = {
identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory; identityUaClientSecretDAL: TIdentityUaClientSecretDALFactory;
identityAccessTokenDAL: TIdentityAccessTokenDALFactory; identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
membershipIdentityDAL: TMembershipIdentityDALFactory; membershipIdentityDAL: TMembershipIdentityDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
orgDAL: Pick<TOrgDALFactory, "findById">; orgDAL: Pick<TOrgDALFactory, "findById">;
keyStore: Pick< keyStore: Pick<
@@ -231,7 +232,18 @@ export const identityUaServiceFactory = ({
const identityAccessToken = await identityUaDAL.transaction(async (tx) => { const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx); const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx);
await membershipIdentityDAL.update( 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, lastLoginAuthMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
lastLoginTime: new Date() lastLoginTime: new Date()
@@ -351,16 +363,35 @@ export const identityUaServiceFactory = ({
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => { 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" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 plan = await licenseService.getPlan(identityMembershipOrg.scopeOrgId);
const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => { const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => {
@@ -554,15 +601,31 @@ export const identityUaServiceFactory = ({
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" }); throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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);
}
return { ...uaIdentityAuth, orgId: identityMembershipOrg.scopeOrgId }; return { ...uaIdentityAuth, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -590,43 +653,59 @@ export const identityUaServiceFactory = ({
if (identityMembershipOrg.identity.orgId !== actorOrgId) { if (identityMembershipOrg.identity.orgId !== actorOrgId) {
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" }); 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({ if (identityMembershipOrg.identity.projectId) {
actor: ActorType.IDENTITY, const { permission } = await permissionService.getProjectPermission({
actorId: identityMembershipOrg.identity.id, actionProjectType: ActionProjectType.Any,
orgId: identityMembershipOrg.scopeOrgId, actor,
actorAuthMethod, actorId,
actorOrgId, projectId: identityMembershipOrg.identity.projectId,
scope: OrganizationActionScope.Any 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 universal auth of identity with more privileged role",
shouldUseNewPrivilegeSystem,
OrgPermissionIdentityActions.RevokeAuth,
OrgPermissionSubjects.Identity
),
details: { missingPermissions: permissionBoundary.missingPermissions }
}); });
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 revokedIdentityUniversalAuth = await identityUaDAL.transaction(async (tx) => {
const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx); const deletedUniversalAuth = await identityUaDAL.delete({ identityId }, tx);
return { ...deletedUniversalAuth?.[0], orgId: identityMembershipOrg.scopeOrgId }; 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" }); throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 appCfg = getConfig();
const clientSecret = crypto.randomBytes(32).toString("hex"); const clientSecret = crypto.randomBytes(32).toString("hex");
const clientSecretHash = await crypto.hashing().createHash(clientSecret, appCfg.SALT_ROUNDS); 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" }); throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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({ const identityUniversalAuth = await identityUaDAL.findOne({
identityId identityId
}); });
@@ -828,43 +941,57 @@ export const identityUaServiceFactory = ({
const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id }); const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id });
if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
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 }; return { ...clientSecret, identityId, orgId: identityMembershipOrg.scopeOrgId };
}; };
@@ -900,45 +1027,63 @@ export const identityUaServiceFactory = ({
const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id }); const clientSecret = await identityUaClientSecretDAL.findOne({ id: clientSecretId, identityUAId: identityUa.id });
if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` }); if (!clientSecret) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 }
}); });
}
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, { const updatedClientSecret = await identityUaClientSecretDAL.updateById(clientSecretId, {
isClientSecretRevoked: true isClientSecretRevoked: true
}); });
@@ -971,16 +1116,31 @@ export const identityUaServiceFactory = ({
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" }); throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
} }
const { permission } = await permissionService.getOrgPermission({ if (identityMembershipOrg.identity.projectId) {
scope: OrganizationActionScope.Any, const { permission } = await permissionService.getProjectPermission({
actor, actionProjectType: ActionProjectType.Any,
actorId, actor,
orgId: identityMembershipOrg.scopeOrgId, actorId,
actorAuthMethod, projectId: identityMembershipOrg.identity.projectId,
actorOrgId 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 deleted = await keyStore.deleteItems({ const deleted = await keyStore.deleteItems({
pattern: `lockout:identity:${identityId}:${IdentityAuthMethod.UNIVERSAL_AUTH}:*` 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 { AccessScope, OrgMembershipRole } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { getIdentityActiveLockoutAuthMethods } from "@app/services/identity-v2/identity-fns";
import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal"; import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal";
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
@@ -24,6 +26,7 @@ type TScopedIdentityV2ServiceFactoryDep = {
membershipIdentityDAL: TMembershipIdentityDALFactory; membershipIdentityDAL: TMembershipIdentityDALFactory;
membershipRoleDAL: TMembershipRoleDALFactory; membershipRoleDAL: TMembershipRoleDALFactory;
identityMetadataDAL: TIdentityMetadataDALFactory; identityMetadataDAL: TIdentityMetadataDALFactory;
keyStore: Pick<TKeyStoreFactory, "getKeysByPattern" | "getItem">;
}; };
export type TScopedIdentityV2ServiceFactory = ReturnType<typeof identityV2ServiceFactory>; export type TScopedIdentityV2ServiceFactory = ReturnType<typeof identityV2ServiceFactory>;
@@ -34,7 +37,8 @@ export const identityV2ServiceFactory = ({
licenseService, licenseService,
membershipIdentityDAL, membershipIdentityDAL,
membershipRoleDAL, membershipRoleDAL,
identityMetadataDAL identityMetadataDAL,
keyStore
}: TScopedIdentityV2ServiceFactoryDep) => { }: TScopedIdentityV2ServiceFactoryDep) => {
const orgFactory = newOrgIdentityFactory({ const orgFactory = newOrgIdentityFactory({
permissionService permissionService
@@ -217,7 +221,9 @@ export const identityV2ServiceFactory = ({
const identity = await identityDAL.getIdentityById(dto.scopeData, dto.selector.identityId); const identity = await identityDAL.getIdentityById(dto.scopeData, dto.selector.identityId);
if (!identity) throw new NotFoundError({ message: `Identity with id ${dto.selector.identityId} not found` }); 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) => { 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 { AccessScope, ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
@@ -47,7 +47,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
}); });
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Edit, ProjectPermissionIdentityActions.Edit,
ProjectPermissionSub.Identity subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
); );
}; };
@@ -63,7 +63,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
}); });
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Delete, ProjectPermissionIdentityActions.Delete,
ProjectPermissionSub.Identity subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
); );
}; };
@@ -95,7 +95,7 @@ export const newProjectIdentityFactory = ({ permissionService }: TProjectIdentit
}); });
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Read, 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`) .join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Identity}.id`)
.where(`${TableName.Membership}.scope`, AccessScope.Organization) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorIdentityId`) .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.whereNull(`${TableName.Identity}.projectId`)
.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection) .orderBy(`${TableName.Identity}.${orderBy}`, orderDirection)
.select( .select(
selectAllTableCols(TableName.Membership), selectAllTableCols(TableName.Membership),
@@ -404,6 +405,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
.whereNotNull(`${TableName.Membership}.actorIdentityId`) .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.where(`${TableName.Membership}.scopeOrgId`, orgId) .where(`${TableName.Membership}.scopeOrgId`, orgId)
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`) .join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`)
.whereNull(`${TableName.Identity}.projectId`)
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`) .join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`) .leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
.orderBy( .orderBy(
@@ -11,6 +11,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { TKeyStoreFactory } from "@app/keystore/keystore"; import { TKeyStoreFactory } from "@app/keystore/keystore";
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors"; import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal"; 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 { TAdditionalPrivilegeDALFactory } from "../additional-privilege/additional-privilege-dal";
import { TMembershipRoleDALFactory } from "../membership/membership-role-dal"; import { TMembershipRoleDALFactory } from "../membership/membership-role-dal";
@@ -220,6 +221,13 @@ export const identityServiceFactory = ({
} }
const identityDetails = await identityDAL.findById(id); 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 identity = await identityDAL.transaction(async (tx) => {
const newIdentity = const newIdentity =
identityDetails.orgId === actorOrgId && (name || hasDeleteProtection) identityDetails.orgId === actorOrgId && (name || hasDeleteProtection)
@@ -286,25 +294,11 @@ export const identityServiceFactory = ({
}); });
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
const activeLockouts = await keyStore.getKeysByPattern(`lockout:identity:${id}:*`); const activeLockoutAuthMethods = await getIdentityActiveLockoutAuthMethods(id, keyStore);
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 { return {
...identity, ...identity,
identity: { ...identity.identity, activeLockoutAuthMethods: Array.from(activeLockoutAuthMethods) } identity: { ...identity.identity, activeLockoutAuthMethods }
}; };
}; };
@@ -340,6 +334,10 @@ export const identityServiceFactory = ({
if (identityOrgMembership.identity.hasDeleteProtection) if (identityOrgMembership.identity.hasDeleteProtection)
throw new BadRequestError({ message: "Identity has delete protection" }); 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) { if (identityOrgMembership.identity.orgId === actorOrgId) {
const deletedIdentity = await identityDAL.deleteById(id); const deletedIdentity = await identityDAL.deleteById(id);
await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.scopeOrgId); await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.scopeOrgId);
@@ -371,8 +371,10 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
}; };
const listAvailableIdentities = async (scopeData: AccessScopeData, rootOrgId: string) => { const listAvailableIdentities = async (scopeData: AccessScopeData, rootOrgId: string) => {
// TODO (akhil/scott): need to implement filters
try { try {
const identitesConnectedToOrg = db const identitiesConnectedToOrg = db
.replicaNode()(TableName.Membership) .replicaNode()(TableName.Membership)
.whereNotNull(`${TableName.Membership}.actorIdentityId`) .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId) .where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId)
@@ -389,6 +391,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`) .join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Membership}.actorIdentityId`)
.where(`${TableName.Membership}.scope`, AccessScope.Organization) .where(`${TableName.Membership}.scope`, AccessScope.Organization)
.whereNotNull(`${TableName.Membership}.actorIdentityId`) .whereNotNull(`${TableName.Membership}.actorIdentityId`)
.whereNull(`${TableName.Identity}.projectId`)
.where((qb) => { .where((qb) => {
// if sub org pick from root and if project pick from org of project // if sub org pick from root and if project pick from org of project
if (scopeData.scope === AccessScope.Organization) { if (scopeData.scope === AccessScope.Organization) {
@@ -397,7 +400,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
void qb.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId); void qb.where(`${TableName.Membership}.scopeOrgId`, scopeData.orgId);
} }
}) })
.whereNotIn(`${TableName.Membership}.actorIdentityId`, identitesConnectedToOrg) .whereNotIn(`${TableName.Membership}.actorIdentityId`, identitiesConnectedToOrg)
.select( .select(
db.ref("id").withSchema(TableName.Identity), db.ref("id").withSchema(TableName.Identity),
db.ref("name").withSchema(TableName.Identity), db.ref("name").withSchema(TableName.Identity),
@@ -340,7 +340,8 @@ export const membershipIdentityServiceFactory = ({
await factory.onListMembershipIdentityGuard(dto); 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); const identities = await membershipIdentityDAL.listAvailableIdentities(dto.scopeData, dto.permission.rootOrgId);
@@ -1,4 +1,4 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError, subject } from "@casl/ability";
import { AccessScope, ActionProjectType, ProjectMembershipRole } from "@app/db/schemas"; import { AccessScope, ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
import { import {
@@ -119,7 +119,7 @@ export const newProjectMembershipIdentityFactory = ({
}); });
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Edit, ProjectPermissionIdentityActions.Edit,
ProjectPermissionSub.Identity subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
); );
const identityDetails = await identityDAL.findById(dto.selector.identityId); const identityDetails = await identityDAL.findById(dto.selector.identityId);
@@ -168,7 +168,7 @@ export const newProjectMembershipIdentityFactory = ({
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Delete, ProjectPermissionIdentityActions.Delete,
ProjectPermissionSub.Identity subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
); );
const identityDetails = await identityDAL.findById(dto.selector.identityId); const identityDetails = await identityDAL.findById(dto.selector.identityId);
@@ -210,7 +210,7 @@ export const newProjectMembershipIdentityFactory = ({
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Read, ProjectPermissionIdentityActions.Read,
ProjectPermissionSub.Identity subject(ProjectPermissionSub.Identity, { identityId: dto.selector.identityId })
); );
}; };
@@ -78,7 +78,11 @@ export enum ProjectPermissionIdentityActions {
Edit = "edit", Edit = "edit",
Delete = "delete", Delete = "delete",
GrantPrivileges = "grant-privileges", GrantPrivileges = "grant-privileges",
AssumePrivileges = "assume-privileges" AssumePrivileges = "assume-privileges",
RevokeAuth = "revoke-auth",
CreateToken = "create-token",
GetToken = "get-token",
DeleteToken = "delete-token"
} }
export enum ProjectPermissionMemberActions { export enum ProjectPermissionMemberActions {
+505 -216
View File
@@ -1,9 +1,9 @@
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { projectIdentityQuery, projectKeys } from "@app/hooks/api";
import { organizationKeys } from "../organization/queries"; import { organizationKeys } from "../organization/queries";
import { subscriptionQueryKeys } from "../subscriptions/queries";
import { identitiesKeys } from "./queries"; import { identitiesKeys } from "./queries";
import { import {
AddIdentityAliCloudAuthDTO, AddIdentityAliCloudAuthDTO,
@@ -21,7 +21,6 @@ import {
ClearIdentityLdapAuthLockoutsDTO, ClearIdentityLdapAuthLockoutsDTO,
ClearIdentityUniversalAuthLockoutsDTO, ClearIdentityUniversalAuthLockoutsDTO,
ClientSecretData, ClientSecretData,
CreateIdentityDTO,
CreateIdentityUniversalAuthClientSecretDTO, CreateIdentityUniversalAuthClientSecretDTO,
CreateIdentityUniversalAuthClientSecretRes, CreateIdentityUniversalAuthClientSecretRes,
CreateTokenIdentityTokenAuthDTO, CreateTokenIdentityTokenAuthDTO,
@@ -29,7 +28,6 @@ import {
DeleteIdentityAliCloudAuthDTO, DeleteIdentityAliCloudAuthDTO,
DeleteIdentityAwsAuthDTO, DeleteIdentityAwsAuthDTO,
DeleteIdentityAzureAuthDTO, DeleteIdentityAzureAuthDTO,
DeleteIdentityDTO,
DeleteIdentityGcpAuthDTO, DeleteIdentityGcpAuthDTO,
DeleteIdentityJwtAuthDTO, DeleteIdentityJwtAuthDTO,
DeleteIdentityKubernetesAuthDTO, DeleteIdentityKubernetesAuthDTO,
@@ -40,7 +38,6 @@ import {
DeleteIdentityTokenAuthDTO, DeleteIdentityTokenAuthDTO,
DeleteIdentityUniversalAuthClientSecretDTO, DeleteIdentityUniversalAuthClientSecretDTO,
DeleteIdentityUniversalAuthDTO, DeleteIdentityUniversalAuthDTO,
Identity,
IdentityAccessToken, IdentityAccessToken,
IdentityAliCloudAuth, IdentityAliCloudAuth,
IdentityAwsAuth, IdentityAwsAuth,
@@ -59,7 +56,6 @@ import {
UpdateIdentityAliCloudAuthDTO, UpdateIdentityAliCloudAuthDTO,
UpdateIdentityAwsAuthDTO, UpdateIdentityAwsAuthDTO,
UpdateIdentityAzureAuthDTO, UpdateIdentityAzureAuthDTO,
UpdateIdentityDTO,
UpdateIdentityGcpAuthDTO, UpdateIdentityGcpAuthDTO,
UpdateIdentityJwtAuthDTO, UpdateIdentityJwtAuthDTO,
UpdateIdentityKubernetesAuthDTO, UpdateIdentityKubernetesAuthDTO,
@@ -72,73 +68,6 @@ import {
UpdateTokenIdentityTokenAuthDTO UpdateTokenIdentityTokenAuthDTO
} from "./types"; } from "./types";
export const useCreateIdentity = () => {
const queryClient = useQueryClient();
return useMutation<Identity, object, CreateIdentityDTO>({
mutationFn: async (body) => {
const {
data: { identity }
} = await apiRequest.post("/api/v1/identities/", body);
return identity;
},
onSuccess: (_, { organizationId }) => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
});
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
}
});
};
export const useUpdateIdentity = () => {
const queryClient = useQueryClient();
return useMutation<Identity, object, UpdateIdentityDTO>({
mutationFn: async ({ identityId, name, role, hasDeleteProtection, metadata }) => {
const {
data: { identity }
} = await apiRequest.patch(`/api/v1/identities/${identityId}`, {
name,
role,
hasDeleteProtection,
metadata
});
return identity;
},
onSuccess: (_, { organizationId, identityId }) => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
}
});
};
export const useDeleteIdentity = () => {
const queryClient = useQueryClient();
return useMutation<Identity, object, DeleteIdentityDTO>({
mutationFn: async ({ identityId }) => {
const {
data: { identity }
} = await apiRequest.delete(`/api/v1/identities/${identityId}`);
return identity;
},
onSuccess: (_, { organizationId }) => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
});
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
}
});
};
// TODO: move these to /auth // TODO: move these to /auth
export const useAddIdentityUniversalAuth = () => { export const useAddIdentityUniversalAuth = () => {
@@ -171,10 +100,20 @@ export const useAddIdentityUniversalAuth = () => {
}); });
return identityUniversalAuth; return identityUniversalAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId) queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
@@ -215,10 +154,20 @@ export const useUpdateIdentityUniversalAuth = () => {
}); });
return identityUniversalAuth; return identityUniversalAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId) queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
@@ -236,10 +185,20 @@ export const useDeleteIdentityUniversalAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/universal-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/universal-auth/identities/${identityId}`);
return identityUniversalAuth; return identityUniversalAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId) queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
@@ -344,10 +303,20 @@ export const useAddIdentityGcpAuth = () => {
return identityGcpAuth; return identityGcpAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) });
} }
@@ -386,10 +355,20 @@ export const useUpdateIdentityGcpAuth = () => {
return identityGcpAuth; return identityGcpAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) });
} }
@@ -405,10 +384,20 @@ export const useDeleteIdentityGcpAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/gcp-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/gcp-auth/identities/${identityId}`);
return identityGcpAuth; return identityGcpAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) });
} }
@@ -445,10 +434,20 @@ export const useAddIdentityAwsAuth = () => {
return identityAwsAuth; return identityAwsAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) });
} }
@@ -485,10 +484,20 @@ export const useUpdateIdentityAwsAuth = () => {
return identityAwsAuth; return identityAwsAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) });
} }
@@ -504,10 +513,20 @@ export const useDeleteIdentityAwsAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/aws-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/aws-auth/identities/${identityId}`);
return identityAwsAuth; return identityAwsAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) });
} }
@@ -542,10 +561,20 @@ export const useAddIdentityOciAuth = () => {
return identityOciAuth; return identityOciAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) });
} }
@@ -580,10 +609,20 @@ export const useUpdateIdentityOciAuth = () => {
return identityOciAuth; return identityOciAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) });
} }
@@ -599,10 +638,20 @@ export const useDeleteIdentityOciAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/oci-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/oci-auth/identities/${identityId}`);
return identityOciAuth; return identityOciAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) });
} }
@@ -635,10 +684,20 @@ export const useAddIdentityAliCloudAuth = () => {
return identityAliCloudAuth; return identityAliCloudAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId) queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId)
@@ -673,10 +732,20 @@ export const useUpdateIdentityAliCloudAuth = () => {
return identityAliCloudAuth; return identityAliCloudAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId) queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId)
@@ -694,10 +763,20 @@ export const useDeleteIdentityAliCloudAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/alicloud-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/alicloud-auth/identities/${identityId}`);
return identityAliCloudAuth; return identityAliCloudAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId) queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId)
@@ -734,10 +813,20 @@ export const useAddIdentityTlsCertAuth = () => {
return identityTlsCertAuth; return identityTlsCertAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId) queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId)
@@ -774,10 +863,20 @@ export const useUpdateIdentityTlsCertAuth = () => {
return identityTlsCertAuth; return identityTlsCertAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId) queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId)
@@ -795,10 +894,20 @@ export const useDeleteIdentityTlsCertAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/tls-cert-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/tls-cert-auth/identities/${identityId}`);
return identityTlsCertAuth; return identityTlsCertAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId) queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId)
@@ -845,10 +954,20 @@ export const useUpdateIdentityOidcAuth = () => {
return identityOidcAuth; return identityOidcAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) });
} }
@@ -893,10 +1012,20 @@ export const useAddIdentityOidcAuth = () => {
return identityOidcAuth; return identityOidcAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) });
} }
@@ -912,10 +1041,20 @@ export const useDeleteIdentityOidcAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/oidc-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/oidc-auth/identities/${identityId}`);
return identityOidcAuth; return identityOidcAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) });
} }
@@ -961,10 +1100,20 @@ export const useUpdateIdentityJwtAuth = () => {
return identityJwtAuth; return identityJwtAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) });
} }
@@ -1011,10 +1160,20 @@ export const useAddIdentityJwtAuth = () => {
return identityJwtAuth; return identityJwtAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) });
} }
@@ -1030,10 +1189,20 @@ export const useDeleteIdentityJwtAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/jwt-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/jwt-auth/identities/${identityId}`);
return identityJwtAuth; return identityJwtAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) });
} }
@@ -1070,10 +1239,20 @@ export const useAddIdentityAzureAuth = () => {
return identityAzureAuth; return identityAzureAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId) queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId)
@@ -1122,10 +1301,20 @@ export const useAddIdentityKubernetesAuth = () => {
return identityKubernetesAuth; return identityKubernetesAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) });
} }
@@ -1162,10 +1351,20 @@ export const useUpdateIdentityAzureAuth = () => {
return identityAzureAuth; return identityAzureAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) });
} }
@@ -1181,10 +1380,20 @@ export const useDeleteIdentityAzureAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/azure-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/azure-auth/identities/${identityId}`);
return identityAzureAuth; return identityAzureAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) });
} }
@@ -1231,10 +1440,20 @@ export const useUpdateIdentityKubernetesAuth = () => {
return identityKubernetesAuth; return identityKubernetesAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId) queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId)
@@ -1252,10 +1471,20 @@ export const useDeleteIdentityKubernetesAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/kubernetes-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/kubernetes-auth/identities/${identityId}`);
return identityKubernetesAuth; return identityKubernetesAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId) queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId)
@@ -1288,10 +1517,20 @@ export const useAddIdentityTokenAuth = () => {
return identityTokenAuth; return identityTokenAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityTokenAuth(identityId) queryKey: identitiesKeys.getIdentityTokenAuth(identityId)
@@ -1324,10 +1563,20 @@ export const useUpdateIdentityTokenAuth = () => {
return identityTokenAuth; return identityTokenAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityTokenAuth(identityId) queryKey: identitiesKeys.getIdentityTokenAuth(identityId)
@@ -1345,10 +1594,20 @@ export const useDeleteIdentityTokenAuth = () => {
} = await apiRequest.delete(`/api/v1/auth/token-auth/identities/${identityId}`); } = await apiRequest.delete(`/api/v1/auth/token-auth/identities/${identityId}`);
return identityTokenAuth; return identityTokenAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityTokenAuth(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityTokenAuth(identityId) });
} }
@@ -1462,10 +1721,20 @@ export const useAddIdentityLdapAuth = () => {
); );
return data.identityLdapAuth; return data.identityLdapAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityLdapAuth(identityId) queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
@@ -1519,10 +1788,20 @@ export const useUpdateIdentityLdapAuth = () => {
); );
return data.identityLdapAuth; return data.identityLdapAuth;
}, },
onSuccess: (_, { identityId, organizationId }) => { onSuccess: (_, { identityId, organizationId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityLdapAuth(identityId) queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
@@ -1538,10 +1817,20 @@ export const useDeleteIdentityLdapAuth = () => {
const { data } = await apiRequest.delete(`/api/v1/auth/ldap-auth/identities/${identityId}`); const { data } = await apiRequest.delete(`/api/v1/auth/ldap-auth/identities/${identityId}`);
return data.identityLdapAuth; return data.identityLdapAuth;
}, },
onSuccess: (_, { organizationId, identityId }) => { onSuccess: (_, { organizationId, identityId, projectId }) => {
queryClient.invalidateQueries({ if (organizationId) {
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId) queryClient.invalidateQueries({
}); queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
}
if (projectId) {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
});
}
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) }); queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityLdapAuth(identityId) queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
@@ -13,10 +13,10 @@ import {
IdentityJwtAuth, IdentityJwtAuth,
IdentityKubernetesAuth, IdentityKubernetesAuth,
IdentityLdapAuth, IdentityLdapAuth,
IdentityMembership,
IdentityMembershipOrg, IdentityMembershipOrg,
IdentityOciAuth, IdentityOciAuth,
IdentityOidcAuth, IdentityOidcAuth,
IdentityProjectMembership,
IdentityTlsCertAuth, IdentityTlsCertAuth,
IdentityTokenAuth, IdentityTokenAuth,
IdentityUniversalAuth, IdentityUniversalAuth,
@@ -52,7 +52,7 @@ export const identitiesKeys = {
[{ identityId }, "identity-project-memberships"] as const [{ identityId }, "identity-project-memberships"] as const
}; };
export const useGetIdentityById = (identityId: string) => { export const useGetOrgIdentityMembershipById = (identityId: string) => {
return useQuery({ return useQuery({
enabled: Boolean(identityId), enabled: Boolean(identityId),
queryKey: identitiesKeys.getIdentityById(identityId), queryKey: identitiesKeys.getIdentityById(identityId),
@@ -67,7 +67,7 @@ export const useGetIdentityById = (identityId: string) => {
}); });
}; };
export const useSearchIdentities = (dto: TSearchIdentitiesDTO) => { export const useSearchOrgIdentityMemberships = (dto: TSearchIdentitiesDTO) => {
const { limit, search, offset, orderBy, orderDirection } = dto; const { limit, search, offset, orderBy, orderDirection } = dto;
return useQuery({ return useQuery({
queryKey: identitiesKeys.searchIdentities(dto), queryKey: identitiesKeys.searchIdentities(dto),
@@ -95,7 +95,7 @@ export const useGetIdentityProjectMemberships = (identityId: string) => {
queryFn: async () => { queryFn: async () => {
const { const {
data: { identityMemberships } data: { identityMemberships }
} = await apiRequest.get<{ identityMemberships: IdentityMembership[] }>( } = await apiRequest.get<{ identityMemberships: IdentityProjectMembership[] }>(
`/api/v1/identities/${identityId}/identity-memberships` `/api/v1/identities/${identityId}/identity-memberships`
); );
return identityMemberships; return identityMemberships;
+118 -76
View File
@@ -1,6 +1,8 @@
import { TemporaryPermissionMode } from "@app/hooks/api/shared";
import { OrderByDirection } from "../generic/types"; import { OrderByDirection } from "../generic/types";
import { OrgIdentityOrderBy } from "../organization/types"; import { OrgIdentityOrderBy } from "../organization/types";
import { Project, ProjectUserMembershipTemporaryMode } from "../projects/types"; import { Project } from "../projects/types";
import { TOrgRole } from "../roles/types"; import { TOrgRole } from "../roles/types";
import { IdentityAuthMethod, IdentityJwtConfigurationType } from "./enums"; import { IdentityAuthMethod, IdentityJwtConfigurationType } from "./enums";
@@ -21,6 +23,8 @@ export type Identity = {
updatedAt: string; updatedAt: string;
isInstanceAdmin?: boolean; isInstanceAdmin?: boolean;
orgId: string; orgId: string;
projectId?: string | null;
metadata?: { key: string; value: string; id: string }[];
}; };
export type IdentityAccessToken = { export type IdentityAccessToken = {
@@ -52,7 +56,7 @@ export type IdentityMembershipOrg = {
updatedAt: string; updatedAt: string;
}; };
export type IdentityMembership = { export type IdentityProjectMembership = {
id: string; id: string;
identity: Identity; identity: Identity;
project: Pick<Project, "id" | "name" | "type">; project: Pick<Project, "id" | "name" | "type">;
@@ -74,7 +78,7 @@ export type IdentityMembership = {
| { | {
isTemporary: true; isTemporary: true;
temporaryRange: string; temporaryRange: string;
temporaryMode: ProjectUserMembershipTemporaryMode; temporaryMode: TemporaryPermissionMode;
temporaryAccessEndTime: string; temporaryAccessEndTime: string;
temporaryAccessStartTime: string; temporaryAccessStartTime: string;
} }
@@ -82,6 +86,8 @@ export type IdentityMembership = {
>; >;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
lastLoginTime?: string;
lastLoginAuthMethod?: IdentityAuthMethod;
}; };
export type CreateIdentityDTO = { export type CreateIdentityDTO = {
@@ -122,7 +128,8 @@ export type IdentityUniversalAuth = {
}; };
export type AddIdentityUniversalAuthDTO = { export type AddIdentityUniversalAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
clientSecretTrustedIps: { clientSecretTrustedIps: {
ipAddress: string; ipAddress: string;
@@ -138,10 +145,11 @@ export type AddIdentityUniversalAuthDTO = {
lockoutThreshold: number; lockoutThreshold: number;
lockoutDurationSeconds: number; lockoutDurationSeconds: number;
lockoutCounterResetSeconds: number; lockoutCounterResetSeconds: number;
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityUniversalAuthDTO = { export type UpdateIdentityUniversalAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
clientSecretTrustedIps?: { clientSecretTrustedIps?: {
ipAddress: string; ipAddress: string;
@@ -157,12 +165,13 @@ export type UpdateIdentityUniversalAuthDTO = {
lockoutThreshold?: number; lockoutThreshold?: number;
lockoutDurationSeconds?: number; lockoutDurationSeconds?: number;
lockoutCounterResetSeconds?: number; lockoutCounterResetSeconds?: number;
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityUniversalAuthDTO = { export type DeleteIdentityUniversalAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityGcpAuth = { export type IdentityGcpAuth = {
identityId: string; identityId: string;
@@ -177,7 +186,8 @@ export type IdentityGcpAuth = {
}; };
export type AddIdentityGcpAuthDTO = { export type AddIdentityGcpAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
type: "iam" | "gce"; type: "iam" | "gce";
allowedServiceAccounts: string; allowedServiceAccounts: string;
@@ -189,10 +199,11 @@ export type AddIdentityGcpAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityGcpAuthDTO = { export type UpdateIdentityGcpAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
type?: "iam" | "gce"; type?: "iam" | "gce";
allowedServiceAccounts?: string; allowedServiceAccounts?: string;
@@ -204,12 +215,13 @@ export type UpdateIdentityGcpAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityGcpAuthDTO = { export type DeleteIdentityGcpAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityOidcAuth = { export type IdentityOidcAuth = {
identityId: string; identityId: string;
@@ -227,7 +239,8 @@ export type IdentityOidcAuth = {
}; };
export type AddIdentityOidcAuthDTO = { export type AddIdentityOidcAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
oidcDiscoveryUrl: string; oidcDiscoveryUrl: string;
caCert: string; caCert: string;
@@ -242,10 +255,11 @@ export type AddIdentityOidcAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityOidcAuthDTO = { export type UpdateIdentityOidcAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
oidcDiscoveryUrl?: string; oidcDiscoveryUrl?: string;
caCert?: string; caCert?: string;
@@ -260,12 +274,13 @@ export type UpdateIdentityOidcAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityOidcAuthDTO = { export type DeleteIdentityOidcAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityAwsAuth = { export type IdentityAwsAuth = {
identityId: string; identityId: string;
@@ -280,7 +295,8 @@ export type IdentityAwsAuth = {
}; };
export type AddIdentityAwsAuthDTO = { export type AddIdentityAwsAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
stsEndpoint: string; stsEndpoint: string;
allowedPrincipalArns: string; allowedPrincipalArns: string;
@@ -291,10 +307,11 @@ export type AddIdentityAwsAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityAwsAuthDTO = { export type UpdateIdentityAwsAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
stsEndpoint?: string; stsEndpoint?: string;
allowedPrincipalArns?: string; allowedPrincipalArns?: string;
@@ -308,9 +325,10 @@ export type UpdateIdentityAwsAuthDTO = {
}; };
export type DeleteIdentityAwsAuthDTO = { export type DeleteIdentityAwsAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityAliCloudAuth = { export type IdentityAliCloudAuth = {
identityId: string; identityId: string;
@@ -323,7 +341,8 @@ export type IdentityAliCloudAuth = {
}; };
export type AddIdentityAliCloudAuthDTO = { export type AddIdentityAliCloudAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
allowedArns: string; allowedArns: string;
accessTokenTTL: number; accessTokenTTL: number;
@@ -332,10 +351,11 @@ export type AddIdentityAliCloudAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityAliCloudAuthDTO = { export type UpdateIdentityAliCloudAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
allowedArns: string; allowedArns: string;
accessTokenTTL?: number; accessTokenTTL?: number;
@@ -344,12 +364,13 @@ export type UpdateIdentityAliCloudAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityAliCloudAuthDTO = { export type DeleteIdentityAliCloudAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityOciAuth = { export type IdentityOciAuth = {
identityId: string; identityId: string;
@@ -363,7 +384,8 @@ export type IdentityOciAuth = {
}; };
export type AddIdentityOciAuthDTO = { export type AddIdentityOciAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
tenancyOcid: string; tenancyOcid: string;
allowedUsernames?: string | null; allowedUsernames?: string | null;
@@ -373,10 +395,11 @@ export type AddIdentityOciAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityOciAuthDTO = { export type UpdateIdentityOciAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
tenancyOcid?: string; tenancyOcid?: string;
allowedUsernames?: string | null; allowedUsernames?: string | null;
@@ -386,12 +409,13 @@ export type UpdateIdentityOciAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityOciAuthDTO = { export type DeleteIdentityOciAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityAzureAuth = { export type IdentityAzureAuth = {
identityId: string; identityId: string;
@@ -405,7 +429,8 @@ export type IdentityAzureAuth = {
}; };
export type AddIdentityAzureAuthDTO = { export type AddIdentityAzureAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
tenantId: string; tenantId: string;
resource: string; resource: string;
@@ -416,10 +441,11 @@ export type AddIdentityAzureAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityAzureAuthDTO = { export type UpdateIdentityAzureAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
tenantId?: string; tenantId?: string;
resource?: string; resource?: string;
@@ -430,12 +456,13 @@ export type UpdateIdentityAzureAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityAzureAuthDTO = { export type DeleteIdentityAzureAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export enum IdentityKubernetesAuthTokenReviewMode { export enum IdentityKubernetesAuthTokenReviewMode {
Api = "api", Api = "api",
@@ -459,7 +486,8 @@ export type IdentityKubernetesAuth = {
}; };
export type AddIdentityKubernetesAuthDTO = { export type AddIdentityKubernetesAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
kubernetesHost: string | null; kubernetesHost: string | null;
tokenReviewerJwt?: string; tokenReviewerJwt?: string;
@@ -475,10 +503,11 @@ export type AddIdentityKubernetesAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityKubernetesAuthDTO = { export type UpdateIdentityKubernetesAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
kubernetesHost?: string | null; kubernetesHost?: string | null;
tokenReviewerJwt?: string | null; tokenReviewerJwt?: string | null;
@@ -494,12 +523,13 @@ export type UpdateIdentityKubernetesAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityKubernetesAuthDTO = { export type DeleteIdentityKubernetesAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityTlsCertAuth = { export type IdentityTlsCertAuth = {
identityId: string; identityId: string;
@@ -512,7 +542,8 @@ export type IdentityTlsCertAuth = {
}; };
export type AddIdentityTlsCertAuthDTO = { export type AddIdentityTlsCertAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
caCertificate: string; caCertificate: string;
allowedCommonNames?: string; allowedCommonNames?: string;
@@ -522,10 +553,11 @@ export type AddIdentityTlsCertAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityTlsCertAuthDTO = { export type UpdateIdentityTlsCertAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
caCertificate: string; caCertificate: string;
allowedCommonNames?: string | null; allowedCommonNames?: string | null;
@@ -535,12 +567,13 @@ export type UpdateIdentityTlsCertAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityTlsCertAuthDTO = { export type DeleteIdentityTlsCertAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type CreateIdentityUniversalAuthClientSecretDTO = { export type CreateIdentityUniversalAuthClientSecretDTO = {
identityId: string; identityId: string;
@@ -585,7 +618,8 @@ export type IdentityTokenAuth = {
}; };
export type AddIdentityLdapAuthDTO = { export type AddIdentityLdapAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
templateId?: string; templateId?: string;
url?: string; url?: string;
@@ -609,11 +643,12 @@ export type AddIdentityLdapAuthDTO = {
lockoutThreshold: number; lockoutThreshold: number;
lockoutDurationSeconds: number; lockoutDurationSeconds: number;
lockoutCounterResetSeconds: number; lockoutCounterResetSeconds: number;
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityLdapAuthDTO = { export type UpdateIdentityLdapAuthDTO = {
identityId: string; identityId: string;
organizationId: string; organizationId?: string;
projectId?: string;
templateId?: string; templateId?: string;
url?: string; url?: string;
bindDN?: string; bindDN?: string;
@@ -636,12 +671,13 @@ export type UpdateIdentityLdapAuthDTO = {
lockoutThreshold?: number; lockoutThreshold?: number;
lockoutDurationSeconds?: number; lockoutDurationSeconds?: number;
lockoutCounterResetSeconds?: number; lockoutCounterResetSeconds?: number;
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityLdapAuthDTO = { export type DeleteIdentityLdapAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityLdapAuth = { export type IdentityLdapAuth = {
url?: string; url?: string;
@@ -673,7 +709,8 @@ export type ClearIdentityLdapAuthLockoutsDTO = {
}; };
export type AddIdentityTokenAuthDTO = { export type AddIdentityTokenAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
accessTokenTTL: number; accessTokenTTL: number;
accessTokenMaxTTL: number; accessTokenMaxTTL: number;
@@ -681,10 +718,11 @@ export type AddIdentityTokenAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityTokenAuthDTO = { export type UpdateIdentityTokenAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
accessTokenTTL?: number; accessTokenTTL?: number;
accessTokenMaxTTL?: number; accessTokenMaxTTL?: number;
@@ -692,12 +730,13 @@ export type UpdateIdentityTokenAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityTokenAuthDTO = { export type DeleteIdentityTokenAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type IdentityJwtAuth = { export type IdentityJwtAuth = {
identityId: string; identityId: string;
@@ -716,7 +755,8 @@ export type IdentityJwtAuth = {
}; };
export type AddIdentityJwtAuthDTO = { export type AddIdentityJwtAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
configurationType: string; configurationType: string;
jwksUrl?: string; jwksUrl?: string;
@@ -732,10 +772,11 @@ export type AddIdentityJwtAuthDTO = {
accessTokenTrustedIps: { accessTokenTrustedIps: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type UpdateIdentityJwtAuthDTO = { export type UpdateIdentityJwtAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
configurationType?: string; configurationType?: string;
jwksUrl?: string; jwksUrl?: string;
@@ -751,12 +792,13 @@ export type UpdateIdentityJwtAuthDTO = {
accessTokenTrustedIps?: { accessTokenTrustedIps?: {
ipAddress: string; ipAddress: string;
}[]; }[];
}; } & ({ organizationId: string } | { projectId: string });
export type DeleteIdentityJwtAuthDTO = { export type DeleteIdentityJwtAuthDTO = {
organizationId: string; organizationId?: string;
projectId?: string;
identityId: string; identityId: string;
}; } & ({ organizationId: string } | { projectId: string });
export type CreateTokenIdentityTokenAuthDTO = { export type CreateTokenIdentityTokenAuthDTO = {
identityId: string; identityId: string;
@@ -785,8 +827,8 @@ export type RevokeTokenRes = {
message: string; message: string;
}; };
export type TProjectIdentitiesList = { export type TProjectIdentityMembershipsList = {
identityMemberships: IdentityMembership[]; identityMemberships: IdentityProjectMembership[];
totalCount: number; totalCount: number;
}; };
+4
View File
@@ -25,10 +25,14 @@ export * from "./ldapConfig";
export * from "./oidcConfig"; export * from "./oidcConfig";
export * from "./orgAdmin"; export * from "./orgAdmin";
export * from "./organization"; export * from "./organization";
export * from "./orgIdentity";
export * from "./orgIdentityMembership";
export * from "./pkiAlerts"; export * from "./pkiAlerts";
export * from "./pkiCollections"; export * from "./pkiCollections";
export * from "./pkiSubscriber"; export * from "./pkiSubscriber";
export * from "./pkiSyncs"; export * from "./pkiSyncs";
export * from "./projectIdentity";
export * from "./projectIdentityMembership";
export * from "./projects"; export * from "./projects";
export * from "./projectUserAdditionalPrivilege"; export * from "./projectUserAdditionalPrivilege";
export * from "./rateLimit"; export * from "./rateLimit";
@@ -0,0 +1,3 @@
export * from "./mutations";
export * from "./queries";
export type * from "./types";
@@ -0,0 +1,116 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { identitiesKeys } from "@app/hooks/api";
import { CreateIdentityDTO, Identity, UpdateIdentityDTO } from "@app/hooks/api/identities/types";
import { organizationKeys } from "@app/hooks/api/organization/queries";
import { subscriptionQueryKeys } from "@app/hooks/api/subscriptions/queries";
import { orgIdentityQuery } from "./queries";
import { TDeleteOrgIdentityDTO, TOrgIdentity } from "./types";
// TODO (scott/akhi): eventually move to the new api commented out below; the current ones use old api
export const useCreateOrgIdentity = () => {
const queryClient = useQueryClient();
return useMutation<Identity, object, CreateIdentityDTO>({
mutationFn: async (body) => {
const {
data: { identity }
} = await apiRequest.post("/api/v1/identities/", body);
return identity;
},
onSuccess: (_, { organizationId }) => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
});
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
}
});
};
export const useUpdateOrgIdentity = () => {
const queryClient = useQueryClient();
return useMutation<Identity, object, UpdateIdentityDTO>({
mutationFn: async ({ identityId, name, role, hasDeleteProtection, metadata }) => {
const {
data: { identity }
} = await apiRequest.patch(`/api/v1/identities/${identityId}`, {
name,
role,
hasDeleteProtection,
metadata
});
return identity;
},
onSuccess: (_, { organizationId, identityId }) => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
}
});
};
// export const useCreateOrgIdentity = () => {
// const queryClient = useQueryClient();
// return useMutation({
// mutationFn: async (dto: TCreateOrgIdentityDTO) => {
// const { data } = await apiRequest.post<{ identity: TOrgIdentity }>(
// "/api/v1/organization/identities",
// dto
// );
// return data;
// },
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: orgIdentityQuery.allKey() });
// queryClient.invalidateQueries({
// queryKey: subscriptionQueryKeys.all()
// });
// }
// });
// };
//
// export const useUpdateOrgIdentity = () => {
// const queryClient = useQueryClient();
// return useMutation({
// mutationFn: async ({ identityId, ...updates }: TUpdateOrgIdentityDTO) => {
// const { data } = await apiRequest.patch<{ identity: TOrgIdentity }>(
// `/api/v1/organization/identities/${identityId}`,
// updates
// );
// return data;
// },
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: orgIdentityQuery.allKey() });
// }
// });
// };
export const useDeleteOrgIdentity = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId }: TDeleteOrgIdentityDTO) => {
const { data } = await apiRequest.delete<{ identity: TOrgIdentity }>(
`/api/v1/identities/${identityId}`
);
return data;
},
onSuccess: (_, { orgId }) => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(orgId)
});
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(orgId) });
queryClient.invalidateQueries({ queryKey: orgIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.all()
});
}
});
};
@@ -0,0 +1,40 @@
import { queryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TGetOrgIdentityByIdDTO, TListOrgIdentitiesDTO, TOrgIdentity } from "./types";
export const orgIdentityQuery = {
allKey: () => ["organization-identities"] as const,
getByIdKey: (params: TGetOrgIdentityByIdDTO) =>
[...orgIdentityQuery.allKey(), "by-id", params] as const,
listKey: (params?: TListOrgIdentitiesDTO) =>
[...orgIdentityQuery.allKey(), "list", params] as const,
getById: (params: TGetOrgIdentityByIdDTO) =>
queryOptions({
queryKey: orgIdentityQuery.getByIdKey(params),
queryFn: async () => {
const { data } = await apiRequest.get<{ identity: TOrgIdentity }>(
`/api/v1/organization/identities/${params.identityId}`
);
return data.identity;
}
}),
list: (params: TListOrgIdentitiesDTO = {}) =>
queryOptions({
queryKey: orgIdentityQuery.listKey(params),
queryFn: async () => {
const { data } = await apiRequest.get<{
identities: TOrgIdentity[];
totalCount: number;
}>("/api/v1/organization/identities", {
params: {
offset: params.offset,
limit: params.limit,
search: params.search
}
});
return data;
}
})
};
@@ -0,0 +1,31 @@
import { TIdentity, TMetadata } from "@app/hooks/api/shared";
export type TOrgIdentity = TIdentity;
export type TCreateOrgIdentityDTO = {
name: string;
hasDeleteProtection?: boolean;
metadata?: TMetadata;
};
export type TUpdateOrgIdentityDTO = {
identityId: string;
name?: string;
hasDeleteProtection?: boolean;
metadata?: TMetadata;
};
export type TGetOrgIdentityByIdDTO = {
identityId: string;
};
export type TListOrgIdentitiesDTO = {
offset?: number;
limit?: number;
search?: string;
};
export type TDeleteOrgIdentityDTO = {
identityId: string;
orgId: string;
};
@@ -0,0 +1,31 @@
import { queryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import {
TAvailableOrganizationIdentities,
TListAvailableOrganizationIdentitiesDTO,
TListOrgIdentityMembershipsDTO
} from "./types";
export const orgIdentityMembershipQuery = {
allKey: () => ["organization-identity-memberships"] as const,
listAvailableKey: (params?: TListOrgIdentityMembershipsDTO) =>
[...orgIdentityMembershipQuery.allKey(), "list-available", params] as const,
listAvailable: (params: TListAvailableOrganizationIdentitiesDTO = {}) =>
queryOptions({
queryKey: orgIdentityMembershipQuery.listAvailableKey(params),
queryFn: async () => {
const { data } = await apiRequest.get<{
identities: TAvailableOrganizationIdentities;
}>("/api/v1/organization/available-identities", {
params: {
offset: params.offset,
limit: params.limit,
identityName: params.identityName
}
});
return data.identities;
}
})
};
@@ -1,6 +1,4 @@
export enum TemporaryPermissionMode { import { TRoles } from "@app/hooks/api/shared";
Relative = "relative"
}
export type TOrgIdentityMembership = { export type TOrgIdentityMembership = {
id: string; id: string;
@@ -12,21 +10,24 @@ export type TOrgIdentityMembership = {
export type TCreateOrgIdentityMembershipDTO = { export type TCreateOrgIdentityMembershipDTO = {
identityId: string; identityId: string;
roles: Array< roles: TRoles;
| {
role: string;
isTemporary?: false;
}
| {
role: string;
isTemporary: true;
temporaryMode: TemporaryPermissionMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
>;
}; };
export type TDeleteOrgIdentityMembershipDTO = { export type TDeleteOrgIdentityMembershipDTO = {
identityId: string; identityId: string;
}; };
export type TListOrgIdentityMembershipsDTO = {
offset?: number;
limit?: number;
identityName?: string;
roles?: string[];
};
export type TListAvailableOrganizationIdentitiesDTO = {
offset?: number;
limit?: number;
identityName?: string;
};
export type TAvailableOrganizationIdentities = Array<{ id: string; name: string }>;
@@ -6,7 +6,6 @@ export {
useDeleteOrgById, useDeleteOrgById,
useDeleteOrgPmtMethod, useDeleteOrgPmtMethod,
useDeleteOrgTaxId, useDeleteOrgTaxId,
useGetAvailableOrgIdentities,
useGetIdentityMembershipOrgs, useGetIdentityMembershipOrgs,
useGetOrganizationGroups, useGetOrganizationGroups,
useGetOrganizations, useGetOrganizations,
@@ -579,19 +579,6 @@ export const useGetOrgIntegrationAuths = <TData = IntegrationAuth[],>(
}); });
}; };
export const useGetAvailableOrgIdentities = (enabled = true) =>
useQuery({
queryKey: organizationKeys.getAvailableIdentities(),
queryFn: async () => {
const { data } = await apiRequest.get<{ identities: { name: string; id: string }[] }>(
"/api/v1/organization/identities/available"
);
return data.identities;
},
enabled
});
export const useGetAvailableOrgUsers = (enabled = true) => export const useGetAvailableOrgUsers = (enabled = true) =>
useQuery({ useQuery({
queryKey: organizationKeys.getAvailableUsers(), queryKey: organizationKeys.getAvailableUsers(),
@@ -1,15 +0,0 @@
export {
useCreateOrganizationIdentity,
useDeleteOrganizationIdentity,
useUpdateOrganizationIdentity
} from "./mutations";
export { organizationIdentityQuery } from "./queries";
export type {
TCreateOrganizationIdentityDTO,
TDeleteOrganizationIdentityDTO,
TGetOrganizationIdentityByIdDTO,
TListOrganizationIdentitiesDTO,
TMetadata,
TOrganizationIdentity,
TUpdateOrganizationIdentityDTO
} from "./types";
@@ -1,58 +0,0 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { organizationIdentityQuery } from "./queries";
import {
TCreateOrganizationIdentityDTO,
TDeleteOrganizationIdentityDTO,
TOrganizationIdentity,
TUpdateOrganizationIdentityDTO
} from "./types";
export const useCreateOrganizationIdentity = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (dto: TCreateOrganizationIdentityDTO) => {
const { data } = await apiRequest.post<{ identity: TOrganizationIdentity }>(
"/api/v1/organization/identities",
dto
);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: organizationIdentityQuery.allKey() });
}
});
};
export const useUpdateOrganizationIdentity = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId, ...updates }: TUpdateOrganizationIdentityDTO) => {
const { data } = await apiRequest.patch<{ identity: TOrganizationIdentity }>(
`/api/v1/organization/identities/${identityId}`,
updates
);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: organizationIdentityQuery.allKey() });
}
});
};
export const useDeleteOrganizationIdentity = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId }: TDeleteOrganizationIdentityDTO) => {
const { data } = await apiRequest.delete<{ identity: TOrganizationIdentity }>(
`/api/v1/organization/identities/${identityId}`
);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: organizationIdentityQuery.allKey() });
}
});
};
@@ -1,44 +0,0 @@
import { queryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import {
TGetOrganizationIdentityByIdDTO,
TListOrganizationIdentitiesDTO,
TOrganizationIdentity
} from "./types";
export const organizationIdentityQuery = {
allKey: () => ["organization-identities"] as const,
getByIdKey: (params: TGetOrganizationIdentityByIdDTO) =>
[...organizationIdentityQuery.allKey(), "by-id", params] as const,
listKey: (params?: TListOrganizationIdentitiesDTO) =>
[...organizationIdentityQuery.allKey(), "list", params] as const,
getById: (params: TGetOrganizationIdentityByIdDTO) =>
queryOptions({
queryKey: organizationIdentityQuery.getByIdKey(params),
queryFn: async () => {
const { data } = await apiRequest.get<{ identity: TOrganizationIdentity }>(
`/api/v1/organization/identities/${params.identityId}`
);
return data.identity;
}
}),
list: (params: TListOrganizationIdentitiesDTO = {}) =>
queryOptions({
queryKey: organizationIdentityQuery.listKey(params),
queryFn: async () => {
const { data } = await apiRequest.get<{
identities: TOrganizationIdentity[];
totalCount: number;
}>("/api/v1/organization/identities", {
params: {
offset: params.offset,
limit: params.limit,
search: params.search
}
});
return data;
}
})
};
@@ -1,43 +0,0 @@
export type TMetadata = {
key: string;
value: string;
};
export type TOrganizationIdentity = {
id: string;
name: string;
orgId: string;
projectId: string | null;
createdAt: string;
updatedAt: string;
hasDeleteProtection: boolean;
authMethods?: string[];
metadata?: TMetadata[];
};
export type TCreateOrganizationIdentityDTO = {
name: string;
hasDeleteProtection?: boolean;
metadata?: TMetadata[];
};
export type TUpdateOrganizationIdentityDTO = {
identityId: string;
name?: string;
hasDeleteProtection?: boolean;
metadata?: TMetadata[];
};
export type TGetOrganizationIdentityByIdDTO = {
identityId: string;
};
export type TListOrganizationIdentitiesDTO = {
offset?: number;
limit?: number;
search?: string;
};
export type TDeleteOrganizationIdentityDTO = {
identityId: string;
};
@@ -1,6 +1,8 @@
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { identitiesKeys, projectKeys } from "@app/hooks/api";
import { subscriptionQueryKeys } from "@app/hooks/api/subscriptions/queries";
import { projectIdentityQuery } from "./queries"; import { projectIdentityQuery } from "./queries";
import { import {
@@ -18,10 +20,13 @@ export const useCreateProjectIdentity = () => {
`/api/v1/projects/${projectId}/identities`, `/api/v1/projects/${projectId}/identities`,
dto dto
); );
return data; return data.identity;
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() }); queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.all()
});
} }
}); });
}; };
@@ -34,10 +39,13 @@ export const useUpdateProjectIdentity = () => {
`/api/v1/projects/${projectId}/identities/${identityId}`, `/api/v1/projects/${projectId}/identities/${identityId}`,
updates updates
); );
return data; return data.identity;
}, },
onSuccess: () => { onSuccess: (_, { projectId, identityId }) => {
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() }); queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId)
});
} }
}); });
}; };
@@ -49,10 +57,20 @@ export const useDeleteProjectIdentity = () => {
const { data } = await apiRequest.delete<{ identity: TProjectIdentity }>( const { data } = await apiRequest.delete<{ identity: TProjectIdentity }>(
`/api/v1/projects/${projectId}/identities/${identityId}` `/api/v1/projects/${projectId}/identities/${identityId}`
); );
return data; return data.identity;
}, },
onSuccess: () => { onSuccess: (_, { projectId, identityId }) => {
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() }); queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.all()
});
} }
}); });
}; };
@@ -2,11 +2,7 @@ import { queryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { import { TGetProjectIdentityByIdDTO, TListProjectIdentitiesDTO, TProjectIdentity } from "./types";
TGetProjectIdentityByIdDTO,
TListProjectIdentitiesDTO,
TProjectIdentity
} from "./types";
export const projectIdentityQuery = { export const projectIdentityQuery = {
allKey: () => ["project-identities"] as const, allKey: () => ["project-identities"] as const,
@@ -1,28 +1,18 @@
import { TIdentity, TMetadata } from "@app/hooks/api/shared";
export type TProjectIdentityMetadata = { export type TProjectIdentityMetadata = {
key: string; key: string;
value: string; value: string;
id: string; id: string;
}; };
export type TProjectIdentity = { export type TProjectIdentity = TIdentity;
id: string;
name: string;
orgId: string;
projectId: string | null;
createdAt: string;
updatedAt: string;
hasDeleteProtection: boolean;
metadata?: TProjectIdentityMetadata[];
};
export type TCreateProjectIdentityDTO = { export type TCreateProjectIdentityDTO = {
projectId: string; projectId: string;
name: string; name: string;
hasDeleteProtection?: boolean; hasDeleteProtection?: boolean;
metadata?: Array<{ metadata?: TMetadata[];
key: string;
value: string;
}>;
}; };
export type TUpdateProjectIdentityDTO = { export type TUpdateProjectIdentityDTO = {
@@ -30,10 +20,7 @@ export type TUpdateProjectIdentityDTO = {
identityId: string; identityId: string;
name?: string; name?: string;
hasDeleteProtection?: boolean; hasDeleteProtection?: boolean;
metadata?: Array<{ metadata?: TMetadata[];
key: string;
value: string;
}>;
}; };
export type TGetProjectIdentityByIdDTO = { export type TGetProjectIdentityByIdDTO = {
@@ -0,0 +1,3 @@
export * from "./mutations";
export * from "./queries";
export * from "./types";
@@ -0,0 +1,93 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { identitiesKeys, projectKeys } from "@app/hooks/api";
import { projectIdentityQuery } from "@app/hooks/api/projectIdentity";
import {
TCreateProjectIdentityMembershipDTO,
TDeleteProjectIdentityMembershipDTO,
TProjectIdentityMembership,
TUpdateProjectIdentityMembershipDTO
} from "./types";
export const useCreateProjectIdentityMembership = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId, projectId, role }: TCreateProjectIdentityMembershipDTO) => {
const {
data: { identityMembership }
} = await apiRequest.post<{ identityMembership: TProjectIdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`,
{
role
}
);
return identityMembership;
},
onSuccess: (_, { identityId, projectId }) => {
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
}
});
};
export const useUpdateProjectIdentityMembership = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
projectId,
identityId,
...updates
}: TUpdateProjectIdentityMembershipDTO) => {
const {
data: { identityMembership }
} = await apiRequest.patch<{ identityMembership: TProjectIdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`,
updates
);
return identityMembership;
},
onSuccess: (_, { projectId, identityId }) => {
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId)
});
}
});
};
export const useDeleteProjectIdentityMembership = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId, projectId }: TDeleteProjectIdentityMembershipDTO) => {
const {
data: { identityMembership }
} = await apiRequest.delete<{ identityMembership: TProjectIdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
);
return identityMembership;
},
onSuccess: (_, { identityId, projectId }) => {
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
}
});
};
@@ -0,0 +1,101 @@
import { queryOptions, useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import {
projectKeys,
TAvailableProjectIdentities,
TListAvailableProjectIdentitiesDTO
} from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import {
IdentityProjectMembership,
TProjectIdentityMembershipsList
} from "@app/hooks/api/identities/types";
import { ProjectIdentityOrderBy, TListProjectIdentitiesDTO } from "@app/hooks/api/projects/types";
export const projectIdentityMembershipQuery = {
allKey: () => ["project-identity-memberships"] as const,
listAvailableKey: (params?: TListAvailableProjectIdentitiesDTO) =>
[...projectIdentityMembershipQuery.allKey(), "list-available", params] as const,
listAvailable: (params: TListAvailableProjectIdentitiesDTO) =>
queryOptions({
queryKey: projectIdentityMembershipQuery.listAvailableKey(params),
queryFn: async () => {
const { data } = await apiRequest.get<{
identities: TAvailableProjectIdentities;
}>(`/api/v1/projects/${params.projectId}/available-identities`, {
params: {
offset: params.offset,
limit: params.limit,
identityName: params.identityName
}
});
return data.identities;
}
})
};
// TODO (scott/akhi): move to new projectIdentityMembershipQuery structure
export const useListProjectIdentityMemberships = (
{
projectId,
offset = 0,
limit = 100,
orderBy = ProjectIdentityOrderBy.Name,
orderDirection = OrderByDirection.ASC,
search = ""
}: TListProjectIdentitiesDTO,
options?: Omit<
UseQueryOptions<
TProjectIdentityMembershipsList,
unknown,
TProjectIdentityMembershipsList,
ReturnType<typeof projectKeys.getProjectIdentityMembershipsWithParams>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: projectKeys.getProjectIdentityMembershipsWithParams({
projectId,
offset,
limit,
orderBy,
orderDirection,
search
}),
queryFn: async () => {
const params = new URLSearchParams({
offset: String(offset),
limit: String(limit),
orderBy: String(orderBy),
orderDirection: String(orderDirection),
search: String(search)
});
const { data } = await apiRequest.get<TProjectIdentityMembershipsList>(
`/api/v1/projects/${projectId}/identity-memberships`,
{ params }
);
return data;
},
enabled: true,
...options
});
};
export const useGetProjectIdentityMembership = (projectId: string, identityId: string) => {
return useQuery({
enabled: Boolean(projectId && identityId),
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId),
queryFn: async () => {
const {
data: { identityMembership }
} = await apiRequest.get<{ identityMembership: IdentityProjectMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
);
return identityMembership;
}
});
};
@@ -0,0 +1,36 @@
import { TRoles } from "@app/hooks/api/shared";
export type TProjectIdentityMembership = {
id: string;
projectId: string;
identityId: string;
createdAt: string;
updatedAt: string;
// TODO
};
export type TCreateProjectIdentityMembershipDTO = {
identityId: string;
projectId: string;
role?: string;
};
export type TUpdateProjectIdentityMembershipDTO = {
identityId: string;
projectId: string;
roles: TRoles;
};
export type TDeleteProjectIdentityMembershipDTO = {
identityId: string;
projectId: string;
};
export type TListAvailableProjectIdentitiesDTO = {
projectId: string;
offset?: number;
limit?: number;
identityName?: string;
};
export type TAvailableProjectIdentities = Array<{ id: string; name: string }>;
@@ -8,10 +8,8 @@ export {
useUpdateProjectSshConfig useUpdateProjectSshConfig
} from "./mutations"; } from "./mutations";
export { export {
useAddIdentityToWorkspace,
useCreateWorkspace, useCreateWorkspace,
useCreateWsEnvironment, useCreateWsEnvironment,
useDeleteIdentityFromWorkspace,
useDeleteUserFromWorkspace, useDeleteUserFromWorkspace,
useDeleteWorkspace, useDeleteWorkspace,
useDeleteWsEnvironment, useDeleteWsEnvironment,
@@ -21,8 +19,6 @@ export {
useGetUserWorkspaceMemberships, useGetUserWorkspaceMemberships,
useGetWorkspaceAuthorizations, useGetWorkspaceAuthorizations,
useGetWorkspaceById, useGetWorkspaceById,
useGetWorkspaceIdentityMembershipDetails,
useGetWorkspaceIdentityMemberships,
useGetWorkspaceIndexStatus, useGetWorkspaceIndexStatus,
useGetWorkspaceIntegrations, useGetWorkspaceIntegrations,
useGetWorkspaceUserDetails, useGetWorkspaceUserDetails,
@@ -41,7 +37,6 @@ export {
useListWorkspaceSshHostGroups, useListWorkspaceSshHostGroups,
useListWorkspaceSshHosts, useListWorkspaceSshHosts,
useSearchProjects, useSearchProjects,
useUpdateIdentityWorkspaceRole,
useUpdateProject, useUpdateProject,
useUpdateUserWorkspaceRole, useUpdateUserWorkspaceRole,
useUpdateWsEnvironment, useUpdateWsEnvironment,
+1 -155
View File
@@ -1,15 +1,12 @@
import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { CaStatus } from "../ca/enums"; import { CaStatus } from "../ca/enums";
import { TCertificateAuthority } from "../ca/types"; import { TCertificateAuthority } from "../ca/types";
import { TCertificate } from "../certificates/types"; import { TCertificate } from "../certificates/types";
import { TCertificateTemplate } from "../certificateTemplates/types"; import { TCertificateTemplate } from "../certificateTemplates/types";
import { TGroupMembership } from "../groups/types"; import { TGroupMembership } from "../groups/types";
import { identitiesKeys } from "../identities/queries";
import { IdentityMembership, TProjectIdentitiesList } from "../identities/types";
import { IntegrationAuth } from "../integrationAuth/types"; import { IntegrationAuth } from "../integrationAuth/types";
import { TIntegration } from "../integrations/types"; import { TIntegration } from "../integrations/types";
import { TPkiAlert } from "../pkiAlerts/types"; import { TPkiAlert } from "../pkiAlerts/types";
@@ -33,13 +30,10 @@ import {
DeleteWorkspaceDTO, DeleteWorkspaceDTO,
Project, Project,
ProjectEnv, ProjectEnv,
ProjectIdentityOrderBy,
ProjectType, ProjectType,
TGetUpgradeProjectStatusDTO, TGetUpgradeProjectStatusDTO,
TListProjectIdentitiesDTO,
TProjectSshConfig, TProjectSshConfig,
TSearchProjectsDTO, TSearchProjectsDTO,
TUpdateWorkspaceIdentityRoleDTO,
TUpdateWorkspaceUserRoleDTO, TUpdateWorkspaceUserRoleDTO,
UpdateAuditLogsRetentionDTO, UpdateAuditLogsRetentionDTO,
UpdateEnvironmentDTO, UpdateEnvironmentDTO,
@@ -452,154 +446,6 @@ export const useUpdateUserWorkspaceRole = () => {
}); });
}; };
export const useAddIdentityToWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
identityId,
projectId,
role
}: {
identityId: string;
projectId: string;
role?: string;
}) => {
const {
data: { identityMembership }
} = await apiRequest.post(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`,
{
role
}
);
return identityMembership;
},
onSuccess: (_, { identityId, projectId }) => {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
}
});
};
export const useUpdateIdentityWorkspaceRole = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId, projectId, roles }: TUpdateWorkspaceIdentityRoleDTO) => {
const {
data: { identityMembership }
} = await apiRequest.patch(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`,
{
roles
}
);
return identityMembership;
},
onSuccess: (_, { identityId, projectId }) => {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId)
});
}
});
};
export const useDeleteIdentityFromWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ identityId, projectId }: { identityId: string; projectId: string }) => {
const {
data: { identityMembership }
} = await apiRequest.delete(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
);
return identityMembership;
},
onSuccess: (_, { identityId, projectId }) => {
queryClient.invalidateQueries({
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
});
queryClient.invalidateQueries({
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
});
}
});
};
export const useGetWorkspaceIdentityMemberships = (
{
projectId,
offset = 0,
limit = 100,
orderBy = ProjectIdentityOrderBy.Name,
orderDirection = OrderByDirection.ASC,
search = ""
}: TListProjectIdentitiesDTO,
options?: Omit<
UseQueryOptions<
TProjectIdentitiesList,
unknown,
TProjectIdentitiesList,
ReturnType<typeof projectKeys.getProjectIdentityMembershipsWithParams>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: projectKeys.getProjectIdentityMembershipsWithParams({
projectId,
offset,
limit,
orderBy,
orderDirection,
search
}),
queryFn: async () => {
const params = new URLSearchParams({
offset: String(offset),
limit: String(limit),
orderBy: String(orderBy),
orderDirection: String(orderDirection),
search: String(search)
});
const { data } = await apiRequest.get<TProjectIdentitiesList>(
`/api/v1/projects/${projectId}/identity-memberships`,
{ params }
);
return data;
},
enabled: true,
...options
});
};
export const useGetWorkspaceIdentityMembershipDetails = (projectId: string, identityId: string) => {
return useQuery({
enabled: Boolean(projectId && identityId),
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId),
queryFn: async () => {
const {
data: { identityMembership }
} = await apiRequest.get<{ identityMembership: IdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
);
return identityMembership;
}
});
};
export const useGetWorkspaceGroupMembershipDetails = (projectId: string, groupId: string) => { export const useGetWorkspaceGroupMembershipDetails = (projectId: string, groupId: string) => {
return useQuery({ return useQuery({
enabled: Boolean(projectId && groupId), enabled: Boolean(projectId && groupId),
-18
View File
@@ -138,24 +138,6 @@ export type TUpdateWorkspaceUserRoleDTO = {
)[]; )[];
}; };
export type TUpdateWorkspaceIdentityRoleDTO = {
identityId: string;
projectId: string;
roles: (
| {
role: string;
isTemporary?: false;
}
| {
role: string;
isTemporary: true;
temporaryMode: ProjectUserMembershipTemporaryMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
)[];
};
export type TUpdateWorkspaceGroupRoleDTO = { export type TUpdateWorkspaceGroupRoleDTO = {
groupId: string; groupId: string;
projectId: string; projectId: string;
+1
View File
@@ -0,0 +1 @@
export * from "./types";
+37
View File
@@ -0,0 +1,37 @@
import { IdentityAuthMethod } from "@app/hooks/api";
export enum TemporaryPermissionMode {
Relative = "relative"
}
export type TMetadata = {
key: string;
value: string;
};
export type TIdentity = {
id: string;
name: string;
orgId: string;
projectId: string | null;
createdAt: string;
updatedAt: string;
hasDeleteProtection: boolean;
authMethods: IdentityAuthMethod[];
activeLockoutAuthMethods: string[];
metadata?: Array<TMetadata & { id: string }>;
};
export type TRoles = Array<
| {
role: string;
isTemporary?: false;
}
| {
role: string;
isTemporary: true;
temporaryMode: TemporaryPermissionMode;
temporaryRange: string;
temporaryAccessStartTime: string;
}
>;
@@ -7,7 +7,8 @@ import { SubscriptionPlan } from "./types";
// import { Workspace } from './types'; // import { Workspace } from './types';
export const subscriptionQueryKeys = { export const subscriptionQueryKeys = {
getOrgSubsription: (orgID: string) => ["plan", { orgID }] as const all: () => ["plan"] as const,
getOrgSubsription: (orgID: string) => [...subscriptionQueryKeys.all(), { orgID }] as const
}; };
export const fetchOrgSubscription = async (orgID: string, refreshCache: boolean = false) => { export const fetchOrgSubscription = async (orgID: string, refreshCache: boolean = false) => {
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -76,7 +77,9 @@ export const IdentityAliCloudAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityAliCloudAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityAliCloudAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAliCloudAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityAliCloudAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -144,7 +147,7 @@ export const IdentityAliCloudAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
allowedArns, allowedArns,
identityId, identityId,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
@@ -154,7 +157,7 @@ export const IdentityAliCloudAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
allowedArns, allowedArns,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -78,7 +79,9 @@ export const IdentityAwsAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -154,7 +157,7 @@ export const IdentityAwsAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
stsEndpoint, stsEndpoint,
allowedPrincipalArns, allowedPrincipalArns,
allowedAccountIds, allowedAccountIds,
@@ -166,7 +169,7 @@ export const IdentityAwsAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
stsEndpoint: stsEndpoint || "", stsEndpoint: stsEndpoint || "",
allowedPrincipalArns: allowedPrincipalArns || "", allowedPrincipalArns: allowedPrincipalArns || "",
@@ -176,10 +179,8 @@ export const IdentityAwsAuthForm = ({
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit), accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
accessTokenTrustedIps accessTokenTrustedIps
}); });
handlePopUpToggle("identityAuthMethod", false);
} }
handlePopUpToggle("identityAuthMethod", false);
createNotification({ createNotification({
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`, text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
type: "success" type: "success"
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -73,7 +74,9 @@ export const IdentityAzureAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -150,7 +153,7 @@ export const IdentityAzureAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
tenantId, tenantId,
resource, resource,
@@ -162,7 +165,7 @@ export const IdentityAzureAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
tenantId: tenantId || "", tenantId: tenantId || "",
resource: resource || "", resource: resource || "",
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -76,7 +77,9 @@ export const IdentityGcpAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -160,7 +163,7 @@ export const IdentityGcpAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
identityId, identityId,
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
type, type,
allowedServiceAccounts, allowedServiceAccounts,
allowedProjects, allowedProjects,
@@ -173,7 +176,7 @@ export const IdentityGcpAuthForm = ({
} else { } else {
await addMutateAsync({ await addMutateAsync({
identityId, identityId,
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
type, type,
allowedServiceAccounts: allowedServiceAccounts || "", allowedServiceAccounts: allowedServiceAccounts || "",
allowedProjects: allowedProjects || "", allowedProjects: allowedProjects || "",
@@ -191,7 +194,6 @@ export const IdentityGcpAuthForm = ({
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`, text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
type: "success" type: "success"
}); });
reset(); reset();
}; };
@@ -4,6 +4,7 @@ import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -109,7 +110,9 @@ export const IdentityJwtAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityJwtAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityJwtAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityJwtAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityJwtAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -227,7 +230,7 @@ export const IdentityJwtAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
identityId, identityId,
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
configurationType, configurationType,
jwksUrl, jwksUrl,
jwksCaCert, jwksCaCert,
@@ -252,7 +255,7 @@ export const IdentityJwtAuthForm = ({
boundAudiences, boundAudiences,
boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])), boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])),
boundSubject, boundSubject,
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
accessTokenMaxTTL: Number(accessTokenMaxTTL), accessTokenMaxTTL: Number(accessTokenMaxTTL),
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit), accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
@@ -266,7 +269,6 @@ export const IdentityJwtAuthForm = ({
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`, text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
type: "success" type: "success"
}); });
reset(); reset();
}; };
@@ -4,6 +4,7 @@ import { faInfoCircle, faPlus, faXmark } from "@fortawesome/free-solid-svg-icons
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -117,7 +118,9 @@ export const IdentityKubernetesAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -318,7 +321,7 @@ export const IdentityKubernetesAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
...(tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Api ...(tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Api
? { ? {
kubernetesHost: kubernetesHost || "" kubernetesHost: kubernetesHost || ""
@@ -341,7 +344,7 @@ export const IdentityKubernetesAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
...(tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Api ...(tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Api
? { ? {
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faQuestionCircle, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faQuestionCircle, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import ms from "ms"; import ms from "ms";
import { z } from "zod"; import { z } from "zod";
@@ -168,7 +169,9 @@ export const IdentityLdapAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityLdapAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityLdapAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityLdapAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityLdapAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -345,7 +348,7 @@ export const IdentityLdapAuthForm = ({
ms(`${lockoutCounterResetValue}${lockoutCounterResetUnit}`) / 1000; ms(`${lockoutCounterResetValue}${lockoutCounterResetUnit}`) / 1000;
const basePayload = { const basePayload = {
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
searchFilter, searchFilter,
ldapCaCertificate, ldapCaCertificate,
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -84,7 +85,9 @@ export const IdentityOciAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityOciAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityOciAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOciAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityOciAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -156,7 +159,7 @@ export const IdentityOciAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
tenancyOcid, tenancyOcid,
allowedUsernames, allowedUsernames,
identityId, identityId,
@@ -167,7 +170,7 @@ export const IdentityOciAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
tenancyOcid, tenancyOcid,
allowedUsernames: allowedUsernames || undefined, allowedUsernames: allowedUsernames || undefined,
@@ -184,7 +187,6 @@ export const IdentityOciAuthForm = ({
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`, text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
type: "success" type: "success"
}); });
reset(); reset();
}; };
@@ -4,6 +4,7 @@ import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -96,7 +97,9 @@ export const IdentityOidcAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -211,7 +214,7 @@ export const IdentityOidcAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
identityId, identityId,
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
oidcDiscoveryUrl, oidcDiscoveryUrl,
caCert, caCert,
boundIssuer, boundIssuer,
@@ -238,7 +241,7 @@ export const IdentityOidcAuthForm = ({
? Object.fromEntries(claimMetadataMapping.map((entry) => [entry.key, entry.value])) ? Object.fromEntries(claimMetadataMapping.map((entry) => [entry.key, entry.value]))
: undefined, : undefined,
boundSubject, boundSubject,
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
accessTokenMaxTTL: Number(accessTokenMaxTTL), accessTokenMaxTTL: Number(accessTokenMaxTTL),
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit), accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
@@ -252,7 +255,6 @@ export const IdentityOidcAuthForm = ({
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`, text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
type: "success" type: "success"
}); });
reset(); reset();
}; };
@@ -1,10 +1,18 @@
import { faLink, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faChevronDown, faLink, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal, Modal, ModalContent } from "@app/components/v2"; import {
Button,
DeleteActionModal,
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
Modal,
ModalContent
} from "@app/components/v2";
import { DocumentationLinkBadge } from "@app/components/v3"; import { DocumentationLinkBadge } from "@app/components/v3";
import { import {
OrgPermissionIdentityActions, OrgPermissionIdentityActions,
@@ -14,17 +22,17 @@ import {
} from "@app/context"; } from "@app/context";
import { OrgPermissionMachineIdentityAuthTemplateActions } from "@app/context/OrgPermissionContext/types"; import { OrgPermissionMachineIdentityAuthTemplateActions } from "@app/context/OrgPermissionContext/types";
import { withPermission } from "@app/hoc"; import { withPermission } from "@app/hoc";
import { useDeleteIdentity } from "@app/hooks/api"; import { useDeleteOrgIdentity } from "@app/hooks/api";
import { useDeleteIdentityAuthTemplate } from "@app/hooks/api/identityAuthTemplates"; import { useDeleteIdentityAuthTemplate } from "@app/hooks/api/identityAuthTemplates";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { IdentityAuthTemplateModal } from "./IdentityAuthTemplateModal"; import { IdentityAuthTemplateModal } from "./IdentityAuthTemplateModal";
import { IdentityAuthTemplatesTable } from "./IdentityAuthTemplatesTable"; import { IdentityAuthTemplatesTable } from "./IdentityAuthTemplatesTable";
import { IdentityLinkForm } from "./IdentityLinkForm";
import { IdentityModal } from "./IdentityModal";
import { IdentityTable } from "./IdentityTable"; import { IdentityTable } from "./IdentityTable";
import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal"; import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal";
import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal"; import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal";
import { OrgIdentityLinkForm } from "./OrgIdentityLinkForm";
import { OrgIdentityModal } from "./OrgIdentityModal";
export const IdentitySection = withPermission( export const IdentitySection = withPermission(
() => { () => {
@@ -32,7 +40,7 @@ export const IdentitySection = withPermission(
const { currentOrg, isSubOrganization } = useOrganization(); const { currentOrg, isSubOrganization } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { mutateAsync: deleteMutateAsync } = useDeleteIdentity(); const { mutateAsync: deleteMutateAsync } = useDeleteOrgIdentity();
const { mutateAsync: deleteTemplateMutateAsync } = useDeleteIdentityAuthTemplate(); const { mutateAsync: deleteTemplateMutateAsync } = useDeleteIdentityAuthTemplate();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"identity", "identity",
@@ -46,7 +54,8 @@ export const IdentitySection = withPermission(
"editTemplate", "editTemplate",
"deleteTemplate", "deleteTemplate",
"viewUsages", "viewUsages",
"linkIdentity" "linkIdentity",
"addOptions"
] as const); ] as const);
const isMoreIdentitiesAllowed = subscription?.identityLimit const isMoreIdentitiesAllowed = subscription?.identityLimit
@@ -58,7 +67,7 @@ export const IdentitySection = withPermission(
const onDeleteIdentitySubmit = async (identityId: string) => { const onDeleteIdentitySubmit = async (identityId: string) => {
await deleteMutateAsync({ await deleteMutateAsync({
identityId, identityId,
organizationId: orgId orgId
}); });
createNotification({ createNotification({
@@ -91,50 +100,70 @@ export const IdentitySection = withPermission(
<p className="text-xl font-medium text-mineshaft-100">Identities</p> <p className="text-xl font-medium text-mineshaft-100">Identities</p>
<DocumentationLinkBadge href="https://infisical.com/docs/documentation/platform/identities/machine-identities" /> <DocumentationLinkBadge href="https://infisical.com/docs/documentation/platform/identities/machine-identities" />
</div> </div>
{isSubOrganization && ( <div className="flex items-center">
<OrgPermissionCan <OrgPermissionCan
I={OrgPermissionIdentityActions.Create} I={OrgPermissionIdentityActions.Create}
a={OrgPermissionSubjects.Identity} a={OrgPermissionSubjects.Identity}
> >
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
variant="plain" variant="outline_bg"
colorSchema="secondary" className={isSubOrganization ? "rounded-r-none" : ""}
leftIcon={<FontAwesomeIcon icon={faLink} />} type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => { onClick={() => {
handlePopUpOpen("linkIdentity"); if (!isMoreIdentitiesAllowed && !isEnterprise) {
handlePopUpOpen("upgradePlan", {
description:
"You can add more identities if you upgrade your Infisical Pro plan."
});
return;
}
handlePopUpOpen("identity");
}} }}
isDisabled={!isAllowed} isDisabled={!isAllowed}
> >
Link Identity Create Identity
</Button> </Button>
)} )}
</OrgPermissionCan> </OrgPermissionCan>
)} {isSubOrganization && (
<OrgPermissionCan <DropdownMenu
I={OrgPermissionIdentityActions.Create} open={popUp.addOptions.isOpen}
a={OrgPermissionSubjects.Identity} onOpenChange={(isOpen) => handlePopUpToggle("addOptions", isOpen)}
>
{(isAllowed) => (
<Button
colorSchema="secondary"
type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
if (!isMoreIdentitiesAllowed && !isEnterprise) {
handlePopUpOpen("upgradePlan", {
text: "You have reached the maximum number of identities allowed on your current plan. Upgrade to Infisical Pro plan to add more identities."
});
return;
}
handlePopUpOpen("identity");
}}
isDisabled={!isAllowed}
> >
Create Identity <DropdownMenuTrigger>
</Button> <Button
variant="outline_bg"
className="rounded-l-none border-l-mineshaft-800 px-3"
>
<FontAwesomeIcon icon={faChevronDown} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" sideOffset={6} className="p-1">
<OrgPermissionCan
I={OrgPermissionIdentityActions.Create}
a={OrgPermissionSubjects.Identity}
>
{(isAllowed) => (
<Button
variant="outline_bg"
className="w-full"
isDisabled={!isAllowed}
leftIcon={<FontAwesomeIcon icon={faLink} />}
onClick={() => {
handlePopUpClose("addOptions");
handlePopUpOpen("linkIdentity");
}}
>
Assign Org Identity
</Button>
)}
</OrgPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
)} )}
</OrgPermissionCan> </div>
</div> </div>
<IdentityTable handlePopUpOpen={handlePopUpOpen} /> <IdentityTable handlePopUpOpen={handlePopUpOpen} />
</div> </div>
@@ -173,7 +202,7 @@ export const IdentitySection = withPermission(
</div> </div>
<IdentityAuthTemplatesTable handlePopUpOpen={handlePopUpOpen} /> <IdentityAuthTemplatesTable handlePopUpOpen={handlePopUpOpen} />
</div> </div>
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<IdentityAuthTemplateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <IdentityAuthTemplateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<MachineAuthTemplateUsagesModal <MachineAuthTemplateUsagesModal
isOpen={popUp.viewUsages.isOpen} isOpen={popUp.viewUsages.isOpen}
@@ -193,10 +222,10 @@ export const IdentitySection = withPermission(
> >
<ModalContent <ModalContent
title="Assign Existing Identity" title="Assign Existing Identity"
subTitle="Assign an existing identity from your root organization to the sub organization. The identity will continue to be managed at its original scope." subTitle="Assign an existing identity from your root organization to this sub organization. The identity will continue to be managed at its original scope."
bodyClassName="overflow-visible" bodyClassName="overflow-visible"
> >
<IdentityLinkForm onClose={() => handlePopUpClose("linkIdentity")} /> <OrgIdentityLinkForm onClose={() => handlePopUpClose("linkIdentity")} />
</ModalContent> </ModalContent>
</Modal> </Modal>
<IdentityTokenAuthTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <IdentityTokenAuthTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
@@ -2,7 +2,6 @@ import { useCallback, useState } from "react";
import { import {
faArrowDown, faArrowDown,
faArrowUp, faArrowUp,
faBuilding,
faCheckCircle, faCheckCircle,
faChevronRight, faChevronRight,
faEdit, faEdit,
@@ -46,6 +45,7 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { Badge, OrgIcon, SubOrgIcon } from "@app/components/v3";
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { import {
getUserTablePreference, getUserTablePreference,
@@ -56,8 +56,8 @@ import { usePagination, useResetPageHelper } from "@app/hooks";
import { import {
identityAuthToNameMap, identityAuthToNameMap,
useGetOrgRoles, useGetOrgRoles,
useSearchIdentities, useSearchOrgIdentityMemberships,
useUpdateIdentity useUpdateOrgIdentity
} from "@app/hooks/api"; } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types"; import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types";
@@ -110,9 +110,9 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
const organizationId = currentOrg?.id || ""; const organizationId = currentOrg?.id || "";
const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); const { mutateAsync: updateMutateAsync } = useUpdateOrgIdentity();
const { data, isPending, isFetching } = useSearchIdentities({ const { data, isPending, isFetching } = useSearchOrgIdentityMemberships({
offset, offset,
limit, limit,
orderDirection, orderDirection,
@@ -357,10 +357,19 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
</Td> </Td>
{isSubOrganization && ( {isSubOrganization && (
<Td> <Td>
<p className="truncate"> <Badge variant="ghost">
<FontAwesomeIcon size="sm" className="mr-1.5" icon={faBuilding} /> {currentOrg.id === orgId ? (
{currentOrg.id === orgId ? "Sub Organization" : "Root Organization"} <>
</p> <SubOrgIcon />
Sub-Organization
</>
) : (
<>
<OrgIcon />
Root Organization
</>
)}
</Badge>
</Td> </Td>
)} )}
<Td> <Td>
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -71,7 +72,9 @@ export const IdentityTlsCertAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityTlsCertAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityTlsCertAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTlsCertAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityTlsCertAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -141,7 +144,7 @@ export const IdentityTlsCertAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
caCertificate, caCertificate,
allowedCommonNames: allowedCommonNames || null, allowedCommonNames: allowedCommonNames || null,
identityId, identityId,
@@ -152,7 +155,7 @@ export const IdentityTlsCertAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
caCertificate, caCertificate,
allowedCommonNames: allowedCommonNames || undefined, allowedCommonNames: allowedCommonNames || undefined,
@@ -169,7 +172,6 @@ export const IdentityTlsCertAuthForm = ({
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`, text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
type: "success" type: "success"
}); });
reset(); reset();
}; };
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -70,7 +71,9 @@ export const IdentityTokenAuthForm = ({
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { projectId } = useParams({
strict: false
});
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth(); const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration); const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
@@ -134,7 +137,7 @@ export const IdentityTokenAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
accessTokenMaxTTL: Number(accessTokenMaxTTL), accessTokenMaxTTL: Number(accessTokenMaxTTL),
@@ -143,7 +146,7 @@ export const IdentityTokenAuthForm = ({
}); });
} else { } else {
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
accessTokenMaxTTL: Number(accessTokenMaxTTL), accessTokenMaxTTL: Number(accessTokenMaxTTL),
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useParams } from "@tanstack/react-router";
import ms from "ms"; import ms from "ms";
import { z } from "zod"; import { z } from "zod";
@@ -105,6 +106,9 @@ export const IdentityUniversalAuthForm = ({
identityId, identityId,
isUpdate isUpdate
}: Props) => { }: Props) => {
const { projectId } = useParams({
strict: false
});
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { subscription } = useSubscription(); const { subscription } = useSubscription();
@@ -232,7 +236,7 @@ export const IdentityUniversalAuthForm = ({
if (data) { if (data) {
// update universal auth configuration // update universal auth configuration
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
clientSecretTrustedIps, clientSecretTrustedIps,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
@@ -249,7 +253,7 @@ export const IdentityUniversalAuthForm = ({
// create new universal auth configuration // create new universal auth configuration
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, ...(projectId ? { projectId } : { organizationId: orgId }),
identityId, identityId,
clientSecretTrustedIps, clientSecretTrustedIps,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
@@ -270,7 +274,6 @@ export const IdentityUniversalAuthForm = ({
text: `Successfully ${isUpdate ? "updated" : "created"} auth method`, text: `Successfully ${isUpdate ? "updated" : "created"} auth method`,
type: "success" type: "success"
}); });
reset(); reset();
}; };
@@ -1,13 +1,15 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, FilterableSelect, FormControl } from "@app/components/v2"; import { Button, FilterableSelect, FormControl } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useOrganization } from "@app/context";
import { useGetAvailableOrgIdentities, useGetOrgRoles } from "@app/hooks/api"; import { useGetOrgRoles } from "@app/hooks/api";
import { useCreateOrgIdentityMembership } from "@app/hooks/api/orgIdentityMembership"; import { useCreateOrgIdentityMembership } from "@app/hooks/api/orgIdentityMembership";
import { orgIdentityMembershipQuery } from "@app/hooks/api/orgIdentityMembership/queries";
const schema = z const schema = z
.object({ .object({
@@ -22,15 +24,26 @@ type Props = {
onClose: () => void; onClose: () => void;
}; };
export const IdentityLinkForm = ({ onClose }: Props) => { export const OrgIdentityLinkForm = ({ onClose }: Props) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { data: roles } = useGetOrgRoles(orgId); const { data: roles } = useGetOrgRoles(orgId);
// const [searchValue, setSearchValue] = useState("");
//
// const [debouncedSearchValue] = useDebounce(searchValue);
const { mutateAsync: createMutateAsync } = useCreateOrgIdentityMembership(); const { mutateAsync: createMutateAsync } = useCreateOrgIdentityMembership();
const { data: rootOrgIdentities, isPending: isRootOrgLoading } = useGetAvailableOrgIdentities();
// TODO: name filter needs to be implemented on backend
const { data: rootOrgIdentities, isPending: isRootOrgLoading } = useQuery({
...orgIdentityMembershipQuery.listAvailable({
// identityName: debouncedSearchValue
}),
placeholderData: (prev) => prev
});
const { const {
control, control,
@@ -69,6 +82,7 @@ export const IdentityLinkForm = ({ onClose }: Props) => {
value={value} value={value}
onChange={onChange} onChange={onChange}
placeholder="Select identity..." placeholder="Select identity..."
// onInputChange={setSearchValue}
options={rootOrgIdentities} options={rootOrgIdentities}
getOptionValue={(option) => option.id} getOptionValue={(option) => option.id}
getOptionLabel={(option) => option.name} getOptionLabel={(option) => option.name}
@@ -94,7 +108,6 @@ export const IdentityLinkForm = ({ onClose }: Props) => {
placeholder="Select role..." placeholder="Select role..."
getOptionValue={(option) => option.slug} getOptionValue={(option) => option.slug}
getOptionLabel={(option) => option.name} getOptionLabel={(option) => option.name}
// menuPortalTarget={document.body}
/> />
</FormControl> </FormControl>
)} )}
@@ -20,7 +20,7 @@ import {
} from "@app/components/v2"; } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useOrganization } from "@app/context";
import { findOrgMembershipRole } from "@app/helpers/roles"; import { findOrgMembershipRole } from "@app/helpers/roles";
import { useCreateIdentity, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api"; import { useCreateOrgIdentity, useGetOrgRoles, useUpdateOrgIdentity } from "@app/hooks/api";
import { useAddIdentityUniversalAuth } from "@app/hooks/api/identities"; import { useAddIdentityUniversalAuth } from "@app/hooks/api/identities";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -47,7 +47,7 @@ type Props = {
handlePopUpToggle: (popUpName: keyof UsePopUpState<["identity"]>, state?: boolean) => void; handlePopUpToggle: (popUpName: keyof UsePopUpState<["identity"]>, state?: boolean) => void;
}; };
export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => { export const OrgIdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
@@ -55,8 +55,8 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
const { data: roles } = useGetOrgRoles(orgId); const { data: roles } = useGetOrgRoles(orgId);
const isOrgIdentity = popUp?.identity?.data ? orgId === popUp?.identity?.data?.orgId : true; const isOrgIdentity = popUp?.identity?.data ? orgId === popUp?.identity?.data?.orgId : true;
const { mutateAsync: createMutateAsync } = useCreateIdentity(); const { mutateAsync: createMutateAsync } = useCreateOrgIdentity();
const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); const { mutateAsync: updateMutateAsync } = useUpdateOrgIdentity();
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth(); const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
const { const {
@@ -10,13 +10,13 @@ import { OrgPermissionCan } from "@app/components/permissions";
import { DeleteActionModal, PageHeader } from "@app/components/v2"; import { DeleteActionModal, PageHeader } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes"; import { ROUTE_PATHS } from "@app/const/routes";
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { useDeleteIdentity, useGetIdentityById } from "@app/hooks/api"; import { useDeleteOrgIdentity, useGetOrgIdentityMembershipById } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { ViewIdentityAuthModal } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityAuthModal"; import { ViewIdentityAuthModal } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityAuthModal";
import { OrgAccessControlTabSections } from "@app/types/org"; import { OrgAccessControlTabSections } from "@app/types/org";
import { IdentityAuthMethodModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal"; import { IdentityAuthMethodModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
import { IdentityModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal"; import { OrgIdentityModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/OrgIdentityModal";
import { import {
IdentityAuthenticationSection, IdentityAuthenticationSection,
IdentityDetailsSection, IdentityDetailsSection,
@@ -31,8 +31,8 @@ const Page = () => {
const identityId = params.identityId as string; const identityId = params.identityId as string;
const { currentOrg, isSubOrganization } = useOrganization(); const { currentOrg, isSubOrganization } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { data } = useGetIdentityById(identityId); const { data } = useGetOrgIdentityMembershipById(identityId);
const { mutateAsync: deleteIdentity } = useDeleteIdentity(); const { mutateAsync: deleteIdentity } = useDeleteOrgIdentity();
const isAuthHidden = orgId !== data?.identity?.orgId; const isAuthHidden = orgId !== data?.identity?.orgId;
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
@@ -46,7 +46,7 @@ const Page = () => {
const onDeleteIdentitySubmit = async (id: string) => { const onDeleteIdentitySubmit = async (id: string) => {
await deleteIdentity({ await deleteIdentity({
identityId: id, identityId: id,
organizationId: orgId orgId
}); });
createNotification({ createNotification({
@@ -100,7 +100,7 @@ const Page = () => {
</div> </div>
</div> </div>
)} )}
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<IdentityAuthMethodModal <IdentityAuthMethodModal
popUp={popUp} popUp={popUp}
handlePopUpOpen={handlePopUpOpen} handlePopUpOpen={handlePopUpOpen}
@@ -4,7 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, Tooltip } from "@app/components/v2"; import { Button, Tooltip } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
import { IdentityAuthMethod, identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api"; import {
IdentityAuthMethod,
identityAuthToNameMap,
useGetOrgIdentityMembershipById
} from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = { type Props = {
@@ -16,7 +20,7 @@ type Props = {
}; };
export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: Props) => { export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: Props) => {
const { data, refetch } = useGetIdentityById(identityId); const { data, refetch } = useGetOrgIdentityMembershipById(identityId);
return data ? ( return data ? (
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
@@ -7,9 +7,9 @@ import { Button, IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context"; import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { import {
useGetIdentityById,
useGetIdentityUniversalAuth, useGetIdentityUniversalAuth,
useGetIdentityUniversalAuthClientSecrets useGetIdentityUniversalAuthClientSecrets,
useGetOrgIdentityMembershipById
} from "@app/hooks/api"; } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -30,7 +30,7 @@ export const IdentityClientSecrets = ({ identityId, handlePopUpOpen }: Props) =>
initialState: "Copy Client ID to clipboard" initialState: "Copy Client ID to clipboard"
}); });
const { data } = useGetIdentityById(identityId); const { data } = useGetOrgIdentityMembershipById(identityId);
const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(identityId); const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(identityId);
const { data: clientSecrets } = useGetIdentityUniversalAuthClientSecrets(identityId); const { data: clientSecrets } = useGetIdentityUniversalAuthClientSecrets(identityId);
return ( return (
@@ -11,7 +11,7 @@ import {
IconButton, IconButton,
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { useGetIdentityById, useGetIdentityTokensTokenAuth } from "@app/hooks/api"; import { useGetIdentityTokensTokenAuth, useGetOrgIdentityMembershipById } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = { type Props = {
@@ -23,7 +23,7 @@ type Props = {
}; };
export const IdentityTokens = ({ identityId, handlePopUpOpen }: Props) => { export const IdentityTokens = ({ identityId, handlePopUpOpen }: Props) => {
const { data } = useGetIdentityById(identityId); const { data } = useGetOrgIdentityMembershipById(identityId);
const { data: tokens } = useGetIdentityTokensTokenAuth(identityId); const { data: tokens } = useGetIdentityTokensTokenAuth(identityId);
return ( return (
<div> <div>
@@ -23,7 +23,7 @@ import {
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api"; import { identityAuthToNameMap, useGetOrgIdentityMembershipById } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = { type Props = {
@@ -41,7 +41,7 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen, isOrgIdent
}); });
const { isSubOrganization } = useOrganization(); const { isSubOrganization } = useOrganization();
const { data } = useGetIdentityById(identityId); const { data } = useGetOrgIdentityMembershipById(identityId);
return data ? ( return data ? (
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4"> <div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
@@ -14,7 +14,7 @@ import {
} from "@app/components/v2"; } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useOrganization } from "@app/context";
import { import {
useAddIdentityToWorkspace, useCreateProjectIdentityMembership,
useGetIdentityProjectMemberships, useGetIdentityProjectMemberships,
useGetProjectRoles, useGetProjectRoles,
useGetUserProjects, useGetUserProjects,
@@ -45,7 +45,7 @@ type Props = {
const Content = ({ identityId, handlePopUpToggle }: Omit<Props, "popUp">) => { const Content = ({ identityId, handlePopUpToggle }: Omit<Props, "popUp">) => {
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const { data: workspaces = [] } = useGetUserProjects(); const { data: workspaces = [] } = useGetUserProjects();
const { mutateAsync: addIdentityToWorkspace } = useAddIdentityToWorkspace(); const { mutateAsync: addIdentityToWorkspace } = useCreateProjectIdentityMembership();
const { const {
control, control,
@@ -9,7 +9,7 @@ import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
import { getProjectBaseURL } from "@app/helpers/project"; import { getProjectBaseURL } from "@app/helpers/project";
import { formatProjectRoleName } from "@app/helpers/roles"; import { formatProjectRoleName } from "@app/helpers/roles";
import { useGetUserProjects } from "@app/hooks/api"; import { useGetUserProjects } from "@app/hooks/api";
import { IdentityMembership } from "@app/hooks/api/identities/types"; import { IdentityProjectMembership } from "@app/hooks/api/identities/types";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
export enum TabSections { export enum TabSections {
@@ -20,7 +20,7 @@ export enum TabSections {
} }
type Props = { type Props = {
membership: IdentityMembership; membership: IdentityProjectMembership;
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>, popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>,
data?: object data?: object
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { DeleteActionModal, IconButton } from "@app/components/v2"; import { DeleteActionModal, IconButton } from "@app/components/v2";
import { useDeleteIdentityFromWorkspace } from "@app/hooks/api"; import { useDeleteProjectIdentityMembership } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { IdentityAddToProjectModal } from "./IdentityAddToProjectModal"; import { IdentityAddToProjectModal } from "./IdentityAddToProjectModal";
@@ -14,7 +14,7 @@ type Props = {
}; };
export const IdentityProjectsSection = ({ identityId }: Props) => { export const IdentityProjectsSection = ({ identityId }: Props) => {
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace(); const { mutateAsync: deleteMutateAsync } = useDeleteProjectIdentityMembership();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"addIdentityToProject", "addIdentityToProject",
@@ -1,11 +1,18 @@
import { useState } from "react"; import { useState } from "react";
import { subject } from "@casl/ability";
import { UseMutationResult } from "@tanstack/react-query"; import { UseMutationResult } from "@tanstack/react-query";
import { useParams } from "@tanstack/react-router";
import ms from "ms"; import ms from "ms";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { VariablePermissionCan } from "@app/components/permissions";
import { Button } from "@app/components/v2"; import { Button } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context"; import {
OrgPermissionIdentityActions,
OrgPermissionSubjects,
ProjectPermissionIdentityActions,
ProjectPermissionSub
} from "@app/context";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay"; import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
@@ -31,7 +38,11 @@ export const LockoutFields = ({
const [lockedOutState, setLockedOutState] = useState(lockedOut); const [lockedOutState, setLockedOutState] = useState(lockedOut);
const clearLockouts = async () => { const { projectId } = useParams({
strict: false
});
async function clearLockouts() {
const deleted = await mutateAsync({ identityId }); const deleted = await mutateAsync({ identityId });
createNotification({ createNotification({
text: `Successfully cleared ${deleted} lockout${deleted === 1 ? "" : "s"}`, text: `Successfully cleared ${deleted} lockout${deleted === 1 ? "" : "s"}`,
@@ -39,13 +50,23 @@ export const LockoutFields = ({
}); });
setLockedOutState(false); setLockedOutState(false);
onResetAllLockouts(); onResetAllLockouts();
}; }
return ( return (
<> <>
<div className="col-span-2 mt-3 flex justify-between border-b border-mineshaft-500 pb-2"> <div className="col-span-2 mt-3 flex justify-between border-b border-mineshaft-500 pb-2">
<span className="text-bunker-300">Lockout Options</span> <span className="text-bunker-300">Lockout Options</span>
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}> <VariablePermissionCan
type={projectId ? "project" : "org"}
I={projectId ? ProjectPermissionIdentityActions.Edit : OrgPermissionIdentityActions.Edit}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
isDisabled={!isAllowed || !lockedOutState || isPending} isDisabled={!isAllowed || !lockedOutState || isPending}
@@ -57,7 +78,7 @@ export const LockoutFields = ({
Reset All Lockouts Reset All Lockouts
</Button> </Button>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
</div> </div>
<IdentityAuthFieldDisplay label="Lockout Threshold"> <IdentityAuthFieldDisplay label="Lockout Threshold">
{data.lockoutThreshold} {data.lockoutThreshold}
@@ -1,10 +1,12 @@
import { useState } from "react"; import { useState } from "react";
import { subject } from "@casl/ability";
import { faBan, faEdit, faKey, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faBan, faEdit, faKey, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useParams } from "@tanstack/react-router";
import { format } from "date-fns"; import { format } from "date-fns";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { VariablePermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DeleteActionModal, DeleteActionModal,
@@ -20,7 +22,12 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context"; import {
OrgPermissionIdentityActions,
OrgPermissionSubjects,
ProjectPermissionIdentityActions,
ProjectPermissionSub
} from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useRevokeIdentityTokenAuthToken } from "@app/hooks/api"; import { useRevokeIdentityTokenAuthToken } from "@app/hooks/api";
import { IdentityAccessToken } from "@app/hooks/api/identities/types"; import { IdentityAccessToken } from "@app/hooks/api/identities/types";
@@ -37,6 +44,10 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
"revokeToken" "revokeToken"
] as const); ] as const);
const { projectId } = useParams({
strict: false
});
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(5); const [perPage, setPerPage] = useState(5);
@@ -68,7 +79,17 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
<div className="col-span-2 mt-3"> <div className="col-span-2 mt-3">
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2"> <div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
<span className="text-bunker-300">Access Tokens</span> <span className="text-bunker-300">Access Tokens</span>
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}> <VariablePermissionCan
type={projectId ? "project" : "org"}
I={projectId ? ProjectPermissionIdentityActions.Edit : OrgPermissionIdentityActions.Edit}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
size="xs" size="xs"
@@ -84,7 +105,7 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
Add Token Add Token
</Button> </Button>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
</div> </div>
<TableContainer className="mt-4 rounded-none border-none"> <TableContainer className="mt-4 rounded-none border-none">
<Table> <Table>
@@ -132,9 +153,20 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
</Td> </Td>
<Td> <Td>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<OrgPermissionCan <VariablePermissionCan
I={OrgPermissionIdentityActions.Edit} type={projectId ? "project" : "org"}
a={OrgPermissionSubjects.Identity} I={
projectId
? ProjectPermissionIdentityActions.Edit
: OrgPermissionIdentityActions.Edit
}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
> >
{(isAllowed) => ( {(isAllowed) => (
<Tooltip content={isAllowed ? "Edit Token" : "Access Restricted"}> <Tooltip content={isAllowed ? "Edit Token" : "Access Restricted"}>
@@ -155,11 +187,22 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
{!isAccessTokenRevoked && ( {!isAccessTokenRevoked && (
<OrgPermissionCan <VariablePermissionCan
I={OrgPermissionIdentityActions.Edit} type={projectId ? "project" : "org"}
a={OrgPermissionSubjects.Identity} I={
projectId
? ProjectPermissionIdentityActions.Edit
: OrgPermissionIdentityActions.Edit
}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
> >
{(isAllowed) => ( {(isAllowed) => (
<Tooltip content={isAllowed ? "Revoke Token" : "Access Restricted"}> <Tooltip content={isAllowed ? "Revoke Token" : "Access Restricted"}>
@@ -181,7 +224,7 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
)} )}
</div> </div>
</Td> </Td>
@@ -1,10 +1,12 @@
import { useState } from "react"; import { useState } from "react";
import { subject } from "@casl/ability";
import { faKey, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faKey, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useParams } from "@tanstack/react-router";
import { format } from "date-fns"; import { format } from "date-fns";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { VariablePermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DeleteActionModal, DeleteActionModal,
@@ -20,7 +22,12 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context"; import {
OrgPermissionIdentityActions,
OrgPermissionSubjects,
ProjectPermissionIdentityActions,
ProjectPermissionSub
} from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useRevokeIdentityUniversalAuthClientSecret } from "@app/hooks/api"; import { useRevokeIdentityUniversalAuthClientSecret } from "@app/hooks/api";
import { ClientSecretData } from "@app/hooks/api/identities/types"; import { ClientSecretData } from "@app/hooks/api/identities/types";
@@ -37,6 +44,10 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
"clientSecret" "clientSecret"
] as const); ] as const);
const { projectId } = useParams({
strict: false
});
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(5); const [perPage, setPerPage] = useState(5);
@@ -60,7 +71,17 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
<div className="col-span-2"> <div className="col-span-2">
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2"> <div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
<span className="text-bunker-300">Client Secrets</span> <span className="text-bunker-300">Client Secrets</span>
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}> <VariablePermissionCan
type={projectId ? "project" : "org"}
I={projectId ? ProjectPermissionIdentityActions.Edit : OrgPermissionIdentityActions.Edit}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
isDisabled={!isAllowed} isDisabled={!isAllowed}
@@ -76,7 +97,7 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
Add Client Secret Add Client Secret
</Button> </Button>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
</div> </div>
<TableContainer className="mt-4 rounded-none border-none"> <TableContainer className="mt-4 rounded-none border-none">
<Table> <Table>
@@ -120,9 +141,20 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
{expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"} {expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"}
</Td> </Td>
<Td> <Td>
<OrgPermissionCan <VariablePermissionCan
I={OrgPermissionIdentityActions.Edit} type={projectId ? "project" : "org"}
a={OrgPermissionSubjects.Identity} I={
projectId
? ProjectPermissionIdentityActions.Edit
: OrgPermissionIdentityActions.Edit
}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
> >
{(isAllowed) => ( {(isAllowed) => (
<Tooltip content={isAllowed ? "Delete Secret" : "Access Restricted"}> <Tooltip content={isAllowed ? "Delete Secret" : "Access Restricted"}>
@@ -143,7 +175,7 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
</IconButton> </IconButton>
</Tooltip> </Tooltip>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
</Td> </Td>
</Tr> </Tr>
); );
@@ -49,6 +49,7 @@ export const ViewIdentityAliCloudAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -1,3 +1,5 @@
import { useParams } from "@tanstack/react-router";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { DeleteActionModal, Modal, ModalContent } from "@app/components/v2"; import { DeleteActionModal, Modal, ModalContent } from "@app/components/v2";
@@ -46,8 +48,7 @@ type Props = {
type TRevokeOptions = { type TRevokeOptions = {
identityId: string; identityId: string;
organizationId: string; } & ({ projectId: string } | { organizationId: string });
};
export const Content = ({ export const Content = ({
identityId, identityId,
@@ -61,7 +62,9 @@ export const Content = ({
>) => { >) => {
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { projectId } = useParams({
strict: false
});
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"revokeAuthMethod", "revokeAuthMethod",
"upgradePlan", "upgradePlan",
@@ -142,7 +145,11 @@ export const Content = ({
const handleDeleteAuthMethod = async () => { const handleDeleteAuthMethod = async () => {
await revokeMethod({ await revokeMethod({
identityId, identityId,
organizationId: orgId ...(projectId
? { projectId }
: {
organizationId: orgId
})
}); });
createNotification({ createNotification({
@@ -46,6 +46,7 @@ export const ViewIdentityAwsAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -46,6 +46,7 @@ export const ViewIdentityAzureAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -1,8 +1,10 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { subject } from "@casl/ability";
import { faChevronDown, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faChevronDown, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useParams } from "@tanstack/react-router";
import { OrgPermissionCan } from "@app/components/permissions"; import { VariablePermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DropdownMenu, DropdownMenu,
@@ -10,15 +12,25 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger DropdownMenuTrigger
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context"; import {
OrgPermissionIdentityActions,
OrgPermissionSubjects,
ProjectPermissionIdentityActions,
ProjectPermissionSub
} from "@app/context";
type Props = { type Props = {
children: ReactNode; children: ReactNode;
onEdit: VoidFunction; onEdit: VoidFunction;
onDelete: VoidFunction; onDelete: VoidFunction;
identityId: string;
}; };
export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props) => { export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit, identityId }: Props) => {
const { projectId } = useParams({
strict: false
});
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div> <div>
@@ -36,9 +48,20 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className="mt-3 min-w-[120px]" align="end"> <DropdownMenuContent className="mt-3 min-w-[120px]" align="end">
<OrgPermissionCan <VariablePermissionCan
I={OrgPermissionIdentityActions.Edit} type={projectId ? "project" : "org"}
a={OrgPermissionSubjects.Identity} I={
projectId
? ProjectPermissionIdentityActions.Edit
: OrgPermissionIdentityActions.Edit
}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -49,10 +72,21 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
Edit Edit
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
<OrgPermissionCan <VariablePermissionCan
I={OrgPermissionIdentityActions.Delete} type={projectId ? "project" : "org"}
a={OrgPermissionSubjects.Identity} I={
projectId
? ProjectPermissionIdentityActions.Delete
: OrgPermissionIdentityActions.Delete
}
a={
projectId
? subject(ProjectPermissionSub.Identity, {
identityId
})
: OrgPermissionSubjects.Identity
}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -63,7 +97,7 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
Delete Delete
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </VariablePermissionCan>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -46,6 +46,7 @@ export const ViewIdentityGcpAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -49,6 +49,7 @@ export const ViewIdentityJwtAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -59,6 +59,7 @@ export const ViewIdentityKubernetesAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -52,6 +52,7 @@ export const ViewIdentityLdapAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -46,6 +46,7 @@ export const ViewIdentityOciAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -48,6 +48,7 @@ export const ViewIdentityOidcAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -51,6 +51,7 @@ export const ViewIdentityTlsCertAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}
@@ -49,6 +49,7 @@ export const ViewIdentityTokenAuthContent = ({
<ViewIdentityContentWrapper <ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")} onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete} onDelete={onDelete}
identityId={identityId}
> >
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)"> <IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL} {data.accessTokenTTL}

Some files were not shown because too many files have changed in this diff Show More