mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
Merge pull request #1782 from akhilmhdh/feat/privilege-identity-api-change
Privilege identity api change
This commit is contained in:
@@ -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 };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
+57
-9
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Generated
+7
-6
@@ -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>[])
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
+3
-2
@@ -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}
|
||||||
|
|||||||
+1
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user