feat(server): completed identity additional privilege

This commit is contained in:
Akhil Mohan
2024-03-28 19:55:12 +05:30
parent 81cf19cb4a
commit 8529fac098
14 changed files with 653 additions and 2 deletions
@@ -0,0 +1,12 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TIdentityProjectAdditionalPrivilegeDALFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeDALFactory
>;
export const identityProjectAdditionalPrivilegeDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.IdentityProjectAdditionalPrivilege);
return orm;
};
@@ -0,0 +1,259 @@
import { ForbiddenError } from "@casl/ability";
import ms from "ms";
import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
import { ActorType } from "@app/services/auth/auth-type";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
import {
IdentityProjectAdditionalPrivilegeTemporaryMode,
TCreateIdentityPrivilegeDTO,
TDeleteIdentityPrivilegeDTO,
TGetIdentityPrivilegeDetailsDTO,
TListIdentityPrivilegesDTO,
TUpdateIdentityPrivilegeDTO
} from "./identity-project-additional-privilege-types";
type TIdentityProjectAdditionalPrivilegeServiceFactoryDep = {
identityProjectAdditionalPrivilegeDAL: TIdentityProjectAdditionalPrivilegeDALFactory;
identityProjectDAL: Pick<TIdentityProjectDALFactory, "findOne" | "findById">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeServiceFactory
>;
export const identityProjectAdditionalPrivilegeServiceFactory = ({
identityProjectAdditionalPrivilegeDAL,
identityProjectDAL,
permissionService
}: TIdentityProjectAdditionalPrivilegeServiceFactoryDep) => {
const create = async ({
name,
slug,
actor,
actorId,
projectId,
identityId,
permissions: customPermission,
actorOrgId,
description,
...dto
}: TCreateIdentityPrivilegeDTO) => {
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
if (!identityProjectMembership)
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityId,
identityProjectMembership.projectId,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
slug,
projectMembershipId: identityProjectMembership.id
});
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
if (!dto.isTemporary) {
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id,
slug,
permissions: customPermission,
name,
description
});
return additionalPrivilege;
}
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
projectMembershipId: identityProjectMembership.id,
slug,
permissions: customPermission,
name,
description,
isTemporary: true,
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: dto.temporaryRange,
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
});
return additionalPrivilege;
};
const updateById = async ({ privilegeId, actorOrgId, actor, actorId, ...dto }: TUpdateIdentityPrivilegeDTO) => {
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId);
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
const identityProjectMembership = await identityProjectDAL.findById(identityPrivilege.projectMembershipId);
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityProjectMembership.identityId,
identityProjectMembership.projectId,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
if (dto?.slug) {
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
slug: dto.slug,
projectMembershipId: identityProjectMembership.id
});
if (existingSlug && existingSlug.id !== identityPrivilege.id)
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
}
const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : identityPrivilege.isTemporary;
if (isTemporary) {
const temporaryAccessStartTime = dto?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
const temporaryRange = dto?.temporaryRange || identityPrivilege?.temporaryRange;
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
...dto,
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
});
return additionalPrivilege;
}
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
...dto,
isTemporary: false,
temporaryAccessStartTime: null,
temporaryAccessEndTime: null,
temporaryRange: null,
temporaryMode: null
});
return additionalPrivilege;
};
const deleteById = async ({ actorId, actor, actorOrgId, privilegeId }: TDeleteIdentityPrivilegeDTO) => {
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId);
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
const identityProjectMembership = await identityProjectDAL.findById(identityPrivilege.projectMembershipId);
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityProjectMembership.identityId,
identityProjectMembership.projectId,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
return deletedPrivilege;
};
const getPrivilegeDetailsById = async ({
privilegeId,
actorOrgId,
actor,
actorId
}: TGetIdentityPrivilegeDetailsDTO) => {
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId);
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
const identityProjectMembership = await identityProjectDAL.findById(identityPrivilege.projectMembershipId);
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityProjectMembership.identityId,
identityProjectMembership.projectId,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
return identityPrivilege;
};
const listIdentityProjectPrivileges = async ({
projectId,
identityId,
actorOrgId,
actor,
actorId
}: TListIdentityPrivilegesDTO) => {
const identityProjectMembership = await identityProjectDAL.findOne({ projectId, identityId });
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
identityProjectMembership.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
ActorType.IDENTITY,
identityProjectMembership.identityId,
identityProjectMembership.projectId,
actorOrgId
);
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
if (!hasRequiredPriviledges)
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
projectMembershipId: identityProjectMembership.id
});
if (!identityPrivileges) throw new BadRequestError({ message: "Identity additional privilege not found" });
return identityPrivileges;
};
return {
create,
updateById,
deleteById,
getPrivilegeDetailsById,
listIdentityProjectPrivileges
};
};
@@ -0,0 +1,51 @@
import { TProjectPermission } from "@app/lib/types";
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
Relative = "relative"
}
export type TCreateIdentityPrivilegeDTO = (
| {
permissions: unknown;
identityId: string;
projectId: string;
name: string;
slug: string;
description?: string;
isTemporary: false;
}
| {
permissions: unknown;
identityId: string;
projectId: string;
name: string;
slug: string;
description?: string;
isTemporary: true;
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
temporaryRange: string;
temporaryAccessStartTime: string;
}
) &
Omit<TProjectPermission, "projectId">;
export type TUpdateIdentityPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> &
Partial<{
permissions: unknown;
name: string;
slug: string;
description?: string;
isTemporary: boolean;
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
temporaryRange: string;
temporaryAccessStartTime: string;
}>;
export type TDeleteIdentityPrivilegeDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
export type TGetIdentityPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
export type TListIdentityPrivilegesDTO = Omit<TProjectPermission, "projectId"> & {
identityId: string;
projectId: string;
};