From 61a0997adccbfd407eb2cf12d9c300b8a135a0b2 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Mon, 6 May 2024 14:00:32 +0530 Subject: [PATCH 1/5] fix(ui): secret path input showing / for a valid value that comes delayed --- .../v2/SecretPathInput/SecretPathInput.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/v2/SecretPathInput/SecretPathInput.tsx b/frontend/src/components/v2/SecretPathInput/SecretPathInput.tsx index 1487cb302a..ed97ad8dd5 100644 --- a/frontend/src/components/v2/SecretPathInput/SecretPathInput.tsx +++ b/frontend/src/components/v2/SecretPathInput/SecretPathInput.tsx @@ -46,14 +46,6 @@ export const SecretPathInput = ({ setInputValue(propValue ?? "/"); }, [propValue]); - useEffect(() => { - if (environment) { - setInputValue("/"); - setSecretPath("/"); - onChange?.("/"); - } - }, [environment]); - useEffect(() => { // update secret path if input is valid if ( @@ -158,9 +150,8 @@ export const SecretPathInput = ({ key={`secret-reference-secret-${i + 1}`} >
From 4692aa12bd44c294b0f5bd9b7fd425718cb41b40 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Mon, 6 May 2024 14:01:30 +0530 Subject: [PATCH 2/5] feat: updated identity additional privilege permission object in api to have a proper body and explanation --- ...ity-project-additional-privilege-router.ts | 43 ++++------ ...ty-project-additional-privilege-service.ts | 79 ++++++++++++++++--- backend/src/lib/api-docs/constants.ts | 31 ++++++-- backend/src/server/routes/sanitizedSchemas.ts | 22 ++++++ frontend/package-lock.json | 13 +-- .../queries.tsx | 20 ++--- frontend/src/hooks/api/roles/types.ts | 2 +- .../SpecificPrivilegeSection.tsx | 4 +- 8 files changed, 146 insertions(+), 68 deletions(-) diff --git a/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts b/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts index a1a2e36fab..0fecc9d2e7 100644 --- a/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts +++ b/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts @@ -1,16 +1,14 @@ -import { MongoAbility, RawRuleOf } from "@casl/ability"; -import { PackRule, packRules, unpackRules } from "@casl/ability/extra"; +import { packRules } from "@casl/ability/extra"; import slugify from "@sindresorhus/slugify"; import ms from "ms"; import { z } from "zod"; -import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas"; import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types"; -import { ProjectPermissionSet } from "@app/ee/services/permission/project-permission"; import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { PermissionSchema, SanitizedIdentityPrivilegeSchema } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => { @@ -41,11 +39,11 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F }) .optional() .describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug), - permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions) + permissions: PermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions) }), response: { 200: z.object({ - privilege: IdentityProjectAdditionalPrivilegeSchema + privilege: SanitizedIdentityPrivilegeSchema }) } }, @@ -92,7 +90,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F }) .optional() .describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug), - permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions), + permissions: PermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions), temporaryMode: z .nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode) .describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode), @@ -107,7 +105,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F }), response: { 200: z.object({ - privilege: IdentityProjectAdditionalPrivilegeSchema + privilege: SanitizedIdentityPrivilegeSchema }) } }, @@ -157,7 +155,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F message: "Slug must be a valid slug" }) .describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug), - permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions), + permissions: PermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions), isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary), temporaryMode: z .nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode) @@ -175,7 +173,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F }), response: { 200: z.object({ - privilege: IdentityProjectAdditionalPrivilegeSchema + privilege: SanitizedIdentityPrivilegeSchema }) } }, @@ -219,7 +217,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F }), response: { 200: z.object({ - privilege: IdentityProjectAdditionalPrivilegeSchema + privilege: SanitizedIdentityPrivilegeSchema }) } }, @@ -260,7 +258,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F }), response: { 200: z.object({ - privilege: IdentityProjectAdditionalPrivilegeSchema + privilege: SanitizedIdentityPrivilegeSchema }) } }, @@ -293,16 +291,11 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F ], querystring: z.object({ identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId), - projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug), - unpacked: z - .enum(["false", "true"]) - .transform((el) => el === "true") - .default("true") - .describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.unpacked) + projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug) }), response: { 200: z.object({ - privileges: IdentityProjectAdditionalPrivilegeSchema.array() + privileges: SanitizedIdentityPrivilegeSchema.array() }) } }, @@ -315,15 +308,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F actorOrgId: req.permission.orgId, ...req.query }); - if (req.query.unpacked) { - return { - privileges: privileges.map(({ permissions, ...el }) => ({ - ...el, - permissions: unpackRules(permissions as PackRule>>[]) - })) - }; - } - return { privileges }; + return { + privileges + }; } }); }; diff --git a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts index 81dc11a007..e4c2c690d7 100644 --- a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts +++ b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts @@ -1,5 +1,7 @@ -import { ForbiddenError } from "@casl/ability"; +import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability"; +import { PackRule, unpackRules } from "@casl/ability/extra"; import ms from "ms"; +import { z } from "zod"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; @@ -8,7 +10,7 @@ import { TIdentityProjectDALFactory } from "@app/services/identity-project/ident import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TPermissionServiceFactory } from "../permission/permission-service"; -import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSet, ProjectPermissionSub } from "../permission/project-permission"; import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal"; import { IdentityProjectAdditionalPrivilegeTemporaryMode, @@ -30,6 +32,22 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType< typeof identityProjectAdditionalPrivilegeServiceFactory >; +// TODO(akhilmhdh): move this to more centralized +export const UnpackedPermissionSchema = z.object({ + subject: z.union([z.string().min(1), z.string().array()]).optional(), + action: z.union([z.string().min(1), z.string().array()]), + conditions: z + .object({ + environment: z.string().optional(), + secretPath: z + .object({ + $glob: z.string().min(1) + }) + .optional() + }) + .optional() +}); + export const identityProjectAdditionalPrivilegeServiceFactory = ({ identityProjectAdditionalPrivilegeDAL, identityProjectDAL, @@ -86,7 +104,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ slug, permissions: customPermission }); - return additionalPrivilege; + return { + ...additionalPrivilege, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules( + (additionalPrivilege.permissions || []) as PackRule>>[] + ) + ) + }; } const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange); @@ -100,7 +125,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime), temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs) }); - return additionalPrivilege; + return { + ...additionalPrivilege, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules( + (additionalPrivilege.permissions || []) as PackRule>>[] + ) + ) + }; }; const updateBySlug = async ({ @@ -163,7 +195,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""), temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || "")) }); - return additionalPrivilege; + return { + ...additionalPrivilege, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules( + (additionalPrivilege.permissions || []) as PackRule>>[] + ) + ) + }; } const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, { @@ -174,7 +213,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ temporaryRange: null, temporaryMode: null }); - return additionalPrivilege; + return { + ...additionalPrivilege, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules( + (additionalPrivilege.permissions || []) as PackRule>>[] + ) + ) + }; }; const deleteBySlug = async ({ @@ -220,7 +266,12 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id); - return deletedPrivilege; + return { + ...deletedPrivilege, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules((deletedPrivilege.permissions || []) as PackRule>>[]) + ) + }; }; const getPrivilegeDetailsBySlug = async ({ @@ -254,7 +305,12 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ }); if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); - return identityPrivilege; + return { + ...identityPrivilege, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules((identityPrivilege.permissions || []) as PackRule>>[]) + ) + }; }; const listIdentityProjectPrivileges = async ({ @@ -284,7 +340,12 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({ projectMembershipId: identityProjectMembership.id }); - return identityPrivileges; + return identityPrivileges.map((el) => ({ + ...el, + permissions: UnpackedPermissionSchema.array().parse( + unpackRules((el.permissions || []) as PackRule>>[]) + ) + })); }; return { diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index efcb03bd32..10006b9efb 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -468,9 +468,18 @@ export const IDENTITY_ADDITIONAL_PRIVILEGE = { identityId: "The ID of the identity to delete.", slug: "The slug of the privilege to create.", permissions: `The permission object for the privilege. -1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]] -2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]] -2. [["read", "secrets", {environment: "dev"}]] +- Read secrets +\`\`\` +{ "permissions": [{"action": "read", "subject": "secrets"]} +\`\`\` +- Read and Write secrets +\`\`\` +{ "permissions": [{"action": "read", "subject": "secrets"], {"action": "write", "subject": "secrets"]} +\`\`\` +- Read secrets scoped to an environment and secret path +\`\`\` +- { "permissions": [{"action": "read", "subject": "secrets", "conditions": { "environment": "dev", "secretPath": { "$glob": "/" } }}] } +\`\`\` `, isPackPermission: "Whether the server should pack(compact) the permission object.", isTemporary: "Whether the privilege is temporary.", @@ -484,11 +493,19 @@ export const IDENTITY_ADDITIONAL_PRIVILEGE = { slug: "The slug of the privilege to update.", newSlug: "The new slug of the privilege to update.", permissions: `The permission object for the privilege. -1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]] -2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]] -2. [["read", "secrets", {environment: "dev"}]] +- Read secrets +\`\`\` +{ "permissions": [{"action": "read", "subject": "secrets"]} +\`\`\` +- Read and Write secrets +\`\`\` +{ "permissions": [{"action": "read", "subject": "secrets"], {"action": "write", "subject": "secrets"]} +\`\`\` +- Read secrets scoped to an environment and secret path +\`\`\` +- { "permissions": [{"action": "read", "subject": "secrets", "conditions": { "environment": "dev", "secretPath": { "$glob": "/" } }}] } +\`\`\` `, - isPackPermission: "Whether the server should pack(compact) the permission object.", isTemporary: "Whether the privilege is temporary.", temporaryMode: "Type of temporary access given. Types: relative", temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d", diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index a0b792789c..04f0a11664 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -2,10 +2,12 @@ import { z } from "zod"; import { DynamicSecretsSchema, + IdentityProjectAdditionalPrivilegeSchema, IntegrationAuthsSchema, SecretApprovalPoliciesSchema, UsersSchema } from "@app/db/schemas"; +import { UnpackedPermissionSchema } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service"; // sometimes the return data must be santizied to avoid leaking important values // always prefer pick over omit in zod @@ -62,6 +64,26 @@ export const secretRawSchema = z.object({ secretComment: z.string().optional() }); +export const PermissionSchema = z.object({ + action: z.string().min(1).describe("Describe what user can actually do. Ex: create, edit, delete, read"), + subject: z.string().min(1).describe("The entity to check action on. Ex: secrets, environments"), + conditions: z + .object({ + environment: z.string().describe("To scope the permission to an environment.").optional(), + secretPath: z + .object({ + $glob: z.string().min(1).describe("To scope the permission to a secret path. Supports glob patterns.") + }) + .optional() + }) + .describe("Criteria which restricts user action only to matched subjects") + .optional() +}); + +export const SanitizedIdentityPrivilegeSchema = IdentityProjectAdditionalPrivilegeSchema.extend({ + permissions: UnpackedPermissionSchema.array() +}); + export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({ inputIV: true, inputTag: true, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e7c587f139..c33c9dc360 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": "frontend", "dependencies": { "@casl/ability": "^6.5.0", "@casl/react": "^3.1.0", @@ -12165,9 +12166,9 @@ "dev": true }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { "jake": "^10.8.5" @@ -22439,9 +22440,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "dependencies": { "chownr": "^2.0.0", diff --git a/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx index 72534c1584..e4bd141fb3 100644 --- a/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx +++ b/frontend/src/hooks/api/identityProjectAdditionalPrivilege/queries.tsx @@ -1,9 +1,7 @@ -import { PackRule, unpackRules } from "@casl/ability/extra"; import { useQuery } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; -import { TProjectPermission } from "../roles/types"; import { TGetIdentityProejctPrivilegeDetails as TGetIdentityProjectPrivilegeDetails, TIdentityProjectPrivilege, @@ -36,17 +34,14 @@ export const useGetIdentityProjectPrivilegeDetails = ({ const { data: { privilege } } = await apiRequest.get<{ - privilege: Omit & { permissions: unknown }; + privilege: TIdentityProjectPrivilege; }>(`/api/v1/additional-privilege/identity/${privilegeSlug}`, { params: { identityId, projectSlug } }); - return { - ...privilege, - permissions: unpackRules(privilege.permissions as PackRule[]) - }; + return privilege; } }); }; @@ -62,16 +57,11 @@ export const useListIdentityProjectPrivileges = ({ const { data: { privileges } } = await apiRequest.get<{ - privileges: Array< - Omit & { permissions: unknown } - >; + privileges: Array; }>("/api/v1/additional-privilege/identity", { - params: { identityId, projectSlug, unpacked: false } + params: { identityId, projectSlug } }); - return privileges.map((el) => ({ - ...el, - permissions: unpackRules(el.permissions as PackRule[]) - })); + return privileges; } }); }; diff --git a/frontend/src/hooks/api/roles/types.ts b/frontend/src/hooks/api/roles/types.ts index 5f205e585e..97a90b4212 100644 --- a/frontend/src/hooks/api/roles/types.ts +++ b/frontend/src/hooks/api/roles/types.ts @@ -41,7 +41,7 @@ export type TPermission = { export type TProjectPermission = { conditions?: Record; action: string; - subject: [string]; + subject: string | string[]; }; export type TGetUserOrgPermissionsDTO = { diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx index 56248145b3..44feb73d9e 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx @@ -142,7 +142,7 @@ const SpecificPrivilegeSecretForm = ({ .filter(({ allowed }) => allowed) .map(({ action }) => ({ action, - subject: [ProjectPermissionSub.Secrets], + subject: ProjectPermissionSub.Secrets, conditions })) }, @@ -477,7 +477,7 @@ export const SpecificPrivilegeSection = ({ identityId }: Props) => { permissions: [ { action: ProjectPermissionActions.Read, - subject: [ProjectPermissionSub.Secrets], + subject: ProjectPermissionSub.Secrets, conditions: { environment: currentWorkspace?.environments?.[0].slug } From 86fd4d5fba3ad02acdd701a538dec04874f83e5f Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Mon, 6 May 2024 14:26:46 +0530 Subject: [PATCH 3/5] feat: added a fixed sorted order to avoid jumps --- .../components/IdentityRoleForm/SpecificPrivilegeSection.tsx | 1 + .../MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx index 44feb73d9e..6c1d4654d7 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx @@ -512,6 +512,7 @@ export const SpecificPrivilegeSection = ({ identityId }: Props) => { ?.filter(({ permissions }) => permissions?.[0]?.subject?.includes(ProjectPermissionSub.Secrets) ) + .sort((a, b) => a.id.localeCompare(b.id)) ?.map((privilege) => ( { ?.filter(({ permissions }) => permissions?.[0]?.subject?.includes(ProjectPermissionSub.Secrets) ) + .sort((a, b) => a.id.localeCompare(b.id)) ?.map((privilege) => ( Date: Mon, 6 May 2024 13:44:39 -0400 Subject: [PATCH 4/5] rephrase text for permission schema zod --- backend/src/server/routes/sanitizedSchemas.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index 04f0a11664..14155ecf99 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -65,18 +65,27 @@ export const secretRawSchema = z.object({ }); export const PermissionSchema = z.object({ - action: z.string().min(1).describe("Describe what user can actually do. Ex: create, edit, delete, read"), - subject: z.string().min(1).describe("The entity to check action on. Ex: secrets, environments"), + action: z + .string() + .min(1) + .describe("Describe what action an entity can take. Possible actions: create, edit, delete, and read"), + subject: z + .string() + .min(1) + .describe("The entity this permission pertains to. Possible options: secrets, environments"), conditions: z .object({ - environment: z.string().describe("To scope the permission to an environment.").optional(), + environment: z.string().describe("The environment slug this permission should allow.").optional(), secretPath: z .object({ - $glob: z.string().min(1).describe("To scope the permission to a secret path. Supports glob patterns.") + $glob: z + .string() + .min(1) + .describe("The secret path this permission should allow. Can be a glob pattern such as /folder-name/*/** ") }) .optional() }) - .describe("Criteria which restricts user action only to matched subjects") + .describe("When specified, only matching conditions will be allowed to access given resource.") .optional() }); From d198ba1a794814d566cdfb107c495bb842049d4c Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Mon, 6 May 2024 23:27:24 +0530 Subject: [PATCH 5/5] feat: refactored the map unpack to a function --- ...ty-project-additional-privilege-service.ts | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts index e4c2c690d7..70753ee094 100644 --- a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts +++ b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts @@ -48,6 +48,11 @@ export const UnpackedPermissionSchema = z.object({ .optional() }); +const unpackPermissions = (permissions: unknown) => + UnpackedPermissionSchema.array().parse( + unpackRules((permissions || []) as PackRule>>[]) + ); + export const identityProjectAdditionalPrivilegeServiceFactory = ({ identityProjectAdditionalPrivilegeDAL, identityProjectDAL, @@ -106,11 +111,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ }); return { ...additionalPrivilege, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules( - (additionalPrivilege.permissions || []) as PackRule>>[] - ) - ) + permissions: unpackPermissions(additionalPrivilege.permissions) }; } @@ -127,11 +128,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ }); return { ...additionalPrivilege, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules( - (additionalPrivilege.permissions || []) as PackRule>>[] - ) - ) + permissions: unpackPermissions(additionalPrivilege.permissions) }; }; @@ -197,11 +194,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ }); return { ...additionalPrivilege, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules( - (additionalPrivilege.permissions || []) as PackRule>>[] - ) - ) + + permissions: unpackPermissions(additionalPrivilege.permissions) }; } @@ -215,11 +209,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ }); return { ...additionalPrivilege, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules( - (additionalPrivilege.permissions || []) as PackRule>>[] - ) - ) + + permissions: unpackPermissions(additionalPrivilege.permissions) }; }; @@ -268,9 +259,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id); return { ...deletedPrivilege, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules((deletedPrivilege.permissions || []) as PackRule>>[]) - ) + + permissions: unpackPermissions(deletedPrivilege.permissions) }; }; @@ -307,9 +297,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ return { ...identityPrivilege, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules((identityPrivilege.permissions || []) as PackRule>>[]) - ) + permissions: unpackPermissions(identityPrivilege.permissions) }; }; @@ -342,9 +330,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ }); return identityPrivileges.map((el) => ({ ...el, - permissions: UnpackedPermissionSchema.array().parse( - unpackRules((el.permissions || []) as PackRule>>[]) - ) + + permissions: unpackPermissions(el.permissions) })); };