@riao/authz-rbac
v1.0.0
Published
Riao RBAC
Downloads
8
Maintainers
Readme
@riao/authz-rbac
A Role-Based Access Control (RBAC) Authorization Driver for the Riao Identity & Access Management (IAM) system.
Overview
@riao/authz-rbac provides a complete RBAC implementation for the Riao IAM framework. RBAC is a widely-used authorization model that grants permissions to roles, which are then assigned to principals (users, applications, etc.).
RBAC Model
The RBAC authorization model follows this hierarchy:
Principal → Roles → PermissionsHow it works:
- Principals (users, applications, services) are assigned Roles
- Roles are granted Permissions
- A Permission is an action (e.g., "read", "write", "delete") optionally scoped to a resource (e.g., "documents", "users")
- Authorization is granted when a principal has an active role that contains the required permission
Key Features
- ✅ Full RBAC authorization model implementation
- ✅ Action-only and resource-specific permissions
- ✅ Role activation/deactivation
- ✅ Multiple roles per principal support
- ✅ Permission inheritance through roles
- ✅ Comprehensive test coverage
- ✅ TypeScript-first development
Installation
npm install @riao/authz-rbac @riao/iam @riao/dbal
npm install --save-dev @riao/clinpx riao migration:create import-rbac-tablesdatabase/main/migrations/123456789-import-rbac-tables.ts:
import { AuthzRbacMigrations } from '@riao/authz-rbac/authz-rbac-migrations';
export default AuthzRbacMigrations;Quick Start
1. Setup
import { Database } from '@riao/dbal';
import { RbacAuthorization } from '@riao/authz-rbac';
const db = new Database(/* ... */);
const rbac = new RbacAuthorization({ db });2. Grant Permissions
Grant a permission to a principal by creating a role and assigning it:
// Grant a single permission
await rbac.grantPermission({
principalId,
action: 'read',
resource: 'documents'
});
// Grant an action-only permission (no specific resource)
await rbac.grantPermission({
principalId,
action: 'logout'
});3. Check Authorization
Check if a principal is authorized for an action:
// Check with a specific resource
const result = await rbac.evaluate({
principal,
action: 'read',
resource: 'documents'
});
// Check without a resource (for action-only permissions)
const logoutResult = await rbac.evaluate({ principal, action: 'logout' });
// Full evaluation with metadata
const writeResult = await rbac.evaluate({
principal,
action: 'write',
resource: 'documents',
metadata: { ip: '192.168.1.1' }
});
if (writeResult.allowed) {
console.log('Access granted');
} else {
console.log('Access denied:', writeResult.reason);
}4. Revoke Permissions
Revoke a permission by deactivating the role assignment:
await rbac.revokePermission({
principalId,
action: 'read'
});Core Concepts
Roles
Roles are containers for permissions. They allow grouping related permissions and assigning them to principals in bulk.
// Interfaces
interface RbacRole {
id: DatabaseRecordId;
name: string;
description?: string;
create_timestamp: Date;
deactivate_timestamp?: Date;
}Properties:
name: Unique identifier for the role (e.g., "editor", "viewer", "admin")description: Optional description of the role's purposedeactivate_timestamp: When set, indicates the role is inactive
Permissions
Permissions represent what actions can be performed, optionally on specific resources.
interface RbacPermission {
id: DatabaseRecordId;
action: string;
resource: string | null;
description?: string;
create_timestamp: Date;
}Properties:
action: The action being performed (e.g., "read", "write", "delete")resource: Optional resource identifier (e.g., "documents", "users"). Whennull, the permission applies to the action regardless of resource
Examples:
action: "read",resource: "documents"→ Can read documentsaction: "logout",resource: null→ Can logout (no resource)
Role-Permission Assignment
Links roles to permissions. A role can have multiple permissions, and a permission can be assigned to multiple roles.
interface RbacRolePermission {
id: DatabaseRecordId;
role_id: DatabaseRecordId;
permission_id: DatabaseRecordId;
create_timestamp: Date;
}Principal-Role Assignment
Links principals to roles. A principal can have multiple roles, and roles can be deactivated without deletion.
interface RbacPrincipalRole {
id: DatabaseRecordId;
principal_id: DatabaseRecordId;
role_id: DatabaseRecordId;
create_timestamp: Date;
deactivate_timestamp?: Date;
}API Reference
RbacAuthorization Class
Constructor
new RbacAuthorization(options: RbacAuthorizationOptions)Parameters:
options.db: Database instance for RBAC operations
Methods
evaluate(context: AuthorizationContext): Promise<AuthorizationResult>
Evaluates whether a principal is authorized for an action.
const result = await rbac.evaluate({
principal: user,
action: 'read',
resource: 'documents'
});
// Returns: { allowed: true } or { allowed: false, reason: "..." }Evaluation Logic:
- Fetch all active roles for the principal
- Get all permissions assigned to those roles
- Match permissions by action and resource
- Return authorization result
Permission Matching Rules:
- If a resource is provided: Match permissions with the exact resource OR permissions with
resource: null - If no resource is provided: Only match permissions with
resource: null
evaluate(context: AuthorizationContext): Promise<AuthorizationResult>
Evaluates authorization for a principal, action, and optional resource.
const result = await rbac.evaluate({
principal: user,
action: 'delete',
resource: 'users'
});
if (result.allowed) {
// User can delete users
}grantPermission(options: GrantPermissionOptions): Promise<void>
Grants a permission to a principal by assigning a role.
Options:
principalId: The principal ID to grant permission toaction: The action name (e.g., "read", "write", "delete")resource(optional): The resource scope (e.g., "documents", "users")
Behavior:
- Creates or reuses a role for the action
- Creates or reuses a permission for the action and resource
- Links the permission to the role (if not already linked)
- Assigns the role to the principal (if not already assigned)
// Grant "read" permission on "documents"
await rbac.grantPermission({
principalId,
action: 'read',
resource: 'documents'
});
// Grant action-only "logout" permission
await rbac.grantPermission({
principalId,
action: 'logout'
});Note: Idempotent - safe to call multiple times
revokePermission(options: RevokePermissionOptions): Promise<void>
Revokes a permission from a principal by deactivating the role assignment.
Options:
principalId: The principal ID to revoke permission fromaction: The action name to revoke
await rbac.revokePermission({
principalId,
action: 'delete'
});Behavior:
- Finds the role for the action
- Sets
deactivate_timestampon the principal-role assignment - Does not delete data; allows for audit trails and reactivation
Note: Deactivation is soft-delete; the principal can be re-granted the permission later
Repository Access
The RBAC instance exposes public repositories for direct database access:
const rbac = new RbacAuthorization({ db });
// Access repositories directly if needed
rbac.rolesRepo // QueryRepository<RbacRole>
rbac.permissionsRepo // QueryRepository<RbacPermission>
rbac.rolePermissionsRepo // QueryRepository<RbacRolePermission>
rbac.principalRolesRepo // QueryRepository<RbacPrincipalRole>Usage Examples
Basic Permission Check
const rbac = new RbacAuthorization({ db });
// Check if user can read documents
const result = await rbac.evaluate({
principal: user,
action: 'read',
resource: 'documents'
});
if (result.allowed) {
// Allow access
}Multiple Roles
A principal can have multiple roles with different permissions:
// Principal has both "editor" and "reviewer" roles
await rbac.grantPermission({
principalId: userId,
action: 'write',
resource: 'articles'
}); // editor
await rbac.grantPermission({
principalId: userId,
action: 'approve'
}); // reviewer
// Has access through either role
const canWrite = await rbac.evaluate({
principal: user,
action: 'write',
resource: 'articles'
}); // true
const canApprove = await rbac.evaluate({
principal: user,
action: 'approve'
}); // trueHierarchical Permissions
Implement permission hierarchies using naming conventions:
// Define related permissions
await rbac.grantPermission({
principalId: userId,
action: 'read',
resource: 'documents'
});
await rbac.grantPermission({
principalId: userId,
action: 'read',
resource: 'reports'
});
await rbac.grantPermission({
principalId: userId,
action: 'read',
resource: 'analytics'
});
// Check specific permissions
const canReadDocs = await rbac.evaluate({ principal: user, action: 'read', resource: 'documents' }); // trueTemporary Access
Grant access temporarily and revoke it later:
// Grant temporary access
await rbac.grantPermission({
principalId: userId,
action: 'export',
resource: 'data'
});
// Later, revoke the access
await rbac.revokePermission({
principalId: userId,
action: 'export'
});Action-Only Permissions
For permissions that don't require a resource scope:
// Grant action-only permission
await rbac.grantPermission({ principalId: userId, action: 'logout' });
await rbac.grantPermission({ principalId: userId, action: 'change-password' });
await rbac.grantPermission({ principalId: userId, action: 'view-profile' });
// Check authorization
const canLogout = await rbac.evaluate({
principal: user,
action: 'logout'
}); // true
const canLogoutWithResource = await rbac.evaluate({
principal: user,
action: 'logout',
resource: 'resource'
}); // true (null resource matches any)Contributing & Development
See contributing.md for information on how to develop or contribute to this project!
License
See LICENSE.md
