159 lines
5.3 KiB
TypeScript
159 lines
5.3 KiB
TypeScript
import { RowAccess, RowAccessTypes } from "~/application/enums/entities/RowAccess";
|
|
import {
|
|
createRowPermission,
|
|
deleteRowPermissionByIdForRow,
|
|
getRowPermissions,
|
|
updateRowPermissionForRow,
|
|
} from "../../db/permissions/rowPermissions.db.server";
|
|
import { DefaultVisibility } from "~/application/dtos/shared/DefaultVisibility";
|
|
import { Row } from "@toolctrl/db/prisma-browser";
|
|
import { getGroup } from "~/utils/db/permissions/groups.db.server";
|
|
import { getRole } from "~/utils/db/permissions/roles.db.server";
|
|
import { getTenant, getTenantUserByIds } from "~/utils/db/tenants.db.server";
|
|
|
|
export namespace RowPermissionsApi {
|
|
type ShareData = { type: "tenant" | "user" | "role" | "group" | "public"; id?: string; access: RowAccess };
|
|
type ShareScope = { currentTenantId?: string | null };
|
|
|
|
export async function shareWithDefault(row: Row, defaultVisibility: DefaultVisibility) {
|
|
if (defaultVisibility === "tenant" && row.tenantId) {
|
|
await shareWithTenant(row, row.tenantId, "edit");
|
|
}
|
|
if (defaultVisibility === "public") {
|
|
return await shareWithPublic(row, "view");
|
|
}
|
|
}
|
|
export async function share(row: Row, data: ShareData, scope: ShareScope = {}) {
|
|
await validateShareTarget(data, scope);
|
|
const permission = {
|
|
tenantId: data.type === "tenant" ? data.id : null,
|
|
roleId: data.type === "role" ? data.id : null,
|
|
groupId: data.type === "group" ? data.id : null,
|
|
userId: data.type === "user" ? data.id : null,
|
|
public: data.type === "public",
|
|
access: data.access,
|
|
};
|
|
if (data.type !== "public" && !permission.tenantId && !permission.roleId && !permission.groupId && !permission.userId) {
|
|
throw Error("Invalid permission");
|
|
}
|
|
const permissions = await getRowPermissions(row.id);
|
|
const existing = permissions.find(
|
|
(f) =>
|
|
f.tenantId === permission.tenantId &&
|
|
f.roleId === permission.roleId &&
|
|
f.groupId === permission.groupId &&
|
|
f.userId === permission.userId &&
|
|
f.public === permission.public
|
|
);
|
|
if (data.type === "user" && data.id === row?.createdByUserId) {
|
|
return;
|
|
} else if (existing) {
|
|
return existing;
|
|
}
|
|
const create = await createRowPermission({
|
|
rowId: row.id,
|
|
...permission,
|
|
});
|
|
|
|
return create;
|
|
}
|
|
export async function validateShareTarget(data: ShareData, scope: ShareScope = {}) {
|
|
validateAccess(data.access);
|
|
if (data.type !== "tenant" && data.type !== "user" && data.type !== "role" && data.type !== "group" && data.type !== "public") {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
if (data.type !== "public" && data.id === "public") {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
if (data.type === "public" && data.id) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
if (data.type === "public") {
|
|
return;
|
|
}
|
|
if (!data.id) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
const { currentTenantId } = scope;
|
|
if (data.type === "tenant") {
|
|
if (currentTenantId && data.id !== currentTenantId) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
if (currentTenantId === null && !(await getTenant(data.id))) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
return;
|
|
}
|
|
if (data.type === "user") {
|
|
if (currentTenantId && !(await getTenantUserByIds(currentTenantId, data.id))) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
return;
|
|
}
|
|
if (data.type === "group") {
|
|
if (!(await getGroup(data.id, currentTenantId))) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
return;
|
|
}
|
|
const role = await getRole(data.id);
|
|
if (!role || (currentTenantId && role.type !== "app")) {
|
|
throw Error("Invalid permission target");
|
|
}
|
|
}
|
|
export function validateAccess(access: RowAccess) {
|
|
if (!RowAccessTypes.includes(access)) {
|
|
throw Error("Invalid permission access");
|
|
}
|
|
}
|
|
export async function shareWithTenant(row: Row, tenantId: string, access: RowAccess) {
|
|
return await share(row, {
|
|
type: "tenant",
|
|
id: tenantId,
|
|
access,
|
|
}, { currentTenantId: tenantId });
|
|
}
|
|
export async function shareWithUser(row: Row, userId: string, access: RowAccess) {
|
|
return await share(row, {
|
|
type: "user",
|
|
id: userId,
|
|
access,
|
|
});
|
|
}
|
|
export async function shareWithRole(row: Row, roleId: string, access: RowAccess) {
|
|
return await share(row, {
|
|
type: "role",
|
|
id: roleId,
|
|
access,
|
|
});
|
|
}
|
|
export async function shareWithGroup(row: Row, groupId: string, access: RowAccess) {
|
|
return await share(row, {
|
|
type: "group",
|
|
id: groupId,
|
|
access,
|
|
});
|
|
}
|
|
export async function shareWithPublic(row: Row, access: RowAccess) {
|
|
return await share(row, {
|
|
type: "public",
|
|
access,
|
|
});
|
|
}
|
|
export async function setAccessForRow(rowId: string, id: string, access: RowAccess) {
|
|
validateAccess(access);
|
|
const updated = await updateRowPermissionForRow(rowId, id, { access });
|
|
if (updated.count !== 1) {
|
|
throw Error("Permission not found");
|
|
}
|
|
return updated;
|
|
}
|
|
export async function delForRow(rowId: string, id: string) {
|
|
const deleted = await deleteRowPermissionByIdForRow(rowId, id);
|
|
if (deleted.count !== 1) {
|
|
throw Error("Permission not found");
|
|
}
|
|
return deleted;
|
|
}
|
|
}
|