@iamjs/core
v2.0.15
Published
This package contains the core logic and interfaces for the authorization library with End-to-end typesafety
Downloads
86
Maintainers
Readme
@iamjs/core
Overview
iamjs is a powerful and flexible library for implementing role-based access control (RBAC) in JavaScript applications. The core package (@iamjs/core
) provides essential functionality for managing roles, permissions, and authorization in your application. With iamjs, you can easily define complex permission structures, manage user roles, and enforce access control throughout your application.
Table of Contents
Installation
You can install the iamjs core library using your preferred package manager:
npm install @iamjs/core
# or
yarn add @iamjs/core
# or
pnpm add @iamjs/core
# or
bun add @iamjs/core
Key Concepts
- Role: A set of permissions that can be assigned to users. Roles define what actions a user can perform on various resources.
- Resource: An entity in your application that actions can be performed on (e.g., 'user', 'post', 'comment').
- Action: An operation that can be performed on a resource (e.g., 'create', 'read', 'update', 'delete', 'list').
- Permission: A combination of a resource and allowed actions. Permissions can be base permissions (CRUD operations) or custom permissions specific to your application.
- Schema: A collection of roles that defines the overall authorization structure of your application.
Usage
Creating Roles
Create new roles using the Role
class. You can define base permissions using CRUD notation and add custom permissions as needed.
import { Role } from '@iamjs/core';
// Create a basic user role
const userRole = new Role({
name: 'user',
description: 'Standard user with limited permissions',
config: {
user: {
base: '-r--l', // Can only read and list users
custom: {
updateOwnProfile: true
}
},
post: {
base: 'crud-', // Can create, read, update, but not delete posts
custom: {
publish: true,
unpublish: true
}
},
comment: {
base: 'crudl', // Full access to comments
custom: {
report: true
}
}
}
});
// Create an admin role with full permissions
const adminRole = new Role({
name: 'admin',
description: 'Administrator with full access',
config: {
user: {
base: 'crudl',
custom: {
ban: true,
unban: true
}
},
post: {
base: 'crudl',
custom: {
publish: true,
unpublish: true,
feature: true
}
},
comment: {
base: 'crudl',
custom: {
delete: true,
restore: true
}
}
}
});
In these examples, we've created two roles: a user
role with limited permissions and an admin
role with full access. The base permissions use CRUD notation (Create, Read, Update, Delete, List), where a dash (-) indicates the absence of that permission.
Managing Permissions
You can extend or update role permissions using the add
and update
methods:
// Add a new resource to the user role
const extendedUserRole = userRole.add({
resource: 'category',
permissions: {
base: '-r--l', // Users can only read and list categories
custom: {
subscribe: true,
unsubscribe: true
}
}
});
// Update existing permissions for the admin role
const updatedAdminRole = adminRole.update({
resource: 'user',
permissions: {
base: 'crudl',
custom: {
ban: true,
unban: true,
promoteToModerator: true // New custom permission
}
}
});
// Remove a resource from a role
const reducedUserRole = userRole.remove({
resource: 'comment'
});
These methods allow you to dynamically adjust permissions as your application evolves.
Checking Permissions
The Role
class provides several methods to check permissions:
// Check if a role can perform a specific action on a resource
console.log(userRole.can('post', 'create')); // true
console.log(userRole.can('user', 'delete')); // false
// Check if a role cannot perform a specific action
console.log(userRole.cannot('user', 'ban')); // true
// Check if a role can perform any of the specified actions
console.log(userRole.canAny('post', ['create', 'delete'])); // true
// Check if a role can perform all of the specified actions
console.log(userRole.canAll('post', ['create', 'read', 'update', 'delete'])); // false
These methods make it easy to implement fine-grained access control in your application logic.
Authorization Management
Use the AuthManager
class for centralized authorization management:
import { AuthManager, Schema } from '@iamjs/core';
// Create a schema with multiple roles
const schema = new Schema({
roles: {
user: userRole,
admin: adminRole,
moderator: moderatorRole // Assuming you've defined this role
}
});
// Initialize the AuthManager with the schema
const auth = new AuthManager(schema);
// Check authorization for different scenarios
console.log(auth.authorize({
role: 'user',
actions: ['read', 'create'],
resources: 'post'
})); // true
console.log(auth.authorize({
role: 'user',
actions: ['delete'],
resources: 'post'
})); // false
console.log(auth.authorize({
role: 'admin',
actions: ['ban'],
resources: 'user'
})); // true
// Check multiple resources at once
console.log(auth.authorize({
role: 'moderator',
actions: ['update', 'delete'],
resources: ['post', 'comment'],
strict: true // Requires permission for all specified resources
})); // Depends on moderator role definition
The AuthManager
provides a convenient way to check permissions across different roles and resources in your application.
API Reference
Role Class
constructor(options: RoleOptions)
add(options: AddOptions): Role
update(options: UpdateOptions): Role
remove(options: RemoveOptions): Role
can(resource: string, action: string): boolean
cannot(resource: string, action: string): boolean
canAny(resource: string, actions: string[]): boolean
canAll(resource: string, actions: string[]): boolean
toObject(): object
toJSON(): string
static fromObject(obj: object): Role
static fromJSON(json: string): Role
static from(data: any, transform: Function): Role
AuthManager Class
constructor(schema: Schema)
authorize(request: AuthorizationRequest): boolean
Schema Class
constructor(options: SchemaOptions)
getRole(name: string): Role | undefined
hasRole(name: string): boolean
addRole(role: Role): void
removeRole(name: string): void
Advanced Usage
Serialization and Deserialization
Convert roles to and from different formats for storage or transmission:
// Convert a role to JSON
const jsonString = userRole.toJSON();
console.log(jsonString);
// {"name":"user","description":"Standard user with limited permissions","config":{"user":{"base":"-r--l","custom":{"updateOwnProfile":true}},...}}
// Create a role from JSON
const recreatedUserRole = Role.fromJSON(jsonString);
// Convert a role to a plain object
const objectRepresentation = adminRole.toObject();
console.log(objectRepresentation);
// {name: "admin", description: "Administrator with full access", config: {...}}
// Create a role from an object
const recreatedAdminRole = Role.fromObject(objectRepresentation);
Custom Data Transformations
Use the from
method for creating roles from custom data sources, such as encrypted data:
import crypto from 'crypto';
import { Role, GetRoleConfig } from '@iamjs/core';
// Assume we have these encryption functions defined
const encrypt = (data: string) => { /* ... */ };
const decrypt = (data: string) => { /* ... */ };
// Encrypt role data for storage
const encryptedRoleData = encrypt(userRole.toJSON());
// Later, recreate the role from encrypted data
const decryptedRole = Role.from(encryptedRoleData, (data) => {
const decrypted = decrypt(data);
return Role.fromJSON(decrypted).toObject() as GetRoleConfig<typeof userRole>;
});
console.log(decryptedRole.can('post', 'create')); // true
This approach allows you to securely store and transmit role data while maintaining the ability to recreate functional Role
instances.
Best Practices
- Granular Permissions: Define permissions at a granular level to allow for fine-tuned access control.
- Hierarchical Roles: Consider implementing role hierarchies (e.g., admin > moderator > user) to simplify permission management.
- Regular Audits: Periodically review and audit your role definitions to ensure they align with your application's security requirements.
- Dynamic Role Assignment: Implement systems to dynamically assign and revoke roles based on user actions or system events.
- Principle of Least Privilege: Always start with the minimum necessary permissions and add more as needed.
- Consistent Naming: Use consistent naming conventions for resources and actions across your application.
- Documentation: Keep your role definitions and permission structures well-documented for easier maintenance and onboarding.
Contributing
We welcome contributions to iamjs! If you'd like to contribute, please follow these steps:
- Fork the repository
- Create a new branch for your feature or bug fix
- Make your changes and write tests if applicable
- Submit a pull request with a clear description of your changes
Please see our Contributing Guide for more detailed information.
License
iamjs is released under the MIT License. See the LICENSE file for more details.