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 {
@@ -86,18 +86,20 @@ export const convertorServiceFactory = ({
return { membershipId: membership.id, membership };
};
const additionalPrivilegeIdToMembershipId = async (privilegeId: string, scope: AccessScope, orgId: string) => {
const membership = await additionalPrivilegeDAL.getMembershipById(privilegeId);
if (!membership || membership.scope !== scope || membership.scopeOrgId !== orgId) {
const additionalPrivilegeIdToDoc = async (privilegeId: string) => {
const doc = await additionalPrivilegeDAL.findOne({
id: privilegeId
});
if (!doc) {
throw new NotFoundError({ message: `Privilege with id ${privilegeId} not found` });
}
return { membership, membershipId: membership.id };
return { privilege: doc };
};
const additionalPrivilegeNameToId = async (privilegeName: string, membershipId: string) => {
const additionalPrivilegeNameToDoc = async (privilegeName: string, projectId: string) => {
const privilege = await additionalPrivilegeDAL.findOne({
name: privilegeName,
membershipId
projectId
});
if (!privilege) {
throw new NotFoundError({ message: `Privilege with slug ${privilegeName} not found` });
@@ -111,8 +113,8 @@ export const convertorServiceFactory = ({
userMembershipIdToUserId,
groupMembershipIdToGroupId,
identityMembershipIdToIdentityId,
additionalPrivilegeIdToMembershipId,
additionalPrivilegeNameToId,
additionalPrivilegeIdToDoc,
additionalPrivilegeNameToDoc,
identityIdToMembershipId
};
};
@@ -123,7 +123,8 @@ export const orgAdminServiceFactory = ({
{
scopeProjectId: projectId,
actorUserId: actorId,
scope: AccessScope.Project
scope: AccessScope.Project,
scopeOrgId: actorOrgId
},
tx
);
+1 -1
View File
@@ -305,7 +305,7 @@ export const orgDALFactory = (db: TDbClient) => {
.select(
db.ref("id").withSchema(TableName.Membership),
db.ref("inviteEmail").withSchema(TableName.Membership),
db.ref("actorOrgId").withSchema(TableName.Membership).as("orgId"),
db.ref("scopeOrgId").withSchema(TableName.Membership).as("orgId"),
db.ref("role").withSchema(TableName.MembershipRole),
db.ref("customRoleId").withSchema(TableName.MembershipRole).as("roleId"),
db.ref("status").withSchema(TableName.Membership),
+2 -2
View File
@@ -2,7 +2,7 @@ import { AccessScope, ActionProjectType, TableName } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
import { UnpackedPermissionSchema, unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
import { newNamespaceRoleFactory } from "./namespace/namespace-role-factory";
import { newOrgRoleFactory } from "./org/org-role-factory";
@@ -188,7 +188,7 @@ export const roleServiceFactory = ({
const predefinedRole = await factory.getPredefinedRoles(scopeData);
const selectedRole = predefinedRole.find((el) => el.slug === dto.selector.slug);
if (!selectedRole) throw new BadRequestError({ message: `Role with slug ${dto.selector.slug} not found` });
return selectedRole;
return { ...selectedRole, permissions: UnpackedPermissionSchema.array().parse(selectedRole.permissions) };
}
const role = await roleDAL.findOne({
@@ -946,12 +946,13 @@ export const superAdminServiceFactory = ({
}
}
const membershipRole = await membershipRoleDAL.findOne({ membershipId });
const [organizationMembership] = await membershipUserDAL.delete({
scopeOrgId: organizationId,
scope: AccessScope.Organization,
id: membershipId
});
return organizationMembership;
return { ...organizationMembership, role: membershipRole.role, orgId: organizationId };
};
const joinOrganization = async (orgId: string, actor: OrgServiceActor) => {
@@ -988,14 +989,14 @@ export const superAdminServiceFactory = ({
},
tx
);
await membershipRoleDAL.create(
const membershipRole = await membershipRoleDAL.create(
{
membershipId: membership.id,
role: OrgMembershipRole.Admin
},
tx
);
return membership;
return { ...membership, role: membershipRole.role, orgId: org.id };
});
return orgMembership;
@@ -1052,7 +1053,7 @@ export const superAdminServiceFactory = ({
}
});
return orgMembership;
return { ...orgMembership, orgId: organizationId, role: "" };
};
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {