Attribute-based Access Control

This package provides a factory function that returns a type-safe hasPermission function for implementing Attribute-Based Access Control (ABAC) in your applications. By defining roles, resources, and actions, you can use this library to enforce precise access control policies.

Installation

Install the package using npm:

bash
npm install @anakin-gbit/abac

Quick Start

To integrate ABAC into your application, you must create a configuration file to define your roles, resources, and permissions, then call the factory function createHasPermission to generate a type-safe hasPermission function.

Step 1: Define Your Configuration

Create a configuration file, for example src/lib/abac/index.ts. In this example, we define an article resource with two actions, and assign permissions to two roles: user and admin.

typescript
import { AppliedPermissionsConfig, createHasPermission } from '@anakin-gbit/abac'

// Define the article resource
type Article = { id: string; title: string }

// Define application roles
type ApplicationRoles = 'user' | 'admin'

// Define the authorization subject (e.g., a user object)
type AuthorizationSubject = { id: string; roles: ApplicationRoles[] }

// Define available permissions
type AvailablePermissions = {
  article: {
    action: 'view' | 'delete'
    dataType: Article
  }
}

// Define the applied permissions
const APPLIED_PERMISSIONS: AppliedPermissionsConfig<
  ApplicationRoles,
  AvailablePermissions,
  AuthorizationSubject
> = {
  admin: {
    article: {
      view: true,
      delete: true,
    },
  },
  user: {
    article: {
      view: true,
      delete: false,
    },
  },
}

// Generate the hasPermission function
const hasPermission = createHasPermission<
  ApplicationRoles,
  AvailablePermissions,
  AuthorizationSubject
>(APPLIED_PERMISSIONS)

// Export the function for use in your app
export { hasPermission }

Step 2: Use the hasPermission Function

Now you can use the hasPermission function to enforce permissions in your application.

typescript
import { hasPermission } from '@/lib/abac'

const appUser = { id: 'abc123', roles: ['user'] }
const article = { id: 'cba321', title: 'My Article' }

console.log('Access:', {
  canView: hasPermission(appUser, 'article', 'view', article), // true
  canDelete: hasPermission(appUser, 'article', 'delete', article), // false
})

The function ensures type safety by validating the resource, action, and data parameters according to your configuration.

Type-Safety in Action

The hasPermission function is type-safe. If you try to use an invalid resource, action, or data type, TypeScript will produce a compile-time error. For example:

typescript
// Compile-time error: 'edit' is not a valid action for 'article'
hasPermission(appUser, 'article', 'edit', article)

// Compile-time error: 'appUser' is not a valid resource
hasPermission(appUser, 'appUser', 'view', appUser)

// Compile-time error: Invalid data type for 'article'
hasPermission(appUser, 'article', 'view', { name: 'Invalid Data' })

Advanced Use Cases

The APPLIED_PERMISSIONS object supports defining dynamic permissions by using functions instead of boolean values for specific actions. These functions are evaluated at runtime and receive two arguments:

  • subject: The object representing the entity attempting the action (e.g., a user).
  • data: The resource being acted upon (e.g., an article, app user, or other entity).

This flexibility allows you to implement complex access control logic. Here’s an example:

typescript
const APPLIED_PERMISSIONS: AppliedPermissionsConfig<
  ApplicationRoles,
  AvailablePermissions,
  AuthorizationSubject
> = {
  user: {
    article: {
      // Allow users to view articles only if their ID matches a specific condition
      view: (user, article) => {
        return article.id === user.id // User can view their own articles
      },
      // Disallow users from deleting any articles
      delete: false,
    },
  },
  admin: {
    article: {
      // Admins have blanket permissions to view and delete
      view: true,
      delete: true,
    },
  },
}

In this example, the view action for the article resource is evaluated dynamically for the user role. The function checks if the user is allowed to view the article based on the user’s ID and the article’s ID.

You can define more complex logic using any fields or properties available in the subject or data objects, making this library highly adaptable to various business rules.

API Reference

The package exports the following:

  • createHasPermission: A factory function to generate a hasPermission function.
  • AppliedPermissionsConfig: A helper type for defining your permissions object.