feat: battle of ts - unlimited edition

This commit is contained in:
=
2025-10-10 00:09:46 +05:30
parent fd98465dec
commit 44c9575a3d
38 changed files with 492 additions and 498 deletions
@@ -1,3 +1,4 @@
// eslint-disable-next-line simple-import-sort/imports
import { RawRule } from "@casl/ability";
import { packRules } from "@casl/ability/extra";
@@ -23,6 +24,7 @@ import {
import { newNamespaceAdditionalPrivilegesFactory } from "./namespace/namespace-additional-privilege-factory";
import { newOrgAdditionalPrivilegesFactory } from "./org/org-additional-privilege-factory";
import { newProjectAdditionalPrivilegesFactory } from "./project/project-additional-privilege-factory";
import { ActorType } from "../auth/auth-type";
type TAdditionalPrivilegeServiceFactoryDep = {
additionalPrivilegeDAL: TAdditionalPrivilegeDALFactory;
@@ -52,11 +54,14 @@ export const additionalPrivilegeServiceFactory = ({
const createAdditionalPrivilege = async (dto: TCreateAdditionalPrivilegesDTO) => {
const { scopeData, data } = dto;
const factory = scopeFactory[scopeData.scope];
const { membershipId } = await factory.onCreateAdditionalPrivilegesGuard(dto);
await factory.onCreateAdditionalPrivilegesGuard(dto);
const scope = factory.getScopeField(dto.scopeData);
const dbActorField = data.actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId";
const existingSlug = await additionalPrivilegeDAL.findOne({
name: data.name,
membershipId
[dbActorField]: data.actorId,
[scope.key]: scope.value
});
if (existingSlug) throw new BadRequestError({ message: `Additional privilege with name ${data.name} exist` });
@@ -66,8 +71,9 @@ export const additionalPrivilegeServiceFactory = ({
if (!data.isTemporary) {
const additionalPrivilege = await additionalPrivilegeDAL.create({
membershipId,
name: data.name,
[dbActorField]: data.actorId,
[scope.key]: scope.value,
isTemporary: data.isTemporary,
permissions: JSON.stringify(packRules(data.permissions as RawRule[]))
});
@@ -83,7 +89,8 @@ export const additionalPrivilegeServiceFactory = ({
const relativeTempAllocatedTimeInMs = ms(data.temporaryRange);
const additionalPrivilege = await additionalPrivilegeDAL.create({
membershipId,
[dbActorField]: data.actorId,
[scope.key]: scope.value,
name: data.name,
isTemporary: data.isTemporary,
permissions: data.permissions,
@@ -103,11 +110,14 @@ export const additionalPrivilegeServiceFactory = ({
const updateAdditionalPrivilege = async (dto: TUpdateAdditionalPrivilegesDTO) => {
const { scopeData, data } = dto;
const factory = scopeFactory[scopeData.scope];
const { membershipId } = await factory.onUpdateAdditionalPrivilegesGuard(dto);
await factory.onUpdateAdditionalPrivilegesGuard(dto);
const scope = factory.getScopeField(dto.scopeData);
const dbActorField = dto.selector.actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId";
const existingPrivilege = await additionalPrivilegeDAL.findOne({
id: dto.selector.id,
membershipId
[dbActorField]: dto.selector.actorId,
[scope.key]: scope.value
});
if (!existingPrivilege)
throw new NotFoundError({ message: `Additional privilege with name ${data.name} doesn't exist` });
@@ -135,8 +145,7 @@ export const additionalPrivilegeServiceFactory = ({
}
const relativeTempAllocatedTimeInMs = ms(updatedData.temporaryRange);
const additionalPrivilege = await additionalPrivilegeDAL.create({
membershipId,
const additionalPrivilege = await additionalPrivilegeDAL.updateById(existingPrivilege.id, {
name: updatedData.name,
isTemporary: updatedData.isTemporary,
permissions: updatedData.permissions,
@@ -156,11 +165,14 @@ export const additionalPrivilegeServiceFactory = ({
const deleteAdditionalPrivilege = async (dto: TDeleteAdditionalPrivilegesDTO) => {
const { scopeData, selector } = dto;
const factory = scopeFactory[scopeData.scope];
const { membershipId } = await factory.onDeleteAdditionalPrivilegesGuard(dto);
await factory.onDeleteAdditionalPrivilegesGuard(dto);
const scope = factory.getScopeField(dto.scopeData);
const dbActorField = dto.selector.actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId";
const existingPrivilege = await additionalPrivilegeDAL.findOne({
id: selector.id,
membershipId
[dbActorField]: dto.selector.actorId,
[scope.key]: scope.value
});
if (!existingPrivilege)
throw new NotFoundError({ message: `Additional privilege with id ${selector.id} doesn't exist` });
@@ -174,10 +186,14 @@ export const additionalPrivilegeServiceFactory = ({
const getAdditionalPrivilegeById = async (dto: TGetAdditionalPrivilegesByIdDTO) => {
const { scopeData, selector } = dto;
const factory = scopeFactory[scopeData.scope];
const { membershipId } = await factory.onGetAdditionalPrivilegesByIdGuard(dto);
await factory.onGetAdditionalPrivilegesByIdGuard(dto);
const scope = factory.getScopeField(dto.scopeData);
const dbActorField = dto.selector.actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId";
const additionalPrivilege = await additionalPrivilegeDAL.findOne({
id: selector.id,
membershipId
[dbActorField]: dto.selector.actorId,
[scope.key]: scope.value
});
if (!additionalPrivilege)
throw new NotFoundError({ message: `Additional privilege with id ${selector.id} doesn't exist` });
@@ -190,10 +206,14 @@ export const additionalPrivilegeServiceFactory = ({
const getAdditionalPrivilegeByName = async (dto: TGetAdditionalPrivilegesByNameDTO) => {
const { scopeData, selector } = dto;
const factory = scopeFactory[scopeData.scope];
const { membershipId } = await factory.onGetAdditionalPrivilegesByIdGuard(dto);
await factory.onGetAdditionalPrivilegesByIdGuard(dto);
const dbActorField = dto.selector.actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId";
const scope = factory.getScopeField(dto.scopeData);
const additionalPrivilege = await additionalPrivilegeDAL.findOne({
name: selector.name,
membershipId
[dbActorField]: dto.selector.actorId,
[scope.key]: scope.value
});
if (!additionalPrivilege)
throw new NotFoundError({ message: `Additional privilege with name ${selector.name} doesn't exist` });
@@ -206,9 +226,13 @@ export const additionalPrivilegeServiceFactory = ({
const listAdditionalPrivileges = async (dto: TListAdditionalPrivilegesDTO) => {
const { scopeData } = dto;
const factory = scopeFactory[scopeData.scope];
const { membershipId } = await factory.onListAdditionalPrivilegesGuard(dto);
await factory.onListAdditionalPrivilegesGuard(dto);
const scope = factory.getScopeField(dto.scopeData);
const dbActorField = dto.selector.actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId";
const additionalPrivileges = await additionalPrivilegeDAL.find({
membershipId
[dbActorField]: dto.selector.actorId,
[scope.key]: scope.value
});
return {
@@ -1,14 +1,16 @@
import { AccessScopeData, MembershipActors, TemporaryPermissionMode } from "@app/db/schemas";
import { AccessScopeData, TemporaryPermissionMode } from "@app/db/schemas";
import { OrgServiceActor } from "@app/lib/types";
import { ActorType } from "../auth/auth-type";
export interface TAdditionalPrivilegesScopeFactory {
onCreateAdditionalPrivilegesGuard: (arg: TCreateAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
onUpdateAdditionalPrivilegesGuard: (arg: TUpdateAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
onDeleteAdditionalPrivilegesGuard: (arg: TDeleteAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
onListAdditionalPrivilegesGuard: (arg: TListAdditionalPrivilegesDTO) => Promise<{ membershipId: string }>;
onCreateAdditionalPrivilegesGuard: (arg: TCreateAdditionalPrivilegesDTO) => Promise<void>;
onUpdateAdditionalPrivilegesGuard: (arg: TUpdateAdditionalPrivilegesDTO) => Promise<void>;
onDeleteAdditionalPrivilegesGuard: (arg: TDeleteAdditionalPrivilegesDTO) => Promise<void>;
onListAdditionalPrivilegesGuard: (arg: TListAdditionalPrivilegesDTO) => Promise<void>;
onGetAdditionalPrivilegesByIdGuard: (
arg: TGetAdditionalPrivilegesByIdDTO | TGetAdditionalPrivilegesByNameDTO
) => Promise<{ membershipId: string }>;
) => Promise<void>;
getScopeField: (scope: AccessScopeData) => { key: "orgId" | "namespaceId" | "projectId"; value: string };
}
@@ -17,7 +19,7 @@ export type TCreateAdditionalPrivilegesDTO = {
scopeData: AccessScopeData;
data: {
actorId: string;
actorType: MembershipActors;
actorType: ActorType.USER | ActorType.IDENTITY;
name: string;
permissions: unknown;
isTemporary: boolean;
@@ -33,7 +35,7 @@ export type TUpdateAdditionalPrivilegesDTO = {
selector: {
id: string;
actorId: string;
actorType: MembershipActors;
actorType: ActorType.USER | ActorType.IDENTITY;
};
data: Partial<{
name: string;
@@ -50,7 +52,7 @@ export type TListAdditionalPrivilegesDTO = {
scopeData: AccessScopeData;
selector: {
actorId: string;
actorType: MembershipActors;
actorType: ActorType.USER | ActorType.IDENTITY;
};
};
@@ -60,7 +62,7 @@ export type TDeleteAdditionalPrivilegesDTO = {
selector: {
id: string;
actorId: string;
actorType: MembershipActors;
actorType: ActorType.USER | ActorType.IDENTITY;
};
};
@@ -70,7 +72,7 @@ export type TGetAdditionalPrivilegesByIdDTO = {
selector: {
id: string;
actorId: string;
actorType: MembershipActors;
actorType: ActorType.USER | ActorType.IDENTITY;
};
};
@@ -80,6 +82,6 @@ export type TGetAdditionalPrivilegesByNameDTO = {
selector: {
name: string;
actorId: string;
actorType: MembershipActors;
actorType: ActorType.USER | ActorType.IDENTITY;
};
};
@@ -1,6 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { AccessScope, ActionProjectType, MembershipActors } from "@app/db/schemas";
import { AccessScope, ActionProjectType } from "@app/db/schemas";
import {
constructPermissionErrorMessage,
validatePrivilegeChangeOperation
@@ -11,7 +11,7 @@ import {
ProjectPermissionMemberActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { BadRequestError, InternalServerError, PermissionBoundaryError } from "@app/lib/errors";
import { BadRequestError, PermissionBoundaryError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types";
import { ActorType } from "@app/services/auth/auth-type";
import { TMembershipDALFactory } from "@app/services/membership/membership-dal";
@@ -41,16 +41,6 @@ export const newProjectAdditionalPrivilegesFactory = ({
});
};
const $getActorType = (type: MembershipActors) => {
if (type === MembershipActors.Group)
throw new InternalServerError({ message: "Group additional privilege not implemented for projects" });
if (type === MembershipActors.Identity) return ActorType.IDENTITY;
if (type === MembershipActors.User) return ActorType.USER;
throw new InternalServerError({ message: `Group additional privilege not implemented for actor: ${String(type)}` });
};
const getScopeField: TAdditionalPrivilegesScopeFactory["getScopeField"] = (dto) => {
if (dto.scope === AccessScope.Project) {
return { key: "projectId" as const, value: dto.projectId };
@@ -61,11 +51,11 @@ export const newProjectAdditionalPrivilegesFactory = ({
const onCreateAdditionalPrivilegesGuard: TAdditionalPrivilegesScopeFactory["onCreateAdditionalPrivilegesGuard"] =
async (dto) => {
const scope = getScopeField(dto.scopeData);
const actorType = $getActorType(dto.data.actorType);
const { permission } = await $getPermission(dto.permission, scope.value);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
const { actorType } = dto.data;
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(dto.permission.orgId);
const { permission: targetUserPermission, memberships } = await $getPermission(
{ ...dto.permission, type: actorType, id: dto.data.actorId },
@@ -100,13 +90,12 @@ export const newProjectAdditionalPrivilegesFactory = ({
(el) => el[actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId"] === dto.data.actorId
);
if (!membership) throw new BadRequestError({ message: "Actor doesn't have membership" });
return { membershipId: membership.id };
};
const onUpdateAdditionalPrivilegesGuard: TAdditionalPrivilegesScopeFactory["onUpdateAdditionalPrivilegesGuard"] =
async (dto) => {
const scope = getScopeField(dto.scopeData);
const actorType = $getActorType(dto.selector.actorType);
const { actorType } = dto.selector;
const { permission } = await $getPermission(dto.permission, scope.value);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
@@ -145,13 +134,12 @@ export const newProjectAdditionalPrivilegesFactory = ({
(el) => el[actorType === ActorType.IDENTITY ? "actorIdentityId" : "actorUserId"] === dto.selector.actorId
);
if (!membership) throw new BadRequestError({ message: "Actor doesn't have membership" });
return { membershipId: membership.id };
};
const onDeleteAdditionalPrivilegesGuard: TAdditionalPrivilegesScopeFactory["onDeleteAdditionalPrivilegesGuard"] =
async (dto) => {
const scope = getScopeField(dto.scopeData);
const actorType = $getActorType(dto.selector.actorType);
const { actorType } = dto.selector;
const { permission } = await $getPermission(dto.permission, scope.value);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
@@ -163,14 +151,13 @@ export const newProjectAdditionalPrivilegesFactory = ({
});
if (!membership) throw new BadRequestError({ message: "Actor doesn't have membership" });
return { membershipId: membership.id };
};
const onListAdditionalPrivilegesGuard: TAdditionalPrivilegesScopeFactory["onListAdditionalPrivilegesGuard"] = async (
dto
) => {
const scope = getScopeField(dto.scopeData);
const actorType = $getActorType(dto.selector.actorType);
const { actorType } = dto.selector;
const permissionSet =
actorType === ActorType.USER
@@ -186,13 +173,12 @@ export const newProjectAdditionalPrivilegesFactory = ({
});
if (!membership) throw new BadRequestError({ message: "Actor doesn't have membership" });
return { membershipId: membership.id };
};
const onGetAdditionalPrivilegesByIdGuard: TAdditionalPrivilegesScopeFactory["onGetAdditionalPrivilegesByIdGuard"] =
async (dto) => {
const scope = getScopeField(dto.scopeData);
const actorType = $getActorType(dto.selector.actorType);
const { actorType } = dto.selector;
const permissionSet =
actorType === ActorType.USER
@@ -208,7 +194,6 @@ export const newProjectAdditionalPrivilegesFactory = ({
});
if (!membership) throw new BadRequestError({ message: "Actor doesn't have membership" });
return { membershipId: membership.id };
};
return {