mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 18:41:21 +00:00
Merge pull request #3550 from Infisical/project-specific-default-roles
Improvements: Refactor Project Templates and Project Type Policy Filtering/Specific Roles
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
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
|
||||
id: uuidv4(),
|
||||
projectId,
|
||||
name: "Admin",
|
||||
slug: ProjectMembershipRole.Admin,
|
||||
@@ -19,7 +24,7 @@ export const getPredefinedRoles = (projectId: string, roleFilter?: ProjectMember
|
||||
updatedAt: new Date()
|
||||
},
|
||||
{
|
||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c70", // dummy user for zod validation in response
|
||||
id: uuidv4(),
|
||||
projectId,
|
||||
name: "Developer",
|
||||
slug: ProjectMembershipRole.Member,
|
||||
@@ -29,7 +34,29 @@ export const getPredefinedRoles = (projectId: string, roleFilter?: ProjectMember
|
||||
updatedAt: new Date()
|
||||
},
|
||||
{
|
||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c71", // dummy user for zod validation in response
|
||||
id: uuidv4(),
|
||||
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: uuidv4(),
|
||||
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: uuidv4(),
|
||||
projectId,
|
||||
name: "Viewer",
|
||||
slug: ProjectMembershipRole.Viewer,
|
||||
@@ -39,7 +66,7 @@ export const getPredefinedRoles = (projectId: string, roleFilter?: ProjectMember
|
||||
updatedAt: new Date()
|
||||
},
|
||||
{
|
||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c72", // dummy user for zod validation in response
|
||||
id: uuidv4(),
|
||||
projectId,
|
||||
name: "No Access",
|
||||
slug: ProjectMembershipRole.NoAccess,
|
||||
@@ -48,5 +75,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 === 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,37 @@ 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
|
||||
});
|
||||
|
||||
if (!predefinedRole) throw new NotFoundError({ message: `Default role with slug '${roleSlug}' not found` });
|
||||
|
||||
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 +201,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 })
|
||||
]
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user