You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
12 KiB
270 lines
12 KiB
import {
|
|
AbilityBuilder,
|
|
ForcedSubject,
|
|
MongoAbility,
|
|
RawRuleOf,
|
|
buildMongoQueryMatcher,
|
|
createMongoAbility
|
|
} from "@casl/ability";
|
|
import { Membership } from "../../models";
|
|
import { IRole } from "../models/role";
|
|
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
|
import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js";
|
|
import picomatch from "picomatch";
|
|
|
|
const $glob: FieldInstruction<string> = {
|
|
type: "field",
|
|
validate(instruction, value) {
|
|
if (typeof value !== "string") {
|
|
throw new Error(`"${instruction.name}" expects value to be a string`);
|
|
}
|
|
}
|
|
};
|
|
|
|
const glob: JsInterpreter<FieldCondition<string>> = (node, object, context) => {
|
|
const secretPath = context.get(object, node.field);
|
|
const permissionSecretGlobPath = node.value;
|
|
return picomatch.isMatch(secretPath, permissionSecretGlobPath, { strictSlashes: false });
|
|
};
|
|
|
|
export const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob });
|
|
|
|
export enum ProjectPermissionActions {
|
|
Read = "read",
|
|
Create = "create",
|
|
Edit = "edit",
|
|
Delete = "delete"
|
|
}
|
|
|
|
export enum ProjectPermissionSub {
|
|
Role = "role",
|
|
Member = "member",
|
|
Settings = "settings",
|
|
Integrations = "integrations",
|
|
Webhooks = "webhooks",
|
|
ServiceTokens = "service-tokens",
|
|
Environments = "environments",
|
|
Tags = "tags",
|
|
AuditLogs = "audit-logs",
|
|
IpAllowList = "ip-allowlist",
|
|
Workspace = "workspace",
|
|
Secrets = "secrets",
|
|
SecretRollback = "secret-rollback",
|
|
SecretApproval = "secret-approval",
|
|
SecretRotation = "secret-rotation"
|
|
}
|
|
|
|
type SubjectFields = {
|
|
environment: string;
|
|
secretPath: string;
|
|
};
|
|
|
|
export type ProjectPermissionSet =
|
|
| [
|
|
ProjectPermissionActions,
|
|
ProjectPermissionSub.Secrets | (ForcedSubject<ProjectPermissionSub.Secrets> & SubjectFields)
|
|
]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Role]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Tags]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Member]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Integrations]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Webhooks]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.AuditLogs]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Environments]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.IpAllowList]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.Settings]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.SecretApproval]
|
|
| [ProjectPermissionActions, ProjectPermissionSub.SecretRotation]
|
|
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace]
|
|
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace]
|
|
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
|
|
| [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback];
|
|
|
|
const buildAdminPermission = () => {
|
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRotation);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretRotation);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Member);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Role);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Role);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Environments);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Tags);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.AuditLogs);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.AuditLogs);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.AuditLogs);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.IpAllowList);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.IpAllowList);
|
|
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace);
|
|
|
|
return build({ conditionsMatcher });
|
|
};
|
|
|
|
export const adminProjectPermissions = buildAdminPermission();
|
|
|
|
const buildMemberPermission = () => {
|
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Environments);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Tags);
|
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags);
|
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
|
|
|
return build({ conditionsMatcher });
|
|
};
|
|
|
|
export const memberProjectPermissions = buildMemberPermission();
|
|
|
|
const buildViewerPermission = () => {
|
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
|
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);
|
|
|
|
return build({ conditionsMatcher });
|
|
};
|
|
|
|
export const viewerProjectPermission = buildViewerPermission();
|
|
|
|
export const getUserProjectPermissions = async (userId: string, workspaceId: string) => {
|
|
// TODO(akhilmhdh): speed this up by pulling from cache later
|
|
const membership = await Membership.findOne({
|
|
user: userId,
|
|
workspace: workspaceId
|
|
})
|
|
.populate<{
|
|
customRole: IRole & { permissions: RawRuleOf<MongoAbility<ProjectPermissionSet>>[] };
|
|
}>("customRole")
|
|
.exec();
|
|
|
|
if (!membership || (membership.role === "custom" && !membership.customRole)) {
|
|
throw UnauthorizedRequestError({ message: "User doesn't belong to organization" });
|
|
}
|
|
|
|
if (membership.role === "admin") return { permission: adminProjectPermissions, membership };
|
|
if (membership.role === "member") return { permission: memberProjectPermissions, membership };
|
|
if (membership.role === "viewer") return { permission: viewerProjectPermission, membership };
|
|
|
|
if (membership.role === "custom") {
|
|
const permission = createMongoAbility<ProjectPermissionSet>(membership.customRole.permissions, {
|
|
conditionsMatcher
|
|
});
|
|
return { permission, membership };
|
|
}
|
|
|
|
throw BadRequestError({ message: "User role not found" });
|
|
};
|