feat: removed console and resolved drift in tf due to folder removing in v1

This commit is contained in:
=
2024-10-25 00:59:41 +05:30
parent 7ae28596ec
commit 58ce623a2c
15 changed files with 128 additions and 64 deletions
@@ -1,4 +1,3 @@
import { packRules } 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";
@@ -82,7 +81,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
isTemporary: false, isTemporary: false,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts // @ts-ignore-error this is valid ts
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(permission))) permissions: backfillPermissionV1SchemaToV2Schema(permission)
}); });
return { privilege }; return { privilege };
} }
@@ -164,7 +163,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
isTemporary: true, isTemporary: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts // @ts-ignore-error this is valid ts
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(permission))) permissions: backfillPermissionV1SchemaToV2Schema(permission)
}); });
return { privilege }; return { privilege };
} }
@@ -249,10 +248,12 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
projectSlug: req.body.projectSlug, projectSlug: req.body.projectSlug,
data: { data: {
...updatedInfo, ...updatedInfo,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts
permissions: permission permissions: permission
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts // @ts-ignore-error this is valid ts
JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(permission))) backfillPermissionV1SchemaToV2Schema(permission)
: undefined : undefined
} }
}); });
@@ -68,9 +68,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}, },
data: { data: {
...req.body, ...req.body,
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions))) permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true)))
} }
}); });
return { role }; return { role };
} }
}); });
@@ -129,7 +130,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
data: { data: {
...req.body, ...req.body,
permissions: req.body.permissions permissions: req.body.permissions
? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions))) ? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true)))
: undefined : undefined
} }
}); });
@@ -241,6 +242,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}, },
roleSlug: req.params.slug roleSlug: req.params.slug
}); });
return { role }; return { role };
} }
}); });
@@ -1,4 +1,3 @@
import { packRules } 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";
@@ -70,7 +69,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
projectMembershipId: req.body.projectMembershipId, projectMembershipId: req.body.projectMembershipId,
...req.body.type, ...req.body.type,
slug: req.body.slug || slugify(alphaNumericNanoId(8)), slug: req.body.slug || slugify(alphaNumericNanoId(8)),
permissions: JSON.stringify(packRules(req.body.permissions)) permissions: req.body.permissions
}); });
return { privilege }; return { privilege };
} }
@@ -137,7 +136,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
permissions: req.body.permissions permissions: req.body.permissions
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts // @ts-ignore-error this is valid ts
JSON.stringify(packRules(req.body.permissions)) req.body.permissions
: undefined, : undefined,
privilegeId: req.params.privilegeId privilegeId: req.params.privilegeId
}); });
@@ -1,4 +1,3 @@
import { packRules } 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";
@@ -78,7 +77,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
identityId: req.body.identityId, identityId: req.body.identityId,
...req.body.type, ...req.body.type,
slug: req.body.slug || slugify(alphaNumericNanoId(8)), slug: req.body.slug || slugify(alphaNumericNanoId(8)),
permissions: JSON.stringify(packRules(req.body.permissions)) permissions: req.body.permissions
}); });
return { privilege }; return { privilege };
} }
@@ -149,11 +148,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
data: { data: {
...req.body, ...req.body,
...req.body.type, ...req.body.type,
permissions: req.body.permissions permissions: req.body.permissions || undefined
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts
JSON.stringify(packRules(req.body.permissions))
: undefined
} }
}); });
return { privilege }; return { privilege };
@@ -1,4 +1,5 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { packRules } from "@casl/ability/extra";
import ms from "ms"; import ms from "ms";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
@@ -61,14 +62,18 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission( const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY, ActorType.IDENTITY,
identityId, identityId,
identityProjectMembership.projectId, identityProjectMembership.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
// we need to validate that the privilege given is not higher than the assigning users permission
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
if (!hasRequiredPriviledges) if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
@@ -78,11 +83,12 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
}); });
if (existingSlug) throw new BadRequestError({ message: "Additional privilege with provided slug already exists" }); if (existingSlug) throw new BadRequestError({ message: "Additional privilege with provided slug already exists" });
const packedPermission = JSON.stringify(packRules(customPermission));
if (!dto.isTemporary) { if (!dto.isTemporary) {
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({ const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id, projectMembershipId: identityProjectMembership.id,
slug, slug,
permissions: customPermission permissions: packedPermission
}); });
return { return {
@@ -95,7 +101,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({ const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id, projectMembershipId: identityProjectMembership.id,
slug, slug,
permissions: customPermission, permissions: packedPermission,
isTemporary: true, isTemporary: true,
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative, temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: dto.temporaryRange, temporaryRange: dto.temporaryRange,
@@ -133,14 +139,18 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission( const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY, ActorType.IDENTITY,
identityProjectMembership.identityId, identityProjectMembership.identityId,
identityProjectMembership.projectId, identityProjectMembership.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
// we need to validate that the privilege given is not higher than the assigning users permission
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
if (!hasRequiredPriviledges) if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
@@ -154,12 +164,13 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
} }
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary; const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
const packedPermission = data.permissions ? JSON.stringify(packRules(data.permissions)) : undefined;
if (isTemporary) { if (isTemporary) {
const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime; const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange; const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange;
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, { const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
slug: data.slug, slug: data.slug,
permissions: data.permissions, permissions: packedPermission,
isTemporary: data.isTemporary, isTemporary: data.isTemporary,
temporaryRange: data.temporaryRange, temporaryRange: data.temporaryRange,
temporaryMode: data.temporaryMode, temporaryMode: data.temporaryMode,
@@ -174,7 +185,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, { const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
slug: data.slug, slug: data.slug,
permissions: data.permissions, permissions: packedPermission,
isTemporary: false, isTemporary: false,
temporaryAccessStartTime: null, temporaryAccessStartTime: null,
temporaryAccessEndTime: null, temporaryAccessEndTime: null,
@@ -1,11 +1,13 @@
import { TProjectPermission } from "@app/lib/types"; import { TProjectPermission } from "@app/lib/types";
import { TProjectPermissionV2Schema } from "../permission/project-permission";
export enum IdentityProjectAdditionalPrivilegeTemporaryMode { export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
Relative = "relative" Relative = "relative"
} }
export type TCreateIdentityPrivilegeDTO = { export type TCreateIdentityPrivilegeDTO = {
permissions: unknown; permissions: TProjectPermissionV2Schema[];
identityId: string; identityId: string;
projectId: string; projectId: string;
slug: string; slug: string;
@@ -24,7 +26,7 @@ export type TCreateIdentityPrivilegeDTO = {
export type TUpdateIdentityPrivilegeByIdDTO = { id: string } & Omit<TProjectPermission, "projectId"> & { export type TUpdateIdentityPrivilegeByIdDTO = { id: string } & Omit<TProjectPermission, "projectId"> & {
data: Partial<{ data: Partial<{
permissions: unknown; permissions: TProjectPermissionV2Schema[];
slug: string; slug: string;
isTemporary: boolean; isTemporary: boolean;
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative; temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
@@ -1,5 +1,5 @@
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability"; import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, unpackRules } from "@casl/ability/extra"; import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import ms from "ms"; import ms from "ms";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
@@ -70,14 +70,18 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission( const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY, ActorType.IDENTITY,
identityId, identityId,
identityProjectMembership.projectId, identityProjectMembership.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
// we need to validate that the privilege given is not higher than the assigning users permission
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
if (!hasRequiredPriviledges) if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
@@ -87,11 +91,12 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
}); });
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" }); if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
const packedPermission = JSON.stringify(packRules(customPermission));
if (!dto.isTemporary) { if (!dto.isTemporary) {
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({ const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id, projectMembershipId: identityProjectMembership.id,
slug, slug,
permissions: customPermission permissions: packedPermission
}); });
return { return {
...additionalPrivilege, ...additionalPrivilege,
@@ -103,7 +108,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({ const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id, projectMembershipId: identityProjectMembership.id,
slug, slug,
permissions: customPermission, permissions: packedPermission,
isTemporary: true, isTemporary: true,
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative, temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: dto.temporaryRange, temporaryRange: dto.temporaryRange,
@@ -142,14 +147,19 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY, ActorType.IDENTITY,
identityProjectMembership.identityId, identityProjectMembership.identityId,
identityProjectMembership.projectId, identityProjectMembership.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
// we need to validate that the privilege given is not higher than the assigning users permission
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
if (!hasRequiredPriviledges) if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
@@ -172,23 +182,29 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
} }
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary; const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
const packedPermission = data.permissions ? JSON.stringify(packRules(data.permissions)) : undefined;
if (isTemporary) { if (isTemporary) {
const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime; const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange; const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange;
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, { const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
...data, slug: data.slug,
permissions: packedPermission,
isTemporary: data.isTemporary,
temporaryRange: data.temporaryRange,
temporaryMode: data.temporaryMode,
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 { return {
...additionalPrivilege, ...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions) permissions: unpackPermissions(additionalPrivilege.permissions)
}; };
} }
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, { const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
...data, slug: data.slug,
permissions: packedPermission,
isTemporary: false, isTemporary: false,
temporaryAccessStartTime: null, temporaryAccessStartTime: null,
temporaryAccessEndTime: null, temporaryAccessEndTime: null,
@@ -1,11 +1,13 @@
import { TProjectPermission } from "@app/lib/types"; import { TProjectPermission } from "@app/lib/types";
import { TProjectPermissionV2Schema } from "../permission/project-permission";
export enum IdentityProjectAdditionalPrivilegeTemporaryMode { export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
Relative = "relative" Relative = "relative"
} }
export type TCreateIdentityPrivilegeDTO = { export type TCreateIdentityPrivilegeDTO = {
permissions: unknown; permissions: TProjectPermissionV2Schema[];
identityId: string; identityId: string;
projectSlug: string; projectSlug: string;
slug: string; slug: string;
@@ -27,7 +29,7 @@ export type TUpdateIdentityPrivilegeDTO = { slug: string; identityId: string; pr
"projectId" "projectId"
> & { > & {
data: Partial<{ data: Partial<{
permissions: unknown; permissions: TProjectPermissionV2Schema[];
slug: string; slug: string;
isTemporary: boolean; isTemporary: boolean;
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative; temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
@@ -21,7 +21,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
secretVersioning: true, secretVersioning: true,
pitRecovery: false, pitRecovery: false,
ipAllowlisting: false, ipAllowlisting: false,
rbac: false, rbac: true,
customRateLimits: false, customRateLimits: false,
customAlerts: false, customAlerts: false,
auditLogs: false, auditLogs: false,
@@ -420,6 +420,8 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [
...GeneralPermissionSchema ...GeneralPermissionSchema
]); ]);
export type TProjectPermissionV2Schema = z.infer<typeof ProjectPermissionV2Schema>;
const buildAdminPermissionRules = () => { const buildAdminPermissionRules = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility); const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
@@ -757,8 +759,11 @@ export const isAtLeastAsPrivilegedWorkspace = (
}; };
/* eslint-enable */ /* eslint-enable */
export const backfillPermissionV1SchemaToV2Schema = (data: z.infer<typeof ProjectPermissionV1Schema>[]) => { export const backfillPermissionV1SchemaToV2Schema = (
const formattedData = UnpackedPermissionSchema.array().parse(data); data: z.infer<typeof ProjectPermissionV1Schema>[],
dontRemoveReadFolderPermission?: boolean
) => {
let formattedData = UnpackedPermissionSchema.array().parse(data);
const secretSubjects = formattedData.filter((el) => el.subject === ProjectPermissionSub.Secrets); const secretSubjects = formattedData.filter((el) => el.subject === ProjectPermissionSub.Secrets);
// this means the folder permission as readonly is set // this means the folder permission as readonly is set
@@ -802,13 +807,15 @@ export const backfillPermissionV1SchemaToV2Schema = (data: z.infer<typeof Projec
}; };
}); });
return formattedData if (!dontRemoveReadFolderPermission) {
.filter((i) => i.subject !== ProjectPermissionSub.SecretFolders) formattedData = formattedData.filter((i) => i.subject !== ProjectPermissionSub.SecretFolders);
.concat( }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts return formattedData.concat(
secretImportPolicies, // eslint-disable-next-line @typescript-eslint/ban-ts-comment
dynamicSecretPolicies, // @ts-ignore-error this is valid ts
hasReadOnlyFolder.length ? [] : secretFolderPolicies secretImportPolicies,
); dynamicSecretPolicies,
hasReadOnlyFolder.length ? [] : secretFolderPolicies
);
}; };
@@ -1,5 +1,5 @@
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability"; import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, unpackRules } from "@casl/ability/extra"; import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import ms from "ms"; import ms from "ms";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
@@ -62,14 +62,18 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
const { permission: entityPermission } = await permissionService.getProjectPermission( const { permission: targetUserPermission } = await permissionService.getProjectPermission(
ActorType.USER, ActorType.USER,
projectMembership.userId, projectMembership.userId,
projectMembership.projectId, projectMembership.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, entityPermission);
// we need to validate that the privilege given is not higher than the assigning users permission
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
targetUserPermission.update(targetUserPermission.rules.concat(customPermission));
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetUserPermission);
if (!hasRequiredPriviledges) if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
@@ -81,12 +85,13 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
if (existingSlug) if (existingSlug)
throw new BadRequestError({ message: `Additional privilege with provided slug ${slug} already exists` }); throw new BadRequestError({ message: `Additional privilege with provided slug ${slug} already exists` });
const packedPermission = JSON.stringify(packRules(customPermission));
if (!dto.isTemporary) { if (!dto.isTemporary) {
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({ const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
userId: projectMembership.userId, userId: projectMembership.userId,
projectId: projectMembership.projectId, projectId: projectMembership.projectId,
slug, slug,
permissions: customPermission permissions: packedPermission
}); });
return { return {
...additionalPrivilege, ...additionalPrivilege,
@@ -99,7 +104,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
projectId: projectMembership.projectId, projectId: projectMembership.projectId,
userId: projectMembership.userId, userId: projectMembership.userId,
slug, slug,
permissions: customPermission, permissions: packedPermission,
isTemporary: true, isTemporary: true,
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative, temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: dto.temporaryRange, temporaryRange: dto.temporaryRange,
@@ -142,14 +147,18 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
const { permission: entityPermission } = await permissionService.getProjectPermission( const { permission: targetUserPermission } = await permissionService.getProjectPermission(
ActorType.USER, ActorType.USER,
projectMembership.userId, projectMembership.userId,
projectMembership.projectId, projectMembership.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, entityPermission);
// we need to validate that the privilege given is not higher than the assigning users permission
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
targetUserPermission.update(targetUserPermission.rules.concat(dto.permissions || []));
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetUserPermission);
if (!hasRequiredPriviledges) if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
@@ -164,12 +173,14 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
} }
const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : userPrivilege.isTemporary; const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : userPrivilege.isTemporary;
const packedPermission = dto.permissions && JSON.stringify(packRules(dto.permissions));
if (isTemporary) { if (isTemporary) {
const temporaryAccessStartTime = dto?.temporaryAccessStartTime || userPrivilege?.temporaryAccessStartTime; const temporaryAccessStartTime = dto?.temporaryAccessStartTime || userPrivilege?.temporaryAccessStartTime;
const temporaryRange = dto?.temporaryRange || userPrivilege?.temporaryRange; const temporaryRange = dto?.temporaryRange || userPrivilege?.temporaryRange;
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, { const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
slug: dto.slug, slug: dto.slug,
permissions: dto.permissions, permissions: packedPermission,
isTemporary: dto.isTemporary, isTemporary: dto.isTemporary,
temporaryRange: dto.temporaryRange, temporaryRange: dto.temporaryRange,
temporaryMode: dto.temporaryMode, temporaryMode: dto.temporaryMode,
@@ -185,7 +196,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, { const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
slug: dto.slug, slug: dto.slug,
permissions: dto.permissions, permissions: packedPermission,
isTemporary: false, isTemporary: false,
temporaryAccessStartTime: null, temporaryAccessStartTime: null,
temporaryAccessEndTime: null, temporaryAccessEndTime: null,
@@ -1,18 +1,20 @@
import { TProjectPermission } from "@app/lib/types"; import { TProjectPermission } from "@app/lib/types";
import { TProjectPermissionV2Schema } from "../permission/project-permission";
export enum ProjectUserAdditionalPrivilegeTemporaryMode { export enum ProjectUserAdditionalPrivilegeTemporaryMode {
Relative = "relative" Relative = "relative"
} }
export type TCreateUserPrivilegeDTO = ( export type TCreateUserPrivilegeDTO = (
| { | {
permissions: unknown; permissions: TProjectPermissionV2Schema[];
projectMembershipId: string; projectMembershipId: string;
slug: string; slug: string;
isTemporary: false; isTemporary: false;
} }
| { | {
permissions: unknown; permissions: TProjectPermissionV2Schema[];
projectMembershipId: string; projectMembershipId: string;
slug: string; slug: string;
isTemporary: true; isTemporary: true;
@@ -25,7 +27,7 @@ export type TCreateUserPrivilegeDTO = (
export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> & export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> &
Partial<{ Partial<{
permissions: unknown; permissions: TProjectPermissionV2Schema[];
slug: string; slug: string;
isTemporary: boolean; isTemporary: boolean;
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative; temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
@@ -7,6 +7,7 @@ import { TLicenseServiceFactory } from "@app/ee/services/license/license-service
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal"; import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn"; import { groupBy } from "@app/lib/fn";
@@ -36,7 +37,7 @@ import {
import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal";
type TProjectMembershipServiceFactoryDep = { type TProjectMembershipServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRole">;
smtpService: TSmtpService; smtpService: TSmtpService;
projectBotDAL: TProjectBotDALFactory; projectBotDAL: TProjectBotDALFactory;
projectMembershipDAL: TProjectMembershipDALFactory; projectMembershipDAL: TProjectMembershipDALFactory;
@@ -262,6 +263,21 @@ export const projectMembershipServiceFactory = ({
throw new ForbiddenRequestError({ message: "Forbidden member update" }); throw new ForbiddenRequestError({ message: "Forbidden member update" });
} }
for await (const { role: requestedRoleChange } of roles) {
const { permission: rolePermission } = await permissionService.getProjectPermissionByRole(
requestedRoleChange,
projectId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission);
if (!hasRequiredPriviledges) {
throw new ForbiddenRequestError({
message: `Failed to change to a more privileged role ${requestedRoleChange}`
});
}
}
// validate custom roles input // validate custom roles input
const customInputRoles = roles.filter( const customInputRoles = roles.filter(
({ role }) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole) ({ role }) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole)
@@ -94,7 +94,6 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
privilegeId || "" privilegeId || ""
); );
console.log(privilegeDetails);
const { permission } = useProjectPermission(); const { permission } = useProjectPermission();
const isMemberEditDisabled = permission.cannot( const isMemberEditDisabled = permission.cannot(
ProjectPermissionActions.Edit, ProjectPermissionActions.Edit,
@@ -179,6 +179,7 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
permissions.forEach((permission) => { permissions.forEach((permission) => {
const { subject: caslSub, action, conditions, inverted } = permission; const { subject: caslSub, action, conditions, inverted } = permission;
const subject = (typeof caslSub === "string" ? caslSub : caslSub[0]) as ProjectPermissionSub; const subject = (typeof caslSub === "string" ? caslSub : caslSub[0]) as ProjectPermissionSub;
if (!action.length) return;
if ( if (
[ [