RBAC Improvements
Caching improvements Maker-Checker Implementation
This commit is contained in:
@@ -142,12 +142,14 @@ export function resolveClaimActionSet(
|
||||
actions.add('add');
|
||||
actions.add('update');
|
||||
actions.add('export');
|
||||
} else if (val === 'view') {
|
||||
} else if (val === 'view' || val === 'read') {
|
||||
actions.add('view');
|
||||
} else if (val === 'add') {
|
||||
} else if (val === 'add' || val === 'edit' || val === 'write' || val === 'create') {
|
||||
// 'add' encompasses create and non-approval edits
|
||||
actions.add('view');
|
||||
actions.add('add');
|
||||
} else if (val === 'update') {
|
||||
} else if (val === 'update' || val === 'approve' || val === 'verify') {
|
||||
// 'update' strictly grants approval capability (+ view and add)
|
||||
actions.add('view');
|
||||
actions.add('add');
|
||||
actions.add('update');
|
||||
@@ -173,10 +175,13 @@ export function resolveClaimActionSet(
|
||||
actions.add('update');
|
||||
actions.add('export');
|
||||
} else if (role === 'supervisor') {
|
||||
// Supervisor performs operational review and verification/approval of milestones & evidence
|
||||
actions.add('view');
|
||||
actions.add('add');
|
||||
actions.add('update');
|
||||
actions.add('export');
|
||||
} else if (role === 'contributor') {
|
||||
// Contributor is strictly working-level execution: creating activities, submitting evidence, etc. (NO approve/update)
|
||||
actions.add('view');
|
||||
actions.add('add');
|
||||
} else {
|
||||
@@ -294,11 +299,36 @@ export function evaluateUserPermission(
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates Maker-Checker separation of duties constraint.
|
||||
* In PFM governance, the user who created (or initiated) a record ("Maker")
|
||||
* is strictly barred from approving ("Checking") their own record.
|
||||
*
|
||||
* @param createdBy The user identifier of the record creator (Guid, sub, email)
|
||||
* @param currentUserId Current logged-in user ID (user.id or sub)
|
||||
* @param currentUserSub Current logged-in user subject claim
|
||||
* @param currentUserEmail Current logged-in user email
|
||||
* @returns boolean true if user is the creator (Maker), false otherwise
|
||||
*/
|
||||
export function isRecordMaker(
|
||||
createdBy?: string | null,
|
||||
currentUserId?: string | null,
|
||||
currentUserSub?: string | null,
|
||||
currentUserEmail?: string | null
|
||||
): boolean {
|
||||
if (!createdBy) return false;
|
||||
const creator = createdBy.trim().toLowerCase();
|
||||
if (currentUserId && creator === currentUserId.trim().toLowerCase()) return true;
|
||||
if (currentUserSub && creator === currentUserSub.trim().toLowerCase()) return true;
|
||||
if (currentUserEmail && creator === currentUserEmail.trim().toLowerCase()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook exposing RBAC evaluation helpers for components.
|
||||
*/
|
||||
export function useRbac() {
|
||||
const { roles, claims } = useAuth();
|
||||
const { roles, claims, user, sub } = useAuth();
|
||||
|
||||
const permissions = useMemo(() => {
|
||||
const isUnrestricted =
|
||||
@@ -315,6 +345,22 @@ export function useRbac() {
|
||||
const canApprove = (claim: RoleClaim): boolean => canAccess(claim, 'update');
|
||||
const canExport = (claim: RoleClaim): boolean => canAccess(claim, 'export');
|
||||
|
||||
const isMaker = (createdBy?: string | null): boolean => {
|
||||
return isRecordMaker(createdBy, user?.id, sub, user?.email);
|
||||
};
|
||||
|
||||
const canApproveRecord = (claim: RoleClaim, createdBy?: string | null): boolean => {
|
||||
if (!canApprove(claim)) return false;
|
||||
// Maker-Checker policy: record creator cannot approve their own record
|
||||
return !isMaker(createdBy);
|
||||
};
|
||||
|
||||
const getApprovalRestrictionReason = (claim: RoleClaim, createdBy?: string | null): string | null => {
|
||||
if (!canApprove(claim)) return 'Requires update/approval permission';
|
||||
if (isMaker(createdBy)) return 'Maker-Checker policy: Record creators cannot approve their own records';
|
||||
return null;
|
||||
};
|
||||
|
||||
return {
|
||||
isUnrestricted,
|
||||
canAccess,
|
||||
@@ -322,8 +368,11 @@ export function useRbac() {
|
||||
canAdd,
|
||||
canApprove,
|
||||
canExport,
|
||||
isMaker,
|
||||
canApproveRecord,
|
||||
getApprovalRestrictionReason,
|
||||
};
|
||||
}, [roles, claims]);
|
||||
}, [roles, claims, user, sub]);
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user