Skip to main content

permissions-handling

RBAC (handling permissions)

RBAC implementation

Concept: Instead of checking roles inside components, you check permissions. You define a central configuration that maps Roles to Permissions (e.g., "delete:comments").

The Benefit: Your components became cleaner. They just ask "Can I delete comments?". They don't care who the user is.

The Limitation: It struggles with ownership. A standard RBAC system says "Users can delete comments," but it can't easily say "Users can delete their own comments." This limitation arises because a RBAC system doesn't take into account the resources a user owns.

// ✅ permissions.ts
const ROLES = {
admin: ["view:comments", "create:comments", "delete:comments"],
moderator: ["view:comments", "create:comments", "delete:comments"],
user: ["view:comments", "create:comments"],
} as const;

export function hasPermission(user, permission) {
return ROLES[user.role].includes(permission);
}

// Usage in Component
// Clean, but lacks "ownership" checks
const canDelete = hasPermission(user, "delete:comments");

Attribute-Based Access Control (ABAC)

Concept: To handle complex rules (e.g., "You can delete a comment IF you own it OR if you are an admin"), you need a system that looks at the User, the Action, and the Resource (the specific data object).

// ✅ permissions.ts
type User = { id: string; role: 'admin' | 'user' };
type Comment = { authorId: string; blockedBy?: string[] };

const PERMISSIONS = {
comments: {
// Admin can do anything; Users can only view if not blocked
view: (user: User, comment: Comment) => {
return user.role === 'admin' || !comment.blockedBy?.includes(user.id);
},

// Admin can delete anything; Users can delete ONLY if they are the author
delete: (user: User, comment: Comment) => {
if (user.role === 'admin') return true;
return user.id === comment.authorId;
}
},
todos: {
// defined similar rules for other resources...
}
};

// ✅ lib/auth.ts
export function hasPermission(user, resource, action, data?) {
const resourcePermissions = PERMISSIONS[resource];
if (!resourcePermissions) return false;

const permissionChecker = resourcePermissions[action];
if (!permissionChecker) return false;

return permissionChecker(user, data);
}