### Run Development Server in Next.js Project
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/README.md
Commands to start the local development server for the Next.js project. These commands are typically used with package managers like npm, yarn, pnpm, or bun. Ensure you have the project dependencies installed before running these commands. The server will be accessible at http://localhost:3000.
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
--------------------------------
### Best Practice: Start Restrictive (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/role-based-permissions.mdx
Demonstrates the best practice of starting with limited permissions and explicitly adding necessary ones. This approach prevents accidental over-permissioning. The code shows a 'good' example with specific abilities and a 'bad' example starting with broad permissions.
```typescript
// ✅ Good: Start with limited permissions
const ability = createAbility()
.can('read', 'Post', { published: true })
.can('update', 'Post', { authorId: userId })
// ❌ Bad: Starting with broad permissions
const ability = createAbility()
.can('manage', 'all')
.cannot('delete', 'Post')
```
--------------------------------
### Install Asgardian React Integration
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/index.mdx
This command installs the Asgardian React library, which provides hooks and components for easily integrating Asgardian's authorization logic into React applications. Use npm or yarn for installation.
```sh
npm i @nordic-ui/asgardian-react
```
```sh
yarn add @nordic-ui/asgardian-react
```
--------------------------------
### Install Asgardian Authorization Library
Source: https://github.com/nordic-ui/asgardian/blob/main/packages/asgardian/README.md
Install the Asgardian library using your preferred package manager. This command is compatible with npm, yarn, and pnpm.
```sh
npm install @nordic-ui/asgardian
yarn add @nordic-ui/asgardian
pnpm add @nordic-ui/asgardian
```
--------------------------------
### Install @nordic-ui/asgardian-react
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/hooks.mdx
Installs the necessary React package for Asgardian permissions. This command can be used with either npm or yarn.
```sh
npm install @nordic-ui/asgardian-react
```
```sh
yarn add @nordic-ui/asgardian-react
```
--------------------------------
### React Example: Integrate Asgardian with AbilityProvider and useCan
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/index.mdx
This React TypeScript example demonstrates how to set up Asgardian for frontend authorization. It uses AbilityProvider to make the ability instance available throughout the component tree and the useCan hook to conditionally render UI elements based on user permissions.
```tsx
import { createAbility } from '@nordic-ui/asgardian';
import { AbilityProvider, useCan } from '@nordic-ui/asgardian-react';
const ability = createAbility()
.can('read', 'Article')
.can('write', 'Comment')
.can('manage', 'Profile');
const App = () => {
return (
);
};
const TypedPostActions = () => {
const canWrite = useCan('write', 'Article');
const canDelete = useCan('delete', 'Comment');
return (
{canWrite && }
{canDelete && }
);
};
```
--------------------------------
### Quick Start: Create and Check Permissions with Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/README.md
Demonstrates how to create a type-safe ability instance, define permissions using 'can' and 'cannot' methods, and check if actions are allowed for specific resources and conditions using the 'isAllowed' method.
```typescript
import { createAbility } from '@nordic-ui/asgardian'
// Create a type-safe ability instance
const ability = createAbility<'publish', 'Post'>()
// Define permissions
ability
.can('read', 'Post', { published: true })
.can('update', 'Post', { authorId: 123 })
.can('publish', 'Post', { draft: true, authorId: 123 })
.cannot('delete', 'Post')
// Check permissions
ability.isAllowed('read', 'Post', { published: true }) // true
ability.isAllowed('publish', 'Post', { draft: false }) // false
ability.isAllowed('delete', 'Post') // false
```
--------------------------------
### Setup AbilityProvider in React Application
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/hooks.mdx
Demonstrates how to set up the `AbilityProvider` at the root of your React application. This provider makes the Asgardian ability instance available to all child components.
```tsx
import { createAbility } from '@nordic-ui/asgardian'
import { AbilityProvider } from '@nordic-ui/asgardian-react'
// Create your ability instance
const ability = createAbility()
.can('read', 'Post')
.can('write', 'Comment')
.can('manage', 'Profile')
const App = () => {
return (
)
}
```
--------------------------------
### Combine 'can' and 'cannot' Rules in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This example shows how to chain multiple 'can' and 'cannot' rules to create complex permission logic. It demonstrates allowing users to read posts while explicitly denying them the ability to delete posts, and verifying these permissions.
```typescript
// 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
```
--------------------------------
### Setting and Getting Rule Reasons in Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/api-reference.mdx
Demonstrates how to define rules with specific reasons for denied actions and how to retrieve these reasons using the `getReason` method. It covers cases where a reason is explicitly set, where conditions apply, and where no specific reason is found.
```typescript
ability
.cannot('delete', 'Post').reason('Deletion not allowed')
.cannot('update', 'Post', { archived: true }).reason('Cannot update archived posts')
const deleteReason = ability.getReason('delete', 'Post')
// Returns: "Deletion not allowed"
const updateReason = ability.getReason('update', 'Post', { archived: true })
// Returns: "Cannot update archived posts"
const unknownReason = ability.getReason('create', 'Post')
// Returns: undefined
```
--------------------------------
### Best Practice: Be Explicit (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/role-based-permissions.mdx
Highlights the importance of being explicit about resource access. The 'good' example clearly defines permissions for 'Post' and 'Comment', while the 'bad' example shows an overly broad permission that could lead to security issues.
```typescript
// ✅ Good: Specific resource access
const editorAbility = createAbility()
.can('manage', 'Post') // Explicitly grant all actions on posts
.can('read', 'Comment') // Explicitly grant only read access on comments
// ❌ Bad: Over-permissive resource access
const editorAbility = createAbility()
.can('manage', 'all') // Too broad, grants access to all current and future resources
```
--------------------------------
### Install Asgardian React
Source: https://github.com/nordic-ui/asgardian/blob/main/packages/asgardian-react/README.md
Install the Asgardian React package using npm, yarn, or pnpm. Note that `@nordic-ui/asgardian` is a peer dependency and must also be installed.
```sh
npm install @nordic-ui/asgardian-react
yarn add @nordic-ui/asgardian-react
pnpm add @nordic-ui/asgardian-react
```
--------------------------------
### API Route Protection with Express.js
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
Provides an example of protecting an Express.js API route using `throwIfNotAllowed()`. It demonstrates how to integrate permission checks within a route handler to ensure only authorized users can perform actions like deleting a post, and how to return appropriate HTTP responses.
```typescript
// Express.js route handler
app.delete('/api/posts/:id', async (req, res) => {
try {
const ability = await getUserAbility(req.user)
const post = await getPost(req.params.id)
ability.throwIfNotAllowed('delete', 'Post', post)
await deletePost(req.params.id)
res.json({ success: true })
} catch (error) {
if (error instanceof ForbiddenError) {
res.status(403).json({
error: 'Permission denied',
reason: error.message
})
} else {
res.status(500).json({ error: 'Internal server error' })
}
}
})
```
--------------------------------
### Implement Rule Precedence Best Practices in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This code illustrates effective rule precedence in Asgardian. It contrasts a bad practice of starting with broad permissions and then restricting them, with good practices that start restrictively and add specific allowances, finally showing an explicit approach following the principle of least privilege.
```typescript
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
```
--------------------------------
### Define Rule for All Resources ('all') in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This example shows the use of the 'all' keyword to create a rule that applies to every resource managed by Asgardian. This is typically used for super-administrator roles.
```typescript
// Allow managing all resources
ability.can('manage', 'all');
```
--------------------------------
### Chain Multiple Rules for Complex Permissions in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This example showcases how to build a complex set of permissions by chaining multiple 'can' and 'cannot' rules. It defines read access for posts, read/create access for comments, and explicitly denies update access for comments, then verifies these permissions.
```typescript
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
```
--------------------------------
### Install Asgardian Permission System
Source: https://github.com/nordic-ui/asgardian/blob/main/README.md
Installs the core Asgardian permission system package using npm, yarn, or pnpm. This package is essential for setting up permission rules and checks in your TypeScript application.
```bash
# Using npm
npm install @nordic-ui/asgardian
# Using yarn
yarn add @nordic-ui/asgardian
# Using pnpm
pnpm add @nordic-ui/asgardian
```
--------------------------------
### Add Reasons to Permission Rules with .reason() - TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
Demonstrates how to chain the `.reason()` method after `can()` and `cannot()` rules in Asgardian to provide explanations for permission decisions. This improves user feedback, debugging, and auditing by associating specific reasons with rule outcomes. It shows examples with various conditions and resource types.
```ts
import { createAbility } from '@nordic-ui/asgardian'
const ability = createAbility<'publish' | 'archive', 'Post' | 'Comment'>()
ability
// read published posts
.can('read', 'Post', { published: true })
.reason('Public posts are readable by everyone')
// update own posts
.can('update', 'Post', { authorId: 123 })
.reason('Authors can edit their own posts')
// delete any posts
.cannot('delete', 'Post')
.reason('Deletion not allowed for security reasons')
// publish non-draft posts
.cannot('publish', 'Post', { status: { $ne: 'draft'} })
.reason('Cannot publish non-draft posts')
// update locked posts
.cannot('update', 'Post', { locked: true })
.reason('Post is locked for editing')
```
--------------------------------
### Apply Conditional Permissions in Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/index.mdx
This TypeScript example illustrates how to add conditions to Asgardian rules. It defines a permission to read a 'Post' only if it's published, and then checks this permission against different post objects, demonstrating conditional access control.
```typescript
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
```
--------------------------------
### Check Permissions for Different User Roles (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/chained-api.mdx
Provides examples of checking permissions using the `isAllowed` method of an Asgardian ability instance for different user roles ('admin', 'user', 'moderator'). It demonstrates how the defined chained rules are evaluated against user context. Assumes an initialized Asgardian ability instance and defined roles/permissions.
```typescript
const adminUser = {
id: 123,
roles: ['admin'],
};
const userUser = {
id: 456,
roles: ['user'],
};
const moderatorUser = {
id: 789,
roles: ['moderator'],
};
// Check permissions for admin
console.log(ability.isAllowed('manage', 'Post', null, { user: adminUser })); // true
console.log(ability.isAllowed('create', 'Post', null, { user: adminUser })); // true
console.log(ability.isAllowed('read', 'Post', null, { user: adminUser })); // true
console.log(ability.isAllowed('delete', 'Post', null, { user: adminUser })); // true
// Check permissions for user
console.log(ability.isAllowed('manage', 'Post', null, { user: userUser })); // false
console.log(ability.isAllowed('create', 'Post', null, { user: userUser })); // false
console.log(ability.isAllowed('read', 'Post', null, { user: userUser })); // true
console.log(ability.isAllowed('delete', 'Post', null, { user: userUser })); // false
// Check permissions for moderator
console.log(ability.isAllowed('manage', 'Post', null, { user: moderatorUser })); // false
console.log(ability.isAllowed('create', 'Post', null, { user: moderatorUser })); // true
console.log(ability.isAllowed('read', 'Post', null, { user: moderatorUser })); // true
console.log(ability.isAllowed('delete', 'Post', null, { user: moderatorUser })); // false
```
--------------------------------
### Define Rule with Multiple Actions in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This example illustrates how to specify multiple actions within a single rule using an array. This allows for more concise rule definitions when a permission applies to several operations on the same resource.
```typescript
// Allow creating, updating, and deleting posts
ability.can(['create', 'update', 'delete'], 'Post');
```
--------------------------------
### Role-Based Access Control (RBAC) in TypeScript
Source: https://context7.com/nordic-ui/asgardian/llms.txt
Illustrates how to implement Role-Based Access Control (RBAC) using the Asgardian SDK. It defines permissions based on user roles, including special 'manage' and 'all' permissions. The example shows how to dynamically define abilities based on a user's role, granting different levels of access to resources.
```typescript
import { createAbility } from '@nordic-ui/asgardian'
type User = {
id: number
role: 'admin' | 'editor' | 'author' | 'viewer'
}
type Resources = 'Post' | 'Comment' | 'User' | 'Settings'
function defineAbilities(user: User) {
const ability = createAbility()
if (user.role === 'admin') {
// Admins can do everything
ability.can('manage', 'all')
return ability
}
if (user.role === 'editor') {
// Editors can manage posts and comments
ability.can('manage', ['Post', 'Comment'])
// But can only read users and settings
ability.can('read', ['User', 'Settings'])
}
if (user.role === 'author') {
// Authors can read all posts
ability.can('read', 'Post')
// But can only manage their own posts
ability.can(['create', 'update', 'delete'], 'Post', { authorId: user.id })
// Can create and read comments
ability.can(['create', 'read'], 'Comment')
}
if (user.role === 'viewer') {
// Viewers can only read published content
ability.can('read', 'Post', { published: true })
ability.can('read', 'Comment')
}
return ability
}
// Usage
const admin = { id: 1, role: 'admin' as const }
const adminAbility = defineAbilities(admin)
console.log(adminAbility.isAllowed('delete', 'User')) // true
const author = { id: 123, role: 'author' as const }
const authorAbility = defineAbilities(author)
console.log(authorAbility.isAllowed('update', 'Post', { authorId: 123 })) // true
console.log(authorAbility.isAllowed('update', 'Post', { authorId: 456 })) // false
const viewer = { id: 789, role: 'viewer' as const }
const viewerAbility = defineAbilities(viewer)
console.log(viewerAbility.isAllowed('read', 'Post', { published: true })) // true
console.log(viewerAbility.isAllowed('read', 'Post', { published: false })) // false
console.log(viewerAbility.isAllowed('create', 'Post')) // false
```
--------------------------------
### Setup AbilityProvider in React
Source: https://github.com/nordic-ui/asgardian/blob/main/packages/asgardian-react/README.md
Wrap your application with the `AbilityProvider` component from `@nordic-ui/asgardian-react` to make Asgardian permissions available throughout the component tree. This requires creating an `ability` instance using `createAbility` from `@nordic-ui/asgardian`.
```tsx
import { createAbility } from '@nordic-ui/asgardian';
import { AbilityProvider } from '@nordic-ui/asgardian-react';
const ability = createAbility();
// Configure your ability with rules...
const App = () => {
return (
);
}
```
--------------------------------
### useCan for Navigation Guards (React)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/hooks.mdx
Illustrates using the `useCan` hook to implement navigation guards within a React application. This example controls the rendering of navigation links based on whether the user has permissions to 'access' the 'AdminPanel', 'manage' 'User' resources, or 'view' 'Reports'.
```tsx
const Navigation = () => {
const { can: canViewAdmin } = useCan('access', 'AdminPanel')
const { can: canManageUsers } = useCan('manage', 'User')
const { can: canViewReports } = useCan('view', 'Reports')
return (
)
}
```
--------------------------------
### Implement Nested Chained Permission Rules (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/chained-api.mdx
Illustrates how to create nested or sequential permission rules using Asgardian's chained API. This example defines a read permission with a condition, followed by an update permission with conditional author/admin check, and finally a delete permission with a condition. Assumes an initialized Asgardian ability instance.
```typescript
ability
.can('read', 'Post', (post) => post.published)
.can('update', 'Post', (post, context) =>
post.authorId === context.userId || context.user.roles.includes('admin'))
.cannot('delete', 'Post', (post, context) => post.authorId !== context.userId);
```
--------------------------------
### Apply Multiple Conditions to a Rule in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This snippet demonstrates how to define a rule that requires multiple conditions to be met simultaneously. The example allows 'manage' action on 'Post' only if both `authorId` and `published` properties match the specified criteria. It also shows how `isAllowed` checks all conditions.
```typescript
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
```
--------------------------------
### Use Role-Based Abilities to Check Permissions (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/role-based-permissions.mdx
This example demonstrates how to instantiate a role-based ability object using the previously defined `createRoleAbility` function and then use its `isAllowed` method to perform permission checks. It shows checking for read, update, and delete permissions on 'Post' resources with specific conditions.
```typescript
// Assuming createRoleAbility is defined as above
// Create ability instance based on user's role
const user = {
id: 123,
role: 'author'
}
const ability = createRoleAbility(user.role, user.id)
// Check permissions
ability.isAllowed('read', 'Post', { published: true }) // true
ability.isAllowed('update', 'Post', { authorId: 123 }) // true
ability.isAllowed('update', 'Post', { authorId: 456 }) // false
ability.isAllowed('delete', 'Post', { authorId: 456 }) // false
```
--------------------------------
### useCan and useCannot for Conditional Rendering (React)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/hooks.mdx
Demonstrates how to use `useCan` and `useCannot` hooks to conditionally render UI elements based on user permissions. This example shows controlling visibility of edit/delete buttons and comments section based on user's ability to 'edit' a 'Post', 'delete' a 'Post', or 'create' a 'Comment'.
```tsx
type BlogPostProps = { post: Post }
const BlogPost: FC = ({ post }) => {
const { can: canEdit } = useCan('edit', 'Post', { authorId: post.authorId })
const { can: canDelete } = useCan('delete', 'Post', { authorId: post.authorId })
const { cannot: cannotComment } = useCannot('create', 'Comment')
return (
{post.title}
{post.content}
{canEdit && (
)}
{canDelete && (
)}
{cannotComment ? (
Sign in to leave a comment
) : (
)}
)
}
```
--------------------------------
### Apply Conditions to Rules in TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/rules-and-conditions.mdx
This snippet illustrates how to add conditions to Asgardian rules, allowing for more granular permission checks. The example demonstrates allowing the 'read' action on 'Post' only if the post resource has a 'published' property set to true. It also shows how to check permissions against specific resource objects.
```typescript
// 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
```
--------------------------------
### Best Practice: Clear, Actionable Reasons
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
Illustrates the best practice of providing clear and actionable reasons for permission checks. It contrasts helpful reasons that explain the restriction with vague reasons that offer little insight.
```typescript
// ✅ Good - Clear and actionable for both allow and deny
ability
.can('read', 'Post', { published: true })
.reason('Published content is publicly accessible')
.cannot('publish', 'Post', { status: 'draft' })
.reason('Post must be reviewed before publishing')
// ❌ Less helpful - Vague
ability
.can('read', 'Post')
.reason('Allowed')
.cannot('publish', 'Post', { status: 'draft' })
.reason('Cannot publish')
```
--------------------------------
### Asgardian: Implement Role-Based Access Control
Source: https://github.com/nordic-ui/asgardian/blob/main/README.md
Demonstrates how to define permissions based on user roles (e.g., 'admin', 'editor') by conditionally creating and configuring Asgardian ability instances. Admins get 'manage all' permissions, while editors get specific permissions for 'Post'.
```typescript
const defineAbilities = (user: User) => {
const ability = createAbility()
if (user.role === 'admin') {
ability.can('manage', 'all')
return ability; // Early return because admin has all permissions already
}
if (user.role === 'editor') {
ability.can(['read', 'update'], 'Post')
ability.can('publish', 'Post', { draft: true })
}
return ability
}
```
--------------------------------
### Best Practice: Use Type Safety (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/role-based-permissions.mdx
Emphasizes the use of type safety for roles and resources to ensure predictable behavior and prevent runtime errors. The 'good' example defines specific types for roles and resources, while the 'bad' example uses generic `any` types, lacking type safety.
```typescript
// ✅ Good: Type-safe roles and resources
type Role = 'admin' | 'editor' | 'author' | 'visitor'
type Resources = 'Post' | 'Comment'
const ability = createAbility()
// ❌ Bad: No type safety
const ability = createAbility()
```
--------------------------------
### Retrieve Permission Reasons with getReason() - TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
Shows how to use the `getReason()` method in Asgardian to retrieve the specific explanation associated with a permission rule. This function takes the action, resource, and optional conditions as arguments and returns the reason string for the matching rule, or `undefined` if no rule matches. It demonstrates retrieving reasons for allowed, denied, and conditional actions.
```ts
const ability = createAbility()
ability
.can('read', 'Post').reason('Reading is always allowed')
.cannot('delete', 'Post').reason('Deletion not allowed')
.cannot('update', 'Post', { archived: true }).reason('Cannot modify archived content')
// Get reasons for both allowed and denied actions
const readReason = ability.getReason('read', 'Post')
console.log(readReason) // "Reading is always allowed"
const deleteReason = ability.getReason('delete', 'Post')
console.log(deleteReason) // "Deletion not allowed"
const updateReason = ability.getReason('update', 'Post', { archived: true })
console.log(updateReason) // "Cannot modify archived content"
const unknownReason = ability.getReason('publish', 'Post')
console.log(unknownReason) // undefined (no matching rule)
```
--------------------------------
### Integrate Asgardian with Database for Permissions
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/advanced-usage.mdx
Shows how to integrate Asgardian with a database to fetch and apply user roles and resource permissions dynamically. Assumes database helper functions like `fetchUserRoles` and `fetchResourcePermissions` exist. Requires @nordic-ui/asgardian.
```javascript
import { createAbility } from '@nordic-ui/asgardian';
import { fetchUserRoles, fetchResourcePermissions } from './database';
const ability = createAbility();
// Fetch roles and permissions from the database
async function setupAbility(userId) {
const userRoles = await fetchUserRoles(userId);
const resourcePermissions = await fetchResourcePermissions();
resourcePermissions.forEach(({ action, resource, condition }) => {
ability.can(action, resource, (resource, context) => {
return userRoles.some(role => condition.includes(role));
});
});
}
// Example usage
setupAbility(123).then(() => {
const user = { id: 123, roles: ['admin'] };
console.log(ability.isAllowed('manage', 'all', null, { user })); // true
});
```
--------------------------------
### User-Friendly Messages for Permissions
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
Demonstrates how to define custom, user-friendly reasons for permission checks using the `.reason()` method. This allows for more specific and helpful feedback to users when their actions are restricted.
```typescript
const ability = createAbility()
ability
.can('read', 'Post', { published: true })
.reason('Published posts are available to all users')
.cannot('read', 'Post', { published: false })
.reason('This post is not yet published')
.can('update', 'Post', { authorId: 123 })
.reason('You have full editing rights to your posts')
.cannot('update', 'Post', { authorId: { $ne: 123 } })
.reason('You can only edit your own posts')
.cannot('delete', 'Post', { hasComments: true })
.reason('Cannot delete posts with comments. Please delete comments first.')
.cannot('publish', 'Post', { status: 'draft', reviewStatus: { $ne: 'approved' } })
.reason('Posts must be reviewed before publishing')
```
--------------------------------
### Get Denial Reason - TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/api-reference.mdx
Retrieves the reason string associated with why a specific action was denied on a resource, optionally considering conditions. Returns the reason string or undefined if the action is allowed or no reason was set.
```typescript
getReason(
action: Action | Action[],
resource: Resource,
conditions?: Condition
): string | undefined
// Example usage (assuming a denial with a reason was previously set)
const reason = ability.getReason('delete', 'Post')
if (reason) {
console.log('Cannot delete:', reason)
}
```
--------------------------------
### Get Permission Denial Reason - TypeScript
Source: https://context7.com/nordic-ui/asgardian/llms.txt
Retrieves a custom reason message for denied actions using the `getReason` method. This requires defining rules with `.reason()` beforehand. It returns `undefined` for allowed actions.
```typescript
import { createAbility } from '@nordic-ui/asgardian'
const ability = createAbility<'publish', 'Post'>()
ability
.cannot('update', 'Post', { userId: { $ne: 1 } })
.reason('You must be the owner of the post to update it')
ability
.cannot('publish', 'Post', { status: 'draft' })
.reason('Post must not be in draft status to publish')
ability
.cannot('delete', 'Post', { published: true })
.reason('Cannot delete published posts')
// Check permissions and get reasons
console.log(ability.isAllowed('update', 'Post', { userId: 2 })) // false
console.log(ability.getReason('update', 'Post', { userId: 2 }))
// "You must be the owner of the post to update it"
console.log(ability.isAllowed('publish', 'Post', { status: 'draft' })) // false
console.log(ability.getReason('publish', 'Post', { status: 'draft' }))
// "Post must not be in draft status to publish"
console.log(ability.isAllowed('delete', 'Post', { published: true })) // false
console.log(ability.getReason('delete', 'Post', { published: true }))
// "Cannot delete published posts"
// No reason for allowed actions
console.log(ability.isAllowed('delete', 'Post', { published: false })) // true
console.log(ability.getReason('delete', 'Post', { published: false })) // undefined
```
--------------------------------
### Permission Methods - getReason
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/api-reference.mdx
Retrieves the reason why an action was denied on a resource, optionally with conditions.
```APIDOC
## Permission Methods - getReason
### Description
Retrieves the reason why an action was denied on a resource, optionally with conditions.
### Method
```ts
getReason(
action: Action | Action[],
resource: Resource,
conditions?: Condition
): string | undefined
```
### Parameters
#### Path Parameters
- **action** (Action | Action[]) - Required - Single action or array of actions to check.
- **resource** (Resource) - Required - The resource to check against.
- **conditions** (Condition) - Optional - Conditions to evaluate.
### Request Example
```ts
// Get reason for denial
ability.getReason('delete', 'Post') // "Deletion not allowed for security" or undefined
// With conditions
ability.getReason('update', 'Post', { archived: true }) // "Reason for archived update denial" or undefined
```
### Response
#### Success Response (200)
- **string | undefined**: Returns the denial reason string if available, otherwise `undefined`.
#### Response Example
```json
"Deletion not allowed for security"
```
```
--------------------------------
### Define Basic Permission Rules with Chained API (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/chained-api.mdx
Demonstrates the fundamental usage of Asgardian's chained API to define basic 'can' rules. It initializes an ability instance and chains 'can' methods to specify allowed actions and resources. Requires the '@nordic-ui/asgardian' package.
```typescript
import { createAbility } from '@nordic-ui/asgardian';
const ability = createAbility();
ability
.can('read', 'Post')
.can('update', 'Post');
```
--------------------------------
### Define Dynamic Permission Rules with Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/advanced-usage.mdx
Demonstrates how to create dynamic permission rules in Asgardian that adapt based on context, such as user properties or resource states. Requires the @nordic-ui/asgardian package.
```typescript
import { createAbility } from '@nordic-ui/asgardian';
const ability = createAbility();
// Dynamic rule based on user properties
ability
.can('read', 'Post', (post, context) => post.published || context.user.roles.includes('admin'));
// Dynamic rule based on resource state
ability
.can('update', 'Post', (post, context) => post.authorId === context.userId || context.user.roles.includes('moderator'));
```
--------------------------------
### Use Consistent Messaging with Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
This TypeScript code demonstrates how to define and use consistent messages for permission checks in Asgardian. It shows both good and bad examples of attaching reasons to 'can' and 'cannot' rules, emphasizing clarity and avoiding internal logic exposure.
```typescript
const MESSAGES = {
PUBLIC_ACCESS: 'This content is publicly accessible',
OWNER_ONLY: 'You can only modify your own content',
LOCKED_RESOURCE: 'This resource is locked and cannot be modified',
INVALID_STATE: (state: string) => `Action not allowed when resource is ${state}`,
UPGRADE_REQUIRED: 'Upgrade your account to access this feature'
}
ability
.can('read', 'Post', { published: true })
.reason(MESSAGES.PUBLIC_ACCESS)
.cannot('delete', 'Post', { authorId: { $ne: 123 } })
.reason(MESSAGES.OWNER_ONLY)
.cannot('update', 'Post', { locked: true })
.reason(MESSAGES.LOCKED_RESOURCE)
```
--------------------------------
### Create Ability Instance - TypeScript
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/api-reference.mdx
Creates a new ability instance for defining and checking permissions. Supports generic parameters for custom actions and resources. Returns an object with methods for permission management.
```typescript
function createAbility(): CreateAbility
// Basic usage
const ability = createAbility()
// With custom actions
const ability = createAbility<'publish' | 'archive'>()
// With custom resources
const ability = createAbility()
// With both custom actions and resources
const ability = createAbility<'publish' | 'archive', 'Post' | 'Comment'>()
```
--------------------------------
### Conditional Rendering with useAbility in React
Source: https://github.com/nordic-ui/asgardian/blob/main/packages/asgardian-react/README.md
Demonstrates how to use the `can` method from the `useAbility` hook to conditionally render UI elements based on user permissions. This example shows edit and delete buttons for a post, only visible if the user has the appropriate permissions.
```tsx
const PostActions = ({ post }) => {
const { can } = useAbility();
return (
);
}
```
--------------------------------
### Core Functions - createAbility
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/api-reference.mdx
Creates a new ability instance for defining and checking permissions. Allows customization of actions and resources.
```APIDOC
## Core Functions - createAbility
### Description
Creates a new ability instance for defining and checking permissions. Allows customization of actions and resources.
### Method
```ts
function createAbility(): CreateAbility
```
### Parameters
#### Generic Parameters
- **ExtendedActions** (string) - Optional - Custom action types to extend the default actions.
- **ExtendedResources** (string) - Optional - Custom resource types to extend the default resources.
### Returns
- Returns an object with methods for defining and checking permissions.
### Examples
```ts
// Basic usage
const ability = createAbility()
// With custom actions
const ability = createAbility<'publish' | 'archive'>()
// With custom resources
const ability = createAbility()
// With both custom actions and resources
const ability = createAbility<'publish' | 'archive', 'Post' | 'Comment'>()
```
```
--------------------------------
### Error Handling: useAbility must be within AbilityProvider (React)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/hooks.mdx
Highlights the requirement for hooks like `useCan` and `useAbility` to be used within an `AbilityProvider`. Using them outside this context will result in an error, indicating that the ability context is not available. The example shows correct and incorrect usage patterns.
```tsx
const ComponentWithoutProvider = () => {
const { can: canRead } = useCan('read', 'Post')
return
This won't work
}
// Error: "useAbilityContext must be used within an AbilityProvider"
// ✅ Correct usage
const App = () => {
const ability = createAbility()
return (
)
}
```
--------------------------------
### Define and Check Permissions with Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/index.mdx
This TypeScript code demonstrates how to create an ability instance, define rules for allowed and disallowed actions on 'Post' resources, and then check if specific actions are permitted. It showcases the basic fluent API for setting permissions.
```typescript
import { createAbility } from '@nordic-ui/asgardian';
const ability = createAbility();
ability.can(['read', 'create'], 'Post');
ability.cannot('delete', 'Post');
const canReadPost = ability.isAllowed('read', 'Post');
console.log(canReadPost); // true
const canDeletePost = ability.isAllowed('delete', 'Post');
console.log(canDeletePost); // false
```
--------------------------------
### Avoid Exposing Sensitive Information in Asgardian
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
This TypeScript example illustrates how to prevent sensitive information from being exposed in Asgardian's permission reason messages. It contrasts a good approach, using generic and user-friendly reasons, with a bad approach that reveals internal details like user IDs.
```typescript
// ✅ Good - Generic but helpful
ability
.can('read', 'Post', { public: true })
.reason('Public posts are accessible to all users')
.cannot('read', 'Post', { private: true, authorId: { $ne: user.id } })
.reason('This post is private')
// ❌ Bad - Exposes internal logic
ability.cannot('read', 'Post', { private: true, authorId: { $ne: user.id } })
.reason('Post is private and authorId 456 does not match current user 123')
```
--------------------------------
### Chain Multiple Actions and Manage All Resources (TypeScript)
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/chained-api.mdx
Shows how to chain multiple actions ('create', 'update', 'delete') for a specific resource ('Post') and how to define a broad 'manage all' permission using Asgardian's chained API. Assumes an initialized Asgardian ability instance.
```typescript
ability
.can(['create', 'update', 'delete'], 'Post')
.can('manage', 'all');
```
--------------------------------
### Centralized Permission Checking with PermissionService
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/advanced-usage.mdx
Demonstrates how to create a `PermissionService` class for centralized permission checking. It initializes user abilities based on roles and provides methods to check and enforce permissions, logging denials with detailed context. Dependencies include `@nordic-ui/asgardian` and a `logger` instance.
```typescript
import { createAbility, ForbiddenError } from '@nordic-ui/asgardian'
class PermissionService {
private ability: CreateAbility
constructor(user: User) {
this.ability = this.createUserAbility(user)
}
private createUserAbility(user: User) {
const ability = createAbility()
// Base permissions
ability
.can('read', 'Post', { published: true })
.cannot('read', 'Post', { private: true, authorId: { $ne: user.id } })
.reason('This post is private')
// Role-based permissions
if (user.role === 'admin') {
ability.can('manage', 'all')
} else if (user.role === 'moderator') {
ability
.can(['update', 'delete'], 'Post')
.cannot('delete', 'Post', { hasComments: true })
.reason('Cannot delete posts with comments')
}
return ability
}
// Centralized checking with detailed error context
checkPermission(action: string, resource: string, data?: any) {
const isAllowed = this.ability.isAllowed(action, resource, data)
if (!isAllowed) {
const reason = this.ability.getReason(action, resource, data)
// Log the denial for auditing
this.logPermissionDenial(action, resource, reason, data)
return { allowed: false, reason }
}
return { allowed: true }
}
// Throw with enhanced context
enforcePermission(action: string, resource: string, data?: any) {
const result = this.checkPermission(action, resource, data)
if (!result.allowed) {
throw new ForbiddenError(result.reason || 'Access denied')
}
}
private logPermissionDenial(action: string, resource: string, reason?: string, data?: any) {
logger.warn('Permission denied', {
action,
resource,
reason,
userId: this.user.id,
resourceId: data?.id,
timestamp: new Date().toISOString()
})
}
}
```
--------------------------------
### Role-Based Permission Grouping with switch statement
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/advanced-usage.mdx
Shows how to group permissions effectively using roles and a switch statement. This method simplifies permission management by assigning specific capabilities to different user roles, ensuring easier updates and maintenance.
```typescript
const createRoleBasedAbility = (user: User) => {
const ability = createAbility()
switch (user.role) {
case 'admin':
ability.can('manage', 'all')
break
case 'moderator':
ability
.can(['read', 'update'], 'Post')
.can(['read', 'delete'], 'Comment')
.cannot('delete', 'Post', { published: true })
.reason('Moderators cannot delete published posts')
.cannot('update', 'User', { role: 'admin' })
.reason('Moderators cannot modify admin accounts')
break
case 'user':
ability
.can('read', 'Post', { published: true })
.can(['create', 'update'], 'Post', { authorId: user.id })
.cannot('delete', 'Post')
.reason('Regular users cannot delete posts')
.cannot('update', 'Post', { published: true })
.reason('Cannot edit published posts')
break
default:
ability
.can('read', 'Post', { published: true })
.cannot('create', 'Post')
.reason('Please sign in to create posts')
}
return ability
}
```
--------------------------------
### Logging and Auditing Permission Checks
Source: https://github.com/nordic-ui/asgardian/blob/main/apps/docs/content/docs/error-handling.mdx
Shows how to log permission checks for auditing purposes. The `auditPermissionCheck` function logs whether an action is allowed, the reason for denial, and other relevant details to a logger and potentially a database.
```typescript
const auditPermissionCheck = (action: string, resource: string, user: User, data?: any) => {
const ability = getUserAbility(user)
const isAllowed = ability.isAllowed(action, resource, data)
const reason = ability.getReason(action, resource, data)
logger.info('Permission check', {
userId: user.id,
action,
resource,
isAllowed,
reason,
timestamp: new Date().toISOString(),
data: data ? { id: data.id } : null
})
if (!isAllowed) {
// Could also store in database for audit trail
await saveAuditLog({
userId: user.id,
action: 'PERMISSION_DENIED',
details: { action, resource, reason }
})
}
return isAllowed
}
```