152 lines
4.6 KiB
TypeScript
152 lines
4.6 KiB
TypeScript
import { and, eq, isNull } from 'drizzle-orm';
|
|
import { crmRoleProfiles, memberships } from '@/db/schema';
|
|
import { db } from '@/lib/db';
|
|
import {
|
|
ALL_PERMISSIONS,
|
|
type CrmApprovalAuthority,
|
|
type CrmOwnershipScope,
|
|
type CrmScopeMode,
|
|
getDefaultBusinessRole,
|
|
getRoleProfileDefinition,
|
|
isApprovalAuthority,
|
|
isMembershipRole,
|
|
isOwnershipScope,
|
|
isScopeMode,
|
|
resolveEffectivePermissions,
|
|
type MembershipRole,
|
|
type Permission,
|
|
type SystemRole
|
|
} from './rbac';
|
|
|
|
export interface ResolvedCrmAccess {
|
|
membershipRole: MembershipRole;
|
|
businessRole: string;
|
|
permissions: Permission[];
|
|
branchScopeIds: string[];
|
|
productTypeScopeIds: string[];
|
|
ownershipScope: CrmOwnershipScope;
|
|
branchScopeMode: CrmScopeMode;
|
|
productScopeMode: CrmScopeMode;
|
|
approvalAuthority: CrmApprovalAuthority;
|
|
roleProfileName: string | null;
|
|
}
|
|
|
|
type BranchScopedAccess = Pick<
|
|
ResolvedCrmAccess,
|
|
'branchScopeMode' | 'branchScopeIds'
|
|
> | {
|
|
branchScopeMode: string;
|
|
branchScopeIds: string[];
|
|
};
|
|
|
|
type ProductScopedAccess = Pick<
|
|
ResolvedCrmAccess,
|
|
'productScopeMode' | 'productTypeScopeIds'
|
|
> | {
|
|
productScopeMode: string;
|
|
productTypeScopeIds: string[];
|
|
};
|
|
|
|
function normalizeStringArray(value: unknown) {
|
|
return Array.isArray(value)
|
|
? [...new Set(value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0))]
|
|
: [];
|
|
}
|
|
|
|
export async function resolveCrmMembershipAccess(input: {
|
|
systemRole: SystemRole;
|
|
organizationId: string;
|
|
membership: typeof memberships.$inferSelect;
|
|
}): Promise<ResolvedCrmAccess> {
|
|
if (input.systemRole === 'super_admin') {
|
|
return {
|
|
membershipRole: 'admin',
|
|
businessRole: input.membership.businessRole,
|
|
permissions: ALL_PERMISSIONS,
|
|
branchScopeIds: [],
|
|
productTypeScopeIds: [],
|
|
ownershipScope: 'organization',
|
|
branchScopeMode: 'all',
|
|
productScopeMode: 'all',
|
|
approvalAuthority: 'final',
|
|
roleProfileName: 'System Admin'
|
|
};
|
|
}
|
|
|
|
const membershipRole = isMembershipRole(input.membership.role)
|
|
? input.membership.role
|
|
: 'user';
|
|
const businessRole = input.membership.businessRole || getDefaultBusinessRole(membershipRole);
|
|
const [roleProfile] = await db
|
|
.select()
|
|
.from(crmRoleProfiles)
|
|
.where(
|
|
and(
|
|
eq(crmRoleProfiles.organizationId, input.organizationId),
|
|
eq(crmRoleProfiles.code, businessRole),
|
|
eq(crmRoleProfiles.isActive, true),
|
|
isNull(crmRoleProfiles.deletedAt)
|
|
)
|
|
)
|
|
.limit(1);
|
|
const defaultDefinition = getRoleProfileDefinition(businessRole);
|
|
|
|
const ownershipScope: CrmOwnershipScope = isOwnershipScope(roleProfile?.ownershipScope ?? '')
|
|
? (roleProfile.ownershipScope as CrmOwnershipScope)
|
|
: (defaultDefinition?.ownershipScope ?? 'own');
|
|
const branchScopeMode: CrmScopeMode = isScopeMode(roleProfile?.branchScopeMode ?? '')
|
|
? (roleProfile.branchScopeMode as CrmScopeMode)
|
|
: (defaultDefinition?.branchScopeMode ?? 'assigned');
|
|
const productScopeMode: CrmScopeMode = isScopeMode(roleProfile?.productScopeMode ?? '')
|
|
? (roleProfile.productScopeMode as CrmScopeMode)
|
|
: (defaultDefinition?.productScopeMode ?? 'assigned');
|
|
const approvalAuthority: CrmApprovalAuthority = isApprovalAuthority(
|
|
roleProfile?.approvalAuthority ?? ''
|
|
)
|
|
? (roleProfile.approvalAuthority as CrmApprovalAuthority)
|
|
: (defaultDefinition?.approvalAuthority ?? 'none');
|
|
|
|
return {
|
|
membershipRole,
|
|
businessRole,
|
|
permissions: resolveEffectivePermissions({
|
|
systemRole: input.systemRole,
|
|
membershipRole,
|
|
businessRole,
|
|
rolePermissions: roleProfile?.permissions ?? defaultDefinition?.permissions ?? [],
|
|
directPermissions: input.membership.permissions
|
|
}),
|
|
branchScopeIds: normalizeStringArray(input.membership.branchScopeIds),
|
|
productTypeScopeIds: normalizeStringArray(input.membership.productTypeScopeIds),
|
|
ownershipScope,
|
|
branchScopeMode,
|
|
productScopeMode,
|
|
approvalAuthority,
|
|
roleProfileName: roleProfile?.name ?? defaultDefinition?.name ?? null
|
|
};
|
|
}
|
|
|
|
export function hasBranchScopeAccess(access: BranchScopedAccess, branchId?: string | null) {
|
|
if (!branchId) {
|
|
return true;
|
|
}
|
|
|
|
if (access.branchScopeMode === 'all' || access.branchScopeIds.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
return access.branchScopeIds.includes(branchId);
|
|
}
|
|
|
|
export function hasProductScopeAccess(access: ProductScopedAccess, productTypeId?: string | null) {
|
|
if (!productTypeId) {
|
|
return true;
|
|
}
|
|
|
|
if (access.productScopeMode === 'all' || access.productTypeScopeIds.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
return access.productTypeScopeIds.includes(productTypeId);
|
|
}
|