Skip to content

Fully type-safe pundit inspired authorization library in TypeScript.

License

Notifications You must be signed in to change notification settings

fatihky/pundit-ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pundit-TS - Plain Typescript Authorization Library

Pundit-TS is an authorization library highly-inspired by the pundit gem.

Pundit-TS is a fully type-safe `pundit` alternative.
Here how it auto-completes the actions based on the object you pass:
pundit-ts.demo.mov

Use Cases

  • Check if a user is authorized to perform an action on an entity (ie. Post, Product, Category etc..)
  • Declare actions can be performed per entity class basis
    • UserActions: create, update
    • PostActions: create, publish, unpublish, update, delete
    • CategoryActions: create, update, delete
  • Filter entities on database to avoid unnecessary database queries
    • Apply joins, specific where clauses or similar things to filter database rows.
  • Apply user tiers: only premium users can add more than 3 seats to their organization.
  • Avoid code duplication: Keep your authorization logic in one place. Use whenever you need them. Change one place to affect rest of the code.

Examples

Blog: Plain typescript blog example. Has no relation with a database. Great starting point if you are just starting to use pundit-ts

Prisma Blog: Prisma ORM based blog example. Advanced version of the plain blog example. Uses Prisma ORM for querying database, utilizes PostPolicy#filter for building argument for prisma.post.findMany method.

Usage

Install the package first:

npm i -S pundit-ts

Authorize users

Encapsulate your authorization logic behind your PunditPolicy implementations. Reuse those policies when you need. Manage your authorization logic from one place.

const currentUser = {}; // get user from cookies, headers, jwt etc...

// update post

// fetch post from db
const post = await prisma.post.findFirst({ where: { id: 123 } });

- if (post.authorId === currentUser.id) {
-   // update logic...
- }
+ if (await pundit.authorize(currentUser, post, 'update')) {
+   // update logic...
+ }

Filter entitites

Pundit-TS is a ORM-agnostic library. You may use your choice of ORM, query builder or anything.

-prisma.post.findMany({ /* your arguments  */ })
+prisma.post.findMany(pundit.filter(context, Post))

Declare your models.

// models.ts
class User {}

class Post {}

Declare your actions for each model.

// actions.ts
export type UserActions = "create" | "delete" | "update";
export type PostActions = "create" | "delete" | "update";

Declare your policies

// policies.ts
import { PunditPolicy } from "pundit-ts";
import { Post, User } from "./models";
import { PostActions, UserActions } from "./actions";

export class PolicyContext {
  // your orm related properties might go here
}

export class PostPolicy
  implements PunditPolicy<PolicyContext, Post, PostActions>
{
  async canCreate() {
    return false;
  }

  async canDelete() {
    return false;
  }

  async canUpdate() {
    return false;
  }

  handlesAction(action) {
    return action === "create" || action === "delete" || action === "update";
  }

  handlesModel(object) {
    return object instanceof User;
  }

  filter(ctx) {
    // modify context
  }
}

export class UserPolicy
  implements PunditPolicy<PolicyContext, User, UserActions>
{
  async canCreate() {
    return false;
  }

  async canDelete() {
    return false;
  }

  async canUpdate() {
    return false;
  }

  handlesAction(action) {
    return action === "create" || action === "delete" || action === "update";
  }

  handlesModel(object) {
    return object instanceof User;
  }

  filter(ctx) {
    // modify context
  }
}

Create your Pundit instance:

// pundit.ts
import { Pundit } from "pundit-ts";
import { PostPolicy, UserPolicy } from "./policies";

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

Authorize your actions in a fully type-safe way.

// index.ts
import { PolicyContext } from "./policies";
import { Post } from "./models";
import { pundit } from "./pundit";

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

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

About

Fully type-safe pundit inspired authorization library in TypeScript.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published