Rules and Conditions
In this section, we will explore how to define rules and use conditions in Asgardian to manage user permissions effectively.
Defining Basic Rules
Rules in Asgardian are defined using the can
and cannot
methods. These methods allow you to specify actions, resources, and optional conditions.
Basic can
Rule
The can
method is used to define permissions that a user can perform.
import { createAbility } from '@nordic-ui/asgardian';
const ability = createAbility();
// Allow reading posts
ability.can('read', 'Post');
Basic cannot
Rule
The cannot
method is used to define permissions that a user cannot perform.
// Disallow deleting posts
ability.cannot('delete', 'Post');
Multiple Actions
You can define multiple actions in a single rule using an array.
// Allow creating, updating, and deleting posts
ability.can(['create', 'update', 'delete'], 'Post');
All Actions
To define all actions in a single rule, use the 'manage'
keyword.
// Allow managing all actions
ability.can('manage', 'Post');
Multiple Resources
You can define multiple resources in a single rule using an array.
// Allow creating posts and comments
ability.can('create', ['Post', 'Comment']);
All Resources
To define a rule that applies to all resources, use the 'all'
keyword.
// Allow managing all resources
ability.can('manage', 'all');
Using Conditions
Conditions allow you to specify more granular rules based on specific criteria. You can use objects to define conditions. Asgardian will check the resource object against the condition object.
// Allow reading only published posts
ability.can('read', 'Post', { published: true });
const publishedPost = { published: true };
const draftPost = { published: false };
console.log(ability.isAllowed('read', 'Post', publishedPost)); // true
console.log(ability.isAllowed('read', 'Post', draftPost)); // false
Combining Rules
You can combine multiple rules to create complex permission structures.
Combining can
and cannot
You can mix can
and cannot
rules to define specific permissions.
// Allow reading posts and disallow deleting posts
ability
.can('read', 'Post')
.cannot('delete', 'Post');
const canReadPost = ability.isAllowed('read', 'Post');
console.log(canReadPost); // true
const canDeletePost = ability.isAllowed('delete', 'Post');
console.log(canDeletePost); // false
Advanced Conditions
For advanced use cases, you can create more sophisticated permission rules by combining multiple conditions and rules.
Multiple Conditions
You can specify multiple conditions that must all be met:
const ability = createAbility()
// Allow managing posts only if both conditions are met
ability.can('manage', 'Post', {
authorId: 123,
published: true
})
// All conditions must match
ability.isAllowed('manage', 'Post', { authorId: 123, published: true }) // true
ability.isAllowed('manage', 'Post', { authorId: 123, published: false }) // false
ability.isAllowed('manage', 'Post', { authorId: 456, published: true }) // false
Combining Rules
You can create complex permission structures by chaining multiple rules:
const ability = createAbility()
.can('read', 'Post')
.can(['read', 'create'], 'Comment')
.cannot('update', 'Comment')
// Check permissions
ability.isAllowed('read', 'Post') // true
ability.isAllowed('delete', 'Post') // false
ability.isAllowed('read', 'Comment') // true
ability.isAllowed('create', 'Comment') // true
ability.isAllowed('update', 'Comment') // false
Rule Precedence
Rules are evaluated in order, with later rules taking precedence over earlier ones. It’s recommended to follow the principle of least privilege by:
- Starting with restrictive permissions
- Adding specific allowances where needed
- Using
cannot
rules as safety guards
const ability = createAbility()
// ❌ Bad practice: Starting with broad permissions
ability
.can('manage', 'all')
.cannot('delete', 'Comment') // Trying to restrict after the fact
// ✅ Good practice: Start restrictive, add specific permissions
ability
.can('read', 'Post', { published: true }) // Allow reading published posts
.can(['create', 'update'], 'Post', { authorId: 123 }) // Allow authors to manage their own posts
.cannot('delete', 'Post') // Ensure deletion is never allowed, regardless of other rules
// Even better: Be explicit about all permissions
const userAbility = createAbility()
// Post permissions
.can('read', 'Post', { published: true })
.can('create', 'Post')
.can('update', 'Post', { authorId: 123 })
// Comment permissions
.can('read', 'Comment')
.can('create', 'Comment', { authenticated: true })
.cannot('delete', 'Comment') // Extra safety guard
Using Operators
For more complex conditions, you can use operators:
import { operators } from '@nordic-ui/asgardian'
const ability = createAbility()
ability.can('read', 'Post', {
status: operators.or('published', 'archived'),
views: operators.gt(1000),
tags: operators.includesAll(['important', 'featured'])
})
Summary
In this section, we explored how to create advanced permission rules using multiple conditions, role-based access control, and rule combinations. While the library doesn’t support dynamic context evaluation directly, you can achieve complex permission structures through careful rule composition and condition combinations.