improvements: refactor project templates, filter policies by project type, project type specific roles

This commit is contained in:
Scott Wilson
2025-05-06 11:26:09 -07:00
parent d530604b51
commit 4562f57b54
80 changed files with 1396 additions and 634 deletions
@@ -0,0 +1,22 @@
import { Knex } from "knex";
import { ProjectType, TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.ProjectTemplates, "type"))) {
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
// defaulting to sm for migration to set existing, new ones will always be specified on creation
t.string("type").defaultTo(ProjectType.SecretManager).notNullable();
t.jsonb("environments").nullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.ProjectTemplates, "type")) {
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
t.dropColumn("type");
// not reverting nullable environments
});
}
}
+6 -1
View File
@@ -185,11 +185,16 @@ export enum OrgMembershipStatus {
}
export enum ProjectMembershipRole {
// general
Admin = "admin",
Member = "member",
Custom = "custom",
Viewer = "viewer",
NoAccess = "no-access"
NoAccess = "no-access",
// ssh
SshHostBootstrapper = "ssh-host-bootstrapper",
// kms
KmsCryptographicOperator = "cryptographic-operator"
}
export enum SecretEncryptionAlgo {
+3 -2
View File
@@ -12,10 +12,11 @@ export const ProjectTemplatesSchema = z.object({
name: z.string(),
description: z.string().nullable().optional(),
roles: z.unknown(),
environments: z.unknown(),
environments: z.unknown().nullable().optional(),
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
type: z.string().default("secret-manager")
});
export type TProjectTemplates = z.infer<typeof ProjectTemplatesSchema>;
@@ -1,9 +1,8 @@
import { z } from "zod";
import { ProjectMembershipRole, ProjectTemplatesSchema } from "@app/db/schemas";
import { ProjectMembershipRole, ProjectTemplatesSchema, ProjectType } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
import { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
import { ApiDocsTags, ProjectTemplates } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
@@ -35,6 +34,7 @@ const SanitizedProjectTemplateSchema = ProjectTemplatesSchema.extend({
position: z.number().min(1)
})
.array()
.nullable()
});
const ProjectTemplateRolesSchema = z
@@ -104,6 +104,9 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
hide: false,
tags: [ApiDocsTags.ProjectTemplates],
description: "List project templates for the current organization.",
querystring: z.object({
type: z.nativeEnum(ProjectType).optional().describe(ProjectTemplates.LIST.type)
}),
response: {
200: z.object({
projectTemplates: SanitizedProjectTemplateSchema.array()
@@ -112,7 +115,8 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission);
const { type } = req.query;
const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission, type);
const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name));
@@ -184,6 +188,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
tags: [ApiDocsTags.ProjectTemplates],
description: "Create a project template.",
body: z.object({
type: z.nativeEnum(ProjectType).describe(ProjectTemplates.CREATE.type),
name: slugSchema({ field: "name" })
.refine((val) => !isInfisicalProjectTemplate(val), {
message: `The requested project template name is reserved.`
@@ -191,9 +196,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
.describe(ProjectTemplates.CREATE.name),
description: z.string().max(256).trim().optional().describe(ProjectTemplates.CREATE.description),
roles: ProjectTemplateRolesSchema.default([]).describe(ProjectTemplates.CREATE.roles),
environments: ProjectTemplateEnvironmentsSchema.default(ProjectTemplateDefaultEnvironments).describe(
ProjectTemplates.CREATE.environments
)
environments: ProjectTemplateEnvironmentsSchema.describe(ProjectTemplates.CREATE.environments).optional()
}),
response: {
200: z.object({
@@ -0,0 +1,448 @@
import { AbilityBuilder, createMongoAbility, MongoAbility } from "@casl/ability";
import {
ProjectPermissionActions,
ProjectPermissionCertificateActions,
ProjectPermissionCmekActions,
ProjectPermissionDynamicSecretActions,
ProjectPermissionGroupActions,
ProjectPermissionIdentityActions,
ProjectPermissionKmipActions,
ProjectPermissionMemberActions,
ProjectPermissionSecretActions,
ProjectPermissionSecretRotationActions,
ProjectPermissionSecretSyncActions,
ProjectPermissionSet,
ProjectPermissionSshHostActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
const buildAdminPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
// Admins get full access to everything
[
ProjectPermissionSub.SecretFolders,
ProjectPermissionSub.SecretImports,
ProjectPermissionSub.SecretApproval,
ProjectPermissionSub.Role,
ProjectPermissionSub.Integrations,
ProjectPermissionSub.Webhooks,
ProjectPermissionSub.ServiceTokens,
ProjectPermissionSub.Settings,
ProjectPermissionSub.Environments,
ProjectPermissionSub.Tags,
ProjectPermissionSub.AuditLogs,
ProjectPermissionSub.IpAllowList,
ProjectPermissionSub.CertificateAuthorities,
ProjectPermissionSub.CertificateTemplates,
ProjectPermissionSub.PkiAlerts,
ProjectPermissionSub.PkiCollections,
ProjectPermissionSub.SshCertificateAuthorities,
ProjectPermissionSub.SshCertificates,
ProjectPermissionSub.SshCertificateTemplates,
ProjectPermissionSub.SshHostGroups
].forEach((el) => {
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
el
);
});
can(
[
ProjectPermissionCertificateActions.Read,
ProjectPermissionCertificateActions.Edit,
ProjectPermissionCertificateActions.Create,
ProjectPermissionCertificateActions.Delete,
ProjectPermissionCertificateActions.ReadPrivateKey
],
ProjectPermissionSub.Certificates
);
can(
[
ProjectPermissionSshHostActions.Edit,
ProjectPermissionSshHostActions.Read,
ProjectPermissionSshHostActions.Create,
ProjectPermissionSshHostActions.Delete,
ProjectPermissionSshHostActions.IssueHostCert
],
ProjectPermissionSub.SshHosts
);
can(
[
ProjectPermissionMemberActions.Create,
ProjectPermissionMemberActions.Edit,
ProjectPermissionMemberActions.Delete,
ProjectPermissionMemberActions.Read,
ProjectPermissionMemberActions.GrantPrivileges,
ProjectPermissionMemberActions.AssumePrivileges
],
ProjectPermissionSub.Member
);
can(
[
ProjectPermissionGroupActions.Create,
ProjectPermissionGroupActions.Edit,
ProjectPermissionGroupActions.Delete,
ProjectPermissionGroupActions.Read,
ProjectPermissionGroupActions.GrantPrivileges
],
ProjectPermissionSub.Groups
);
can(
[
ProjectPermissionIdentityActions.Create,
ProjectPermissionIdentityActions.Edit,
ProjectPermissionIdentityActions.Delete,
ProjectPermissionIdentityActions.Read,
ProjectPermissionIdentityActions.GrantPrivileges,
ProjectPermissionIdentityActions.AssumePrivileges
],
ProjectPermissionSub.Identity
);
can(
[
ProjectPermissionSecretActions.DescribeAndReadValue,
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Delete
],
ProjectPermissionSub.Secrets
);
can(
[
ProjectPermissionDynamicSecretActions.ReadRootCredential,
ProjectPermissionDynamicSecretActions.EditRootCredential,
ProjectPermissionDynamicSecretActions.CreateRootCredential,
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
ProjectPermissionDynamicSecretActions.Lease
],
ProjectPermissionSub.DynamicSecrets
);
can([ProjectPermissionActions.Edit, ProjectPermissionActions.Delete], ProjectPermissionSub.Project);
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
can([ProjectPermissionActions.Edit], ProjectPermissionSub.Kms);
can(
[
ProjectPermissionCmekActions.Create,
ProjectPermissionCmekActions.Edit,
ProjectPermissionCmekActions.Delete,
ProjectPermissionCmekActions.Read,
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt,
ProjectPermissionCmekActions.Sign,
ProjectPermissionCmekActions.Verify
],
ProjectPermissionSub.Cmek
);
can(
[
ProjectPermissionSecretSyncActions.Create,
ProjectPermissionSecretSyncActions.Edit,
ProjectPermissionSecretSyncActions.Delete,
ProjectPermissionSecretSyncActions.Read,
ProjectPermissionSecretSyncActions.SyncSecrets,
ProjectPermissionSecretSyncActions.ImportSecrets,
ProjectPermissionSecretSyncActions.RemoveSecrets
],
ProjectPermissionSub.SecretSyncs
);
can(
[
ProjectPermissionKmipActions.CreateClients,
ProjectPermissionKmipActions.UpdateClients,
ProjectPermissionKmipActions.DeleteClients,
ProjectPermissionKmipActions.ReadClients,
ProjectPermissionKmipActions.GenerateClientCertificates
],
ProjectPermissionSub.Kmip
);
can(
[
ProjectPermissionSecretRotationActions.Create,
ProjectPermissionSecretRotationActions.Edit,
ProjectPermissionSecretRotationActions.Delete,
ProjectPermissionSecretRotationActions.Read,
ProjectPermissionSecretRotationActions.ReadGeneratedCredentials,
ProjectPermissionSecretRotationActions.RotateSecrets
],
ProjectPermissionSub.SecretRotation
);
return rules;
};
const buildMemberPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(
[
ProjectPermissionSecretActions.DescribeAndReadValue,
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Delete
],
ProjectPermissionSub.Secrets
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.SecretFolders
);
can(
[
ProjectPermissionDynamicSecretActions.ReadRootCredential,
ProjectPermissionDynamicSecretActions.EditRootCredential,
ProjectPermissionDynamicSecretActions.CreateRootCredential,
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
ProjectPermissionDynamicSecretActions.Lease
],
ProjectPermissionSub.DynamicSecrets
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.SecretImports
);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SecretApproval);
can([ProjectPermissionSecretRotationActions.Read], ProjectPermissionSub.SecretRotation);
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
can([ProjectPermissionMemberActions.Read, ProjectPermissionMemberActions.Create], ProjectPermissionSub.Member);
can([ProjectPermissionGroupActions.Read], ProjectPermissionSub.Groups);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Integrations
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Webhooks
);
can(
[
ProjectPermissionIdentityActions.Read,
ProjectPermissionIdentityActions.Edit,
ProjectPermissionIdentityActions.Create,
ProjectPermissionIdentityActions.Delete
],
ProjectPermissionSub.Identity
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.ServiceTokens
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Settings
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Environments
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Tags
);
can([ProjectPermissionActions.Read], ProjectPermissionSub.Role);
can([ProjectPermissionActions.Read], ProjectPermissionSub.AuditLogs);
can([ProjectPermissionActions.Read], ProjectPermissionSub.IpAllowList);
// double check if all CRUD are needed for CA and Certificates
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateAuthorities);
can(
[
ProjectPermissionCertificateActions.Read,
ProjectPermissionCertificateActions.Edit,
ProjectPermissionCertificateActions.Create,
ProjectPermissionCertificateActions.Delete
],
ProjectPermissionSub.Certificates
);
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateTemplates);
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificates);
can([ProjectPermissionActions.Create], ProjectPermissionSub.SshCertificates);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateTemplates);
can([ProjectPermissionSshHostActions.Read], ProjectPermissionSub.SshHosts);
can(
[
ProjectPermissionCmekActions.Create,
ProjectPermissionCmekActions.Edit,
ProjectPermissionCmekActions.Delete,
ProjectPermissionCmekActions.Read,
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt,
ProjectPermissionCmekActions.Sign,
ProjectPermissionCmekActions.Verify
],
ProjectPermissionSub.Cmek
);
can(
[
ProjectPermissionSecretSyncActions.Create,
ProjectPermissionSecretSyncActions.Edit,
ProjectPermissionSecretSyncActions.Delete,
ProjectPermissionSecretSyncActions.Read,
ProjectPermissionSecretSyncActions.SyncSecrets,
ProjectPermissionSecretSyncActions.ImportSecrets,
ProjectPermissionSecretSyncActions.RemoveSecrets
],
ProjectPermissionSub.SecretSyncs
);
return rules;
};
const buildViewerPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(ProjectPermissionSecretActions.DescribeAndReadValue, ProjectPermissionSub.Secrets);
can(ProjectPermissionSecretActions.DescribeSecret, ProjectPermissionSub.Secrets);
can(ProjectPermissionSecretActions.ReadValue, ProjectPermissionSub.Secrets);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretFolders);
can(ProjectPermissionDynamicSecretActions.ReadRootCredential, ProjectPermissionSub.DynamicSecrets);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
can(ProjectPermissionSecretRotationActions.Read, ProjectPermissionSub.SecretRotation);
can(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
can(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
can(ProjectPermissionIdentityActions.Read, ProjectPermissionSub.Identity);
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates);
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
can(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs);
return rules;
};
const buildNoAccessProjectPermission = () => {
const { rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
return rules;
};
const buildSshHostBootstrapPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(
[ProjectPermissionSshHostActions.Create, ProjectPermissionSshHostActions.IssueHostCert],
ProjectPermissionSub.SshHosts
);
return rules;
};
const buildCryptographicOperatorPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(
[
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt,
ProjectPermissionCmekActions.Sign,
ProjectPermissionCmekActions.Verify
],
ProjectPermissionSub.Cmek
);
return rules;
};
// General
export const projectAdminPermissions = buildAdminPermissionRules();
export const projectMemberPermissions = buildMemberPermissionRules();
export const projectViewerPermission = buildViewerPermissionRules();
export const projectNoAccessPermissions = buildNoAccessProjectPermission();
// SSH
export const sshHostBootstrapPermissions = buildSshHostBootstrapPermissionRules();
// KMS
export const cryptographicOperatorPermissions = buildCryptographicOperatorPermissionRules();
@@ -12,6 +12,14 @@ import {
TIdentityProjectMemberships,
TProjectMemberships
} from "@app/db/schemas";
import {
cryptographicOperatorPermissions,
projectAdminPermissions,
projectMemberPermissions,
projectNoAccessPermissions,
projectViewerPermission,
sshHostBootstrapPermissions
} from "@app/ee/services/permission/default-roles";
import { conditionsMatcher } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { objectify } from "@app/lib/fn";
@@ -32,14 +40,7 @@ import {
TGetServiceTokenProjectPermissionArg,
TGetUserProjectPermissionArg
} from "./permission-service-types";
import {
buildServiceTokenProjectPermission,
projectAdminPermissions,
projectMemberPermissions,
projectNoAccessPermissions,
ProjectPermissionSet,
projectViewerPermission
} from "./project-permission";
import { buildServiceTokenProjectPermission, ProjectPermissionSet } from "./project-permission";
type TPermissionServiceFactoryDep = {
orgRoleDAL: Pick<TOrgRoleDALFactory, "findOne">;
@@ -95,6 +96,10 @@ export const permissionServiceFactory = ({
return projectViewerPermission;
case ProjectMembershipRole.NoAccess:
return projectNoAccessPermissions;
case ProjectMembershipRole.SshHostBootstrapper:
return sshHostBootstrapPermissions;
case ProjectMembershipRole.KmsCryptographicOperator:
return cryptographicOperatorPermissions;
case ProjectMembershipRole.Custom: {
return unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
@@ -678,403 +678,6 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [
export type TProjectPermissionV2Schema = z.infer<typeof ProjectPermissionV2Schema>;
const buildAdminPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
// Admins get full access to everything
[
ProjectPermissionSub.SecretFolders,
ProjectPermissionSub.SecretImports,
ProjectPermissionSub.SecretApproval,
ProjectPermissionSub.Role,
ProjectPermissionSub.Integrations,
ProjectPermissionSub.Webhooks,
ProjectPermissionSub.ServiceTokens,
ProjectPermissionSub.Settings,
ProjectPermissionSub.Environments,
ProjectPermissionSub.Tags,
ProjectPermissionSub.AuditLogs,
ProjectPermissionSub.IpAllowList,
ProjectPermissionSub.CertificateAuthorities,
ProjectPermissionSub.CertificateTemplates,
ProjectPermissionSub.PkiAlerts,
ProjectPermissionSub.PkiCollections,
ProjectPermissionSub.SshCertificateAuthorities,
ProjectPermissionSub.SshCertificates,
ProjectPermissionSub.SshCertificateTemplates,
ProjectPermissionSub.SshHostGroups
].forEach((el) => {
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
el
);
});
can(
[
ProjectPermissionCertificateActions.Read,
ProjectPermissionCertificateActions.Edit,
ProjectPermissionCertificateActions.Create,
ProjectPermissionCertificateActions.Delete,
ProjectPermissionCertificateActions.ReadPrivateKey
],
ProjectPermissionSub.Certificates
);
can(
[
ProjectPermissionSshHostActions.Edit,
ProjectPermissionSshHostActions.Read,
ProjectPermissionSshHostActions.Create,
ProjectPermissionSshHostActions.Delete,
ProjectPermissionSshHostActions.IssueHostCert
],
ProjectPermissionSub.SshHosts
);
can(
[
ProjectPermissionMemberActions.Create,
ProjectPermissionMemberActions.Edit,
ProjectPermissionMemberActions.Delete,
ProjectPermissionMemberActions.Read,
ProjectPermissionMemberActions.GrantPrivileges,
ProjectPermissionMemberActions.AssumePrivileges
],
ProjectPermissionSub.Member
);
can(
[
ProjectPermissionGroupActions.Create,
ProjectPermissionGroupActions.Edit,
ProjectPermissionGroupActions.Delete,
ProjectPermissionGroupActions.Read,
ProjectPermissionGroupActions.GrantPrivileges
],
ProjectPermissionSub.Groups
);
can(
[
ProjectPermissionIdentityActions.Create,
ProjectPermissionIdentityActions.Edit,
ProjectPermissionIdentityActions.Delete,
ProjectPermissionIdentityActions.Read,
ProjectPermissionIdentityActions.GrantPrivileges,
ProjectPermissionIdentityActions.AssumePrivileges
],
ProjectPermissionSub.Identity
);
can(
[
ProjectPermissionSecretActions.DescribeAndReadValue,
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Delete
],
ProjectPermissionSub.Secrets
);
can(
[
ProjectPermissionDynamicSecretActions.ReadRootCredential,
ProjectPermissionDynamicSecretActions.EditRootCredential,
ProjectPermissionDynamicSecretActions.CreateRootCredential,
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
ProjectPermissionDynamicSecretActions.Lease
],
ProjectPermissionSub.DynamicSecrets
);
can([ProjectPermissionActions.Edit, ProjectPermissionActions.Delete], ProjectPermissionSub.Project);
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
can([ProjectPermissionActions.Edit], ProjectPermissionSub.Kms);
can(
[
ProjectPermissionCmekActions.Create,
ProjectPermissionCmekActions.Edit,
ProjectPermissionCmekActions.Delete,
ProjectPermissionCmekActions.Read,
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt,
ProjectPermissionCmekActions.Sign,
ProjectPermissionCmekActions.Verify
],
ProjectPermissionSub.Cmek
);
can(
[
ProjectPermissionSecretSyncActions.Create,
ProjectPermissionSecretSyncActions.Edit,
ProjectPermissionSecretSyncActions.Delete,
ProjectPermissionSecretSyncActions.Read,
ProjectPermissionSecretSyncActions.SyncSecrets,
ProjectPermissionSecretSyncActions.ImportSecrets,
ProjectPermissionSecretSyncActions.RemoveSecrets
],
ProjectPermissionSub.SecretSyncs
);
can(
[
ProjectPermissionKmipActions.CreateClients,
ProjectPermissionKmipActions.UpdateClients,
ProjectPermissionKmipActions.DeleteClients,
ProjectPermissionKmipActions.ReadClients,
ProjectPermissionKmipActions.GenerateClientCertificates
],
ProjectPermissionSub.Kmip
);
can(
[
ProjectPermissionSecretRotationActions.Create,
ProjectPermissionSecretRotationActions.Edit,
ProjectPermissionSecretRotationActions.Delete,
ProjectPermissionSecretRotationActions.Read,
ProjectPermissionSecretRotationActions.ReadGeneratedCredentials,
ProjectPermissionSecretRotationActions.RotateSecrets
],
ProjectPermissionSub.SecretRotation
);
return rules;
};
export const projectAdminPermissions = buildAdminPermissionRules();
const buildMemberPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(
[
ProjectPermissionSecretActions.DescribeAndReadValue,
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Delete
],
ProjectPermissionSub.Secrets
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.SecretFolders
);
can(
[
ProjectPermissionDynamicSecretActions.ReadRootCredential,
ProjectPermissionDynamicSecretActions.EditRootCredential,
ProjectPermissionDynamicSecretActions.CreateRootCredential,
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
ProjectPermissionDynamicSecretActions.Lease
],
ProjectPermissionSub.DynamicSecrets
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.SecretImports
);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SecretApproval);
can([ProjectPermissionSecretRotationActions.Read], ProjectPermissionSub.SecretRotation);
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
can([ProjectPermissionMemberActions.Read, ProjectPermissionMemberActions.Create], ProjectPermissionSub.Member);
can([ProjectPermissionGroupActions.Read], ProjectPermissionSub.Groups);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Integrations
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Webhooks
);
can(
[
ProjectPermissionIdentityActions.Read,
ProjectPermissionIdentityActions.Edit,
ProjectPermissionIdentityActions.Create,
ProjectPermissionIdentityActions.Delete
],
ProjectPermissionSub.Identity
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.ServiceTokens
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Settings
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Environments
);
can(
[
ProjectPermissionActions.Read,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Create,
ProjectPermissionActions.Delete
],
ProjectPermissionSub.Tags
);
can([ProjectPermissionActions.Read], ProjectPermissionSub.Role);
can([ProjectPermissionActions.Read], ProjectPermissionSub.AuditLogs);
can([ProjectPermissionActions.Read], ProjectPermissionSub.IpAllowList);
// double check if all CRUD are needed for CA and Certificates
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateAuthorities);
can(
[
ProjectPermissionCertificateActions.Read,
ProjectPermissionCertificateActions.Edit,
ProjectPermissionCertificateActions.Create,
ProjectPermissionCertificateActions.Delete
],
ProjectPermissionSub.Certificates
);
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateTemplates);
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificates);
can([ProjectPermissionActions.Create], ProjectPermissionSub.SshCertificates);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateTemplates);
can([ProjectPermissionSshHostActions.Read], ProjectPermissionSub.SshHosts);
can(
[
ProjectPermissionCmekActions.Create,
ProjectPermissionCmekActions.Edit,
ProjectPermissionCmekActions.Delete,
ProjectPermissionCmekActions.Read,
ProjectPermissionCmekActions.Encrypt,
ProjectPermissionCmekActions.Decrypt,
ProjectPermissionCmekActions.Sign,
ProjectPermissionCmekActions.Verify
],
ProjectPermissionSub.Cmek
);
can(
[
ProjectPermissionSecretSyncActions.Create,
ProjectPermissionSecretSyncActions.Edit,
ProjectPermissionSecretSyncActions.Delete,
ProjectPermissionSecretSyncActions.Read,
ProjectPermissionSecretSyncActions.SyncSecrets,
ProjectPermissionSecretSyncActions.ImportSecrets,
ProjectPermissionSecretSyncActions.RemoveSecrets
],
ProjectPermissionSub.SecretSyncs
);
return rules;
};
export const projectMemberPermissions = buildMemberPermissionRules();
const buildViewerPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
can(ProjectPermissionSecretActions.DescribeAndReadValue, ProjectPermissionSub.Secrets);
can(ProjectPermissionSecretActions.DescribeSecret, ProjectPermissionSub.Secrets);
can(ProjectPermissionSecretActions.ReadValue, ProjectPermissionSub.Secrets);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretFolders);
can(ProjectPermissionDynamicSecretActions.ReadRootCredential, ProjectPermissionSub.DynamicSecrets);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
can(ProjectPermissionSecretRotationActions.Read, ProjectPermissionSub.SecretRotation);
can(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
can(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
can(ProjectPermissionIdentityActions.Read, ProjectPermissionSub.Identity);
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates);
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
can(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs);
return rules;
};
export const projectViewerPermission = buildViewerPermissionRules();
const buildNoAccessProjectPermission = () => {
const { rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
return rules;
};
export const buildServiceTokenProjectPermission = (
scopes: Array<{ secretPath: string; environment: string }>,
permission: string[]
@@ -1116,8 +719,6 @@ export const buildServiceTokenProjectPermission = (
return build({ conditionsMatcher });
};
export const projectNoAccessPermissions = buildNoAccessProjectPermission();
/* eslint-disable */
/**
@@ -1,22 +1,27 @@
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
import { ProjectType } from "@app/db/schemas";
import {
InfisicalProjectTemplate,
TUnpackedPermission
} from "@app/ee/services/project-template/project-template-types";
import { getPredefinedRoles } from "@app/services/project-role/project-role-fns";
export const getDefaultProjectTemplate = (orgId: string) => ({
import { ProjectTemplateDefaultEnvironments } from "./project-template-constants";
export const getDefaultProjectTemplate = (orgId: string, type: ProjectType) => ({
id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // random ID to appease zod
type,
name: InfisicalProjectTemplate.Default,
createdAt: new Date(),
updatedAt: new Date(),
description: "Infisical's default project template",
environments: ProjectTemplateDefaultEnvironments,
roles: [...getPredefinedRoles("project-template")].map(({ name, slug, permissions }) => ({
name,
slug,
permissions: permissions as TUnpackedPermission[]
})),
description: `Infisical's ${type ? `"${type}"` : ""} default project template`,
environments: type === ProjectType.SecretManager ? ProjectTemplateDefaultEnvironments : null,
roles: [...getPredefinedRoles({ projectId: "project-template", projectType: type })].map(
({ name, slug, permissions }) => ({
name,
slug,
permissions: permissions as TUnpackedPermission[]
})
),
orgId
});
@@ -1,10 +1,11 @@
import { ForbiddenError } from "@casl/ability";
import { packRules } from "@casl/ability/extra";
import { TProjectTemplates } from "@app/db/schemas";
import { ProjectType, TProjectTemplates } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
import { getDefaultProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
import {
TCreateProjectTemplateDTO,
@@ -32,11 +33,13 @@ const $unpackProjectTemplate = ({ roles, environments, ...rest }: TProjectTempla
...rest,
environments: environments as TProjectTemplateEnvironment[],
roles: [
...getPredefinedRoles("project-template").map(({ name, slug, permissions }) => ({
name,
slug,
permissions: permissions as TUnpackedPermission[]
})),
...getPredefinedRoles({ projectId: "project-template", projectType: rest.type as ProjectType }).map(
({ name, slug, permissions }) => ({
name,
slug,
permissions: permissions as TUnpackedPermission[]
})
),
...(roles as TProjectTemplateRole[]).map((role) => ({
...role,
permissions: unpackPermissions(role.permissions)
@@ -49,7 +52,7 @@ export const projectTemplateServiceFactory = ({
permissionService,
projectTemplateDAL
}: TProjectTemplatesServiceFactoryDep) => {
const listProjectTemplatesByOrg = async (actor: OrgServiceActor) => {
const listProjectTemplatesByOrg = async (actor: OrgServiceActor, type?: ProjectType) => {
const plan = await licenseService.getPlan(actor.orgId);
if (!plan.projectTemplates)
@@ -68,11 +71,14 @@ export const projectTemplateServiceFactory = ({
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
const projectTemplates = await projectTemplateDAL.find({
orgId: actor.orgId
orgId: actor.orgId,
...(type ? { type } : {})
});
return [
getDefaultProjectTemplate(actor.orgId),
...(type
? [getDefaultProjectTemplate(actor.orgId, type)]
: Object.values(ProjectType).map((projectType) => getDefaultProjectTemplate(actor.orgId, projectType))),
...projectTemplates.map((template) => $unpackProjectTemplate(template))
];
};
@@ -134,7 +140,7 @@ export const projectTemplateServiceFactory = ({
};
const createProjectTemplate = async (
{ roles, environments, ...params }: TCreateProjectTemplateDTO,
{ roles, environments, type, ...params }: TCreateProjectTemplateDTO,
actor: OrgServiceActor
) => {
const plan = await licenseService.getPlan(actor.orgId);
@@ -169,8 +175,10 @@ export const projectTemplateServiceFactory = ({
const projectTemplate = await projectTemplateDAL.create({
...params,
roles: JSON.stringify(roles.map((role) => ({ ...role, permissions: packRules(role.permissions) }))),
environments: JSON.stringify(environments),
orgId: actor.orgId
environments:
type === ProjectType.SecretManager ? JSON.stringify(environments ?? ProjectTemplateDefaultEnvironments) : null,
orgId: actor.orgId,
type
});
return $unpackProjectTemplate(projectTemplate);
@@ -202,6 +210,12 @@ export const projectTemplateServiceFactory = ({
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
if (projectTemplate.type !== ProjectType.SecretManager && environments)
throw new BadRequestError({ message: "Cannot configure environments for non-SecretManager project templates" });
if (projectTemplate.type === ProjectType.SecretManager && environments === null)
throw new BadRequestError({ message: "Environments cannot be removed for SecretManager project templates" });
if (params.name && projectTemplate.name !== params.name) {
const isConflictingName = Boolean(
await projectTemplateDAL.findOne({
@@ -1,6 +1,6 @@
import { z } from "zod";
import { TProjectEnvironments } from "@app/db/schemas";
import { ProjectType, TProjectEnvironments } from "@app/db/schemas";
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
@@ -15,8 +15,9 @@ export type TProjectTemplateRole = {
export type TCreateProjectTemplateDTO = {
name: string;
description?: string;
type: ProjectType;
roles: TProjectTemplateRole[];
environments: TProjectTemplateEnvironment[];
environments?: TProjectTemplateEnvironment[] | null;
};
export type TUpdateProjectTemplateDTO = Partial<TCreateProjectTemplateDTO>;
+4
View File
@@ -1822,8 +1822,12 @@ export const KMS = {
};
export const ProjectTemplates = {
LIST: {
type: "The type of project template to list."
},
CREATE: {
name: "The name of the project template to be created. Must be slug-friendly.",
type: "The type of project template to be created.",
description: "An optional description of the project template.",
roles: "The roles to be created when the template is applied to a project.",
environments: "The environments to be created when the template is applied to a project."
@@ -1,12 +1,15 @@
import { ProjectMembershipRole } from "@app/db/schemas";
import { ProjectMembershipRole, ProjectType } from "@app/db/schemas";
import {
cryptographicOperatorPermissions,
projectAdminPermissions,
projectMemberPermissions,
projectNoAccessPermissions,
projectViewerPermission
} from "@app/ee/services/permission/project-permission";
projectViewerPermission,
sshHostBootstrapPermissions
} from "@app/ee/services/permission/default-roles";
import { TGetPredefinedRolesDTO } from "@app/services/project-role/project-role-types";
export const getPredefinedRoles = (projectId: string, roleFilter?: ProjectMembershipRole) => {
export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetPredefinedRolesDTO) => {
return [
{
id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // dummy userid
@@ -28,6 +31,28 @@ export const getPredefinedRoles = (projectId: string, roleFilter?: ProjectMember
createdAt: new Date(),
updatedAt: new Date()
},
{
id: "b11b49a9-09a9-4443-916a-4246f9ff2c73", // dummy user for zod validation in response
projectId,
name: "SSH Host Bootstrapper",
slug: ProjectMembershipRole.SshHostBootstrapper,
permissions: sshHostBootstrapPermissions,
description: "Create and issue SSH Hosts in a project",
createdAt: new Date(),
updatedAt: new Date(),
type: ProjectType.SSH
},
{
id: "b11b49a9-09a9-4443-916a-4246f9ff2c74", // dummy user for zod validation in response
projectId,
name: "Cryptographic Operator",
slug: ProjectMembershipRole.KmsCryptographicOperator,
permissions: cryptographicOperatorPermissions,
description: "Perform cryptographic operations, such as encryption and signing, in a project",
createdAt: new Date(),
updatedAt: new Date(),
type: ProjectType.KMS
},
{
id: "b11b49a9-09a9-4443-916a-4246f9ff2c71", // dummy user for zod validation in response
projectId,
@@ -48,5 +73,5 @@ export const getPredefinedRoles = (projectId: string, roleFilter?: ProjectMember
createdAt: new Date(),
updatedAt: new Date()
}
].filter(({ slug }) => !roleFilter || roleFilter.includes(slug));
].filter(({ slug, type }) => (type ? type === projectType : true) && (!roleFilter || roleFilter.includes(slug)));
};
@@ -2,7 +2,7 @@ import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import { requestContext } from "@fastify/request-context";
import { ActionProjectType, ProjectMembershipRole, TableName } from "@app/db/schemas";
import { ActionProjectType, ProjectMembershipRole, ProjectType, TableName, TProjects } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import {
ProjectPermissionActions,
@@ -34,7 +34,7 @@ type TProjectRoleServiceFactoryDep = {
projectRoleDAL: TProjectRoleDALFactory;
identityDAL: Pick<TIdentityDALFactory, "findById">;
userDAL: Pick<TUserDALFactory, "findById">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findProjectById">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
identityProjectMembershipRoleDAL: TIdentityProjectMembershipRoleDALFactory;
projectUserMembershipRoleDAL: TProjectUserMembershipRoleDALFactory;
@@ -98,30 +98,34 @@ export const projectRoleServiceFactory = ({
roleSlug,
filter
}: TGetRoleDetailsDTO) => {
let projectId = "";
let project: TProjects;
if (filter.type === ProjectRoleServiceIdentifierType.SLUG) {
const project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: "Project not found" });
projectId = project.id;
project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId);
} else {
projectId = filter.projectId;
project = await projectDAL.findProjectById(filter.projectId);
}
if (!project) throw new NotFoundError({ message: "Project not found" });
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
if (roleSlug !== "custom" && Object.values(ProjectMembershipRole).includes(roleSlug as ProjectMembershipRole)) {
const predefinedRole = getPredefinedRoles(projectId, roleSlug as ProjectMembershipRole)[0];
const predefinedRole = getPredefinedRoles({
projectId: project.id,
projectType: project.type as ProjectType,
roleFilter: roleSlug as ProjectMembershipRole
})[0];
return { ...predefinedRole, permissions: UnpackedPermissionSchema.array().parse(predefinedRole.permissions) };
}
const customRole = await projectRoleDAL.findOne({ slug: roleSlug, projectId });
const customRole = await projectRoleDAL.findOne({ slug: roleSlug, projectId: project.id });
if (!customRole) throw new NotFoundError({ message: `Project role with slug '${roleSlug}' not found` });
return { ...customRole, permissions: unpackPermissions(customRole.permissions) };
};
@@ -194,29 +198,32 @@ export const projectRoleServiceFactory = ({
};
const listRoles = async ({ actorOrgId, actorAuthMethod, actorId, actor, filter }: TListRolesDTO) => {
let projectId = "";
let project: TProjects;
if (filter.type === ProjectRoleServiceIdentifierType.SLUG) {
const project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
projectId = project.id;
project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId);
} else {
projectId = filter.projectId;
project = await projectDAL.findProjectById(filter.projectId);
}
if (!project) throw new BadRequestError({ message: "Project not found" });
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
const customRoles = await projectRoleDAL.find(
{ projectId },
{ projectId: project.id },
{ sort: [[`${TableName.ProjectRoles}.slug` as "slug", "asc"]] }
);
const roles = [...getPredefinedRoles(projectId), ...(customRoles || [])];
const roles = [
...getPredefinedRoles({ projectId: project.id, projectType: project.type as ProjectType }),
...(customRoles || [])
];
return roles;
};
@@ -1,4 +1,4 @@
import { TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas";
import { ProjectMembershipRole, ProjectType, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas";
import { TProjectPermission } from "@app/lib/types";
export enum ProjectRoleServiceIdentifierType {
@@ -34,3 +34,9 @@ export type TListRolesDTO = {
| { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string }
| { type: ProjectRoleServiceIdentifierType.ID; projectId: string };
} & Omit<TProjectPermission, "projectId">;
export type TGetPredefinedRolesDTO = {
projectId: string;
projectType: ProjectType;
roleFilter?: ProjectMembershipRole;
};
@@ -329,14 +329,16 @@ export const projectServiceFactory = ({
// set default environments and root folder for provided environments
let envs: TProjectEnvironments[] = [];
if (projectTemplate) {
envs = await projectEnvDAL.insertMany(
projectTemplate.environments.map((env) => ({ ...env, projectId: project.id })),
tx
);
await folderDAL.insertMany(
envs.map(({ id }) => ({ name: ROOT_FOLDER_NAME, envId: id, version: 1 })),
tx
);
if (projectTemplate.environments) {
envs = await projectEnvDAL.insertMany(
projectTemplate.environments.map((env) => ({ ...env, projectId: project.id })),
tx
);
await folderDAL.insertMany(
envs.map(({ id }) => ({ name: ROOT_FOLDER_NAME, envId: id, version: 1 })),
tx
);
}
await projectRoleDAL.insertMany(
projectTemplate.packedRoles.map((role) => ({
...role,
@@ -592,7 +594,10 @@ export const projectServiceFactory = ({
workspaces.map(async (workspace) => {
return {
...workspace,
roles: [...(workspaceMappedToRoles[workspace.id] || []), ...getPredefinedRoles(workspace.id)]
roles: [
...(workspaceMappedToRoles[workspace.id] || []),
...getPredefinedRoles({ projectId: workspace.id, projectType: workspace.type as ProjectType })
]
};
})
);