Merge pull request #1782 from akhilmhdh/feat/privilege-identity-api-change

Privilege identity api change
This commit is contained in:
Maidul Islam
2024-05-06 15:01:02 -04:00
committed by GitHub
10 changed files with 146 additions and 79 deletions
@@ -1,16 +1,14 @@
import { MongoAbility, RawRuleOf } from "@casl/ability"; import { packRules } from "@casl/ability/extra";
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify"; import slugify from "@sindresorhus/slugify";
import ms from "ms"; import ms from "ms";
import { z } from "zod"; 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 { 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 { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; 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"; import { AuthMode } from "@app/services/auth/auth-type";
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => { export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
@@ -41,11 +39,11 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}) })
.optional() .optional()
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug), .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: { response: {
200: z.object({ 200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema privilege: SanitizedIdentityPrivilegeSchema
}) })
} }
}, },
@@ -92,7 +90,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}) })
.optional() .optional()
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug), .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 temporaryMode: z
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode) .nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode), .describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
@@ -107,7 +105,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}), }),
response: { response: {
200: z.object({ 200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema privilege: SanitizedIdentityPrivilegeSchema
}) })
} }
}, },
@@ -157,7 +155,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
message: "Slug must be a valid slug" message: "Slug must be a valid slug"
}) })
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug), .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), isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
temporaryMode: z temporaryMode: z
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode) .nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
@@ -175,7 +173,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}), }),
response: { response: {
200: z.object({ 200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema privilege: SanitizedIdentityPrivilegeSchema
}) })
} }
}, },
@@ -219,7 +217,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}), }),
response: { response: {
200: z.object({ 200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema privilege: SanitizedIdentityPrivilegeSchema
}) })
} }
}, },
@@ -260,7 +258,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}), }),
response: { response: {
200: z.object({ 200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema privilege: SanitizedIdentityPrivilegeSchema
}) })
} }
}, },
@@ -293,16 +291,11 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
], ],
querystring: z.object({ querystring: z.object({
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId), identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug), 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)
}), }),
response: { response: {
200: z.object({ 200: z.object({
privileges: IdentityProjectAdditionalPrivilegeSchema.array() privileges: SanitizedIdentityPrivilegeSchema.array()
}) })
} }
}, },
@@ -315,15 +308,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.query ...req.query
}); });
if (req.query.unpacked) { return {
return { privileges
privileges: privileges.map(({ permissions, ...el }) => ({ };
...el,
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
}))
};
}
return { privileges };
} }
}); });
}; };
@@ -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 ms from "ms";
import { z } from "zod";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; 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 { TProjectDALFactory } from "@app/services/project/project-dal";
import { TPermissionServiceFactory } from "../permission/permission-service"; 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 { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
import { import {
IdentityProjectAdditionalPrivilegeTemporaryMode, IdentityProjectAdditionalPrivilegeTemporaryMode,
@@ -30,6 +32,27 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeServiceFactory 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()
});
const unpackPermissions = (permissions: unknown) =>
UnpackedPermissionSchema.array().parse(
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
);
export const identityProjectAdditionalPrivilegeServiceFactory = ({ export const identityProjectAdditionalPrivilegeServiceFactory = ({
identityProjectAdditionalPrivilegeDAL, identityProjectAdditionalPrivilegeDAL,
identityProjectDAL, identityProjectDAL,
@@ -86,7 +109,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
slug, slug,
permissions: customPermission permissions: customPermission
}); });
return additionalPrivilege; return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
} }
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange); const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
@@ -100,7 +126,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime), temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs) temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
}); });
return additionalPrivilege; return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
}; };
const updateBySlug = async ({ const updateBySlug = async ({
@@ -163,7 +192,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""), temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || "")) temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
}); });
return additionalPrivilege; return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
} }
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, { const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
@@ -174,7 +207,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
temporaryRange: null, temporaryRange: null,
temporaryMode: null temporaryMode: null
}); });
return additionalPrivilege; return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
}; };
const deleteBySlug = async ({ const deleteBySlug = async ({
@@ -220,7 +257,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id); const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
return deletedPrivilege; return {
...deletedPrivilege,
permissions: unpackPermissions(deletedPrivilege.permissions)
};
}; };
const getPrivilegeDetailsBySlug = async ({ const getPrivilegeDetailsBySlug = async ({
@@ -254,7 +295,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
}); });
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
return identityPrivilege; return {
...identityPrivilege,
permissions: unpackPermissions(identityPrivilege.permissions)
};
}; };
const listIdentityProjectPrivileges = async ({ const listIdentityProjectPrivileges = async ({
@@ -284,7 +328,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({ const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
projectMembershipId: identityProjectMembership.id projectMembershipId: identityProjectMembership.id
}); });
return identityPrivileges; return identityPrivileges.map((el) => ({
...el,
permissions: unpackPermissions(el.permissions)
}));
}; };
return { return {
+24 -7
View File
@@ -468,9 +468,18 @@ export const IDENTITY_ADDITIONAL_PRIVILEGE = {
identityId: "The ID of the identity to delete.", identityId: "The ID of the identity to delete.",
slug: "The slug of the privilege to create.", slug: "The slug of the privilege to create.",
permissions: `The permission object for the privilege. permissions: `The permission object for the privilege.
1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]] - Read secrets
2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]] \`\`\`
2. [["read", "secrets", {environment: "dev"}]] { "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.", isPackPermission: "Whether the server should pack(compact) the permission object.",
isTemporary: "Whether the privilege is temporary.", isTemporary: "Whether the privilege is temporary.",
@@ -484,11 +493,19 @@ export const IDENTITY_ADDITIONAL_PRIVILEGE = {
slug: "The slug of the privilege to update.", slug: "The slug of the privilege to update.",
newSlug: "The new slug of the privilege to update.", newSlug: "The new slug of the privilege to update.",
permissions: `The permission object for the privilege. permissions: `The permission object for the privilege.
1. [["read", "secrets", {environment: "dev", secretPath: {$glob: "/"}}]] - Read secrets
2. [["read", "secrets", {environment: "dev"}], ["create", "secrets", {environment: "dev"}]] \`\`\`
2. [["read", "secrets", {environment: "dev"}]] { "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.", isTemporary: "Whether the privilege is temporary.",
temporaryMode: "Type of temporary access given. Types: relative", temporaryMode: "Type of temporary access given. Types: relative",
temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d", temporaryRange: "TTL for the temporay time. Eg: 1m, 1h, 1d",
@@ -2,10 +2,12 @@ import { z } from "zod";
import { import {
DynamicSecretsSchema, DynamicSecretsSchema,
IdentityProjectAdditionalPrivilegeSchema,
IntegrationAuthsSchema, IntegrationAuthsSchema,
SecretApprovalPoliciesSchema, SecretApprovalPoliciesSchema,
UsersSchema UsersSchema
} from "@app/db/schemas"; } 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 // sometimes the return data must be santizied to avoid leaking important values
// always prefer pick over omit in zod // always prefer pick over omit in zod
@@ -62,6 +64,35 @@ export const secretRawSchema = z.object({
secretComment: z.string().optional() secretComment: z.string().optional()
}); });
export const PermissionSchema = z.object({
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("The environment slug this permission should allow.").optional(),
secretPath: z
.object({
$glob: z
.string()
.min(1)
.describe("The secret path this permission should allow. Can be a glob pattern such as /folder-name/*/** ")
})
.optional()
})
.describe("When specified, only matching conditions will be allowed to access given resource.")
.optional()
});
export const SanitizedIdentityPrivilegeSchema = IdentityProjectAdditionalPrivilegeSchema.extend({
permissions: UnpackedPermissionSchema.array()
});
export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({ export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({
inputIV: true, inputIV: true,
inputTag: true, inputTag: true,
+7 -6
View File
@@ -4,6 +4,7 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "frontend",
"dependencies": { "dependencies": {
"@casl/ability": "^6.5.0", "@casl/ability": "^6.5.0",
"@casl/react": "^3.1.0", "@casl/react": "^3.1.0",
@@ -12165,9 +12166,9 @@
"dev": true "dev": true
}, },
"node_modules/ejs": { "node_modules/ejs": {
"version": "3.1.9", "version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"jake": "^10.8.5" "jake": "^10.8.5"
@@ -22439,9 +22440,9 @@
} }
}, },
"node_modules/tar": { "node_modules/tar": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"chownr": "^2.0.0", "chownr": "^2.0.0",
@@ -46,14 +46,6 @@ export const SecretPathInput = ({
setInputValue(propValue ?? "/"); setInputValue(propValue ?? "/");
}, [propValue]); }, [propValue]);
useEffect(() => {
if (environment) {
setInputValue("/");
setSecretPath("/");
onChange?.("/");
}
}, [environment]);
useEffect(() => { useEffect(() => {
// update secret path if input is valid // update secret path if input is valid
if ( if (
@@ -158,9 +150,8 @@ export const SecretPathInput = ({
key={`secret-reference-secret-${i + 1}`} key={`secret-reference-secret-${i + 1}`}
> >
<div <div
className={`${ className={`${highlightedIndex === i ? "bg-gray-600" : ""
highlightedIndex === i ? "bg-gray-600" : "" } text-md relative mb-0.5 flex w-full cursor-pointer select-none items-center justify-between rounded-md px-2 py-1 outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-500`}
} text-md relative mb-0.5 flex w-full cursor-pointer select-none items-center justify-between rounded-md px-2 py-1 outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-500`}
> >
<div className="flex gap-2"> <div className="flex gap-2">
<div className="flex items-center text-yellow-700"> <div className="flex items-center text-yellow-700">
@@ -1,9 +1,7 @@
import { PackRule, unpackRules } from "@casl/ability/extra";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { TProjectPermission } from "../roles/types";
import { import {
TGetIdentityProejctPrivilegeDetails as TGetIdentityProjectPrivilegeDetails, TGetIdentityProejctPrivilegeDetails as TGetIdentityProjectPrivilegeDetails,
TIdentityProjectPrivilege, TIdentityProjectPrivilege,
@@ -36,17 +34,14 @@ export const useGetIdentityProjectPrivilegeDetails = ({
const { const {
data: { privilege } data: { privilege }
} = await apiRequest.get<{ } = await apiRequest.get<{
privilege: Omit<TIdentityProjectPrivilege, "permissions"> & { permissions: unknown }; privilege: TIdentityProjectPrivilege;
}>(`/api/v1/additional-privilege/identity/${privilegeSlug}`, { }>(`/api/v1/additional-privilege/identity/${privilegeSlug}`, {
params: { params: {
identityId, identityId,
projectSlug projectSlug
} }
}); });
return { return privilege;
...privilege,
permissions: unpackRules(privilege.permissions as PackRule<TProjectPermission>[])
};
} }
}); });
}; };
@@ -62,16 +57,11 @@ export const useListIdentityProjectPrivileges = ({
const { const {
data: { privileges } data: { privileges }
} = await apiRequest.get<{ } = await apiRequest.get<{
privileges: Array< privileges: Array<TIdentityProjectPrivilege>;
Omit<TIdentityProjectPrivilege, "permissions"> & { permissions: unknown }
>;
}>("/api/v1/additional-privilege/identity", { }>("/api/v1/additional-privilege/identity", {
params: { identityId, projectSlug, unpacked: false } params: { identityId, projectSlug }
}); });
return privileges.map((el) => ({ return privileges;
...el,
permissions: unpackRules(el.permissions as PackRule<TProjectPermission>[])
}));
} }
}); });
}; };
+1 -1
View File
@@ -41,7 +41,7 @@ export type TPermission = {
export type TProjectPermission = { export type TProjectPermission = {
conditions?: Record<string, any>; conditions?: Record<string, any>;
action: string; action: string;
subject: [string]; subject: string | string[];
}; };
export type TGetUserOrgPermissionsDTO = { export type TGetUserOrgPermissionsDTO = {
@@ -142,7 +142,7 @@ const SpecificPrivilegeSecretForm = ({
.filter(({ allowed }) => allowed) .filter(({ allowed }) => allowed)
.map(({ action }) => ({ .map(({ action }) => ({
action, action,
subject: [ProjectPermissionSub.Secrets], subject: ProjectPermissionSub.Secrets,
conditions conditions
})) }))
}, },
@@ -477,7 +477,7 @@ export const SpecificPrivilegeSection = ({ identityId }: Props) => {
permissions: [ permissions: [
{ {
action: ProjectPermissionActions.Read, action: ProjectPermissionActions.Read,
subject: [ProjectPermissionSub.Secrets], subject: ProjectPermissionSub.Secrets,
conditions: { conditions: {
environment: currentWorkspace?.environments?.[0].slug environment: currentWorkspace?.environments?.[0].slug
} }
@@ -512,6 +512,7 @@ export const SpecificPrivilegeSection = ({ identityId }: Props) => {
?.filter(({ permissions }) => ?.filter(({ permissions }) =>
permissions?.[0]?.subject?.includes(ProjectPermissionSub.Secrets) permissions?.[0]?.subject?.includes(ProjectPermissionSub.Secrets)
) )
.sort((a, b) => a.id.localeCompare(b.id))
?.map((privilege) => ( ?.map((privilege) => (
<SpecificPrivilegeSecretForm <SpecificPrivilegeSecretForm
privilege={privilege as TProjectUserPrivilege} privilege={privilege as TProjectUserPrivilege}
@@ -495,6 +495,7 @@ export const SpecificPrivilegeSection = ({ membershipId }: Props) => {
?.filter(({ permissions }) => ?.filter(({ permissions }) =>
permissions?.[0]?.subject?.includes(ProjectPermissionSub.Secrets) permissions?.[0]?.subject?.includes(ProjectPermissionSub.Secrets)
) )
.sort((a, b) => a.id.localeCompare(b.id))
?.map((privilege) => ( ?.map((privilege) => (
<SpecificPrivilegeSecretForm <SpecificPrivilegeSecretForm
privilege={privilege as TProjectUserPrivilege} privilege={privilege as TProjectUserPrivilege}