PitchHut logo
Pundit-TS
by fatihky
Editor's pick
Type-safe authorization library reminiscent of pundit.
Pitch

Pundit-TS offers a robust solution for organizing authorization logic with type safety. Inspired by the pundit gem, it simplifies role-based and attribute-based access control, ensuring that authorization rules are centralized and easy to manage. Ideal for TypeScript developers seeking clarity in their authorization practices.

Description

Pundit-TS: A Plain TypeScript Authorization Library

Pundit-TS is a robust authorization library designed to help developers organize authorization logic in a fully type-safe manner. Drawing inspiration from the Pundit gem, it offers a seamless way to implement authorization policies across various models and actions.

Key Features

  • Type Safety: Pundit-TS ensures all authorization checks are fully type-safe, reducing runtime errors and enhancing developer experience.
  • Flexible Models: Supports multiple access control models such as Role-Based Access Control (RBAC), Attribute-Based Access Control (ABAC), and Discretionary Access Control (DAC).
  • Easily Declared Actions: Define actions for different entity classes, making it straightforward to manage permissions on models like Users, Posts, and Categories.
  • Efficient Filtering: Optimize database queries by applying specific where clauses and joins based on user authorization, minimizing unnecessary data retrieval.
  • Centralized Logic: Keep authorization logic in a single location, enabling reuse and reducing code duplication across your application.

Use Cases

  • Confirm user permissions for actions on various entities (e.g., create, update, delete).
  • Implement premium user features, such as limiting the number of seats in an organization.

Examples

Pundit-TS caters to different use cases with examples:

  • Blog: A simple TypeScript blog example that is not tied to a database, making it a great starting point for newcomers.
  • Prisma Blog: An advanced version that utilizes Prisma ORM for database interactions, employing PostPolicy#filter to construct arguments for prisma.post.findMany calls.

Sample Code

Here are some implementation examples demonstrating how to manage access control:

Role-Based Access Control (RBAC)

class Policy {
  authorize(ctx, object, action) {
    const isAuthenticated = ctx.actor !== null;
    const role = ctx.actor?.role;
    const isAdmin = role === "admin";
    const isEditor = role === "editor";

    switch (action) {
      case "create":
        return isAdmin || isEditor;
      case "delete":
        return isAdmin;
      case "view":
        return true; // Accessible to all, including anonymous users
      default:
        return false; // Disallow other actions
    }
  }
}

Attribute-Based Access Control (ABAC)

class Policy {
  authorize(ctx, object, action) {
    if (ctx.actor === null) {
      throw new UnauthorizedError();
    }

    const role = ctx.actor.role;
    const isAdmin = role === "admin";
    const isEditor = role === "editor";

    switch (action) {
      case "delete":
        return isAdmin;
      case "update:content":
        return isAdmin || isEditor;
      case "view:content":
        return true;
      default:
        return false;
    }
  }
}

Discretionary Access Control (DAC)

Ideal for multi-tenant applications:

class DocumentPolicy {
  authorize(ctx, object, action) {
    if (ctx.actor === null) {
      return false; // Anonymous users cannot perform any action
    }

    const isOrganizationOwner = ctx.actor.id === object.organization.owner_id;
    if (isOrganizationOwner) {
      return true; // Organization owner can perform any action
    }

    const member = object.organization.members.findById(ctx.actor.id);
    if (!member) {
      return false;
    }

    switch (action) {
      case "create":
        return member.permissions.canCreateDocument;
      case "update":
        return member.permissions.canUpdateDocument;
      default:
        return false;
    }
  }
}

Simple Integration

To get started, create your models and actions, declare your policies, and encapsulate your authorization logic behind your PunditPolicy implementations. Here is an example:

// Create your Pundit instance:
import { Pundit } from 'pundit-ts';
import { PostPolicy, UserPolicy } from './policies';

const pundit = new Pundit<PolicyContext>()
  .register(new UserPolicy())
  .register(new PostPolicy());

Once integrated, authorization checks can be performed seamlessly:

const ctx = new PolicyContext();
const post = new Post();

await pundit.authorize(ctx, post, "create");

For more information, visit the Pundit-TS GitHub repository. This library offers developers a comprehensive, type-safe means to manage complex authorization logic.

0 comments

No comments yet.

Sign in to be the first to comment.