![Logo](/icon.png)
gittech. site
for different kinds of informations and explorations.
Pundit-ts: type-safe authorization library for Node.js
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:
https://github.com/user-attachments/assets/c7815622-c1c9-4fbe-986e-6c9f88c8b31d
Use Cases
- Apply RBAC, ABAC, DAC models (see the example snippets below for how to implement)
- 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 joins, specific
- 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.
Installation
npm i -S pundit-ts
π Access Control Models
Here some examples to utilize pundit-ts for applying common access control models like RBAC, ABAC etc..
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; // everyone, including anonymous users can view everything
default:
return false; // disallow every other action
}
}
}
Attribute-Based Access Control (ABAC)
class Policy {
authorize(ctx, object, action) {
// non logged-in users cannot perform any 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": // only admins can delete the record
return isAdmin;
// update permissions
case "update:content":
return isAdmin || isEditor;
case "update:title":
return isAdmin;
case "view:content":
case "view:title":
return true; // everyone can view title and content
default:
return false;
}
}
}
Discretionary Access Control (DAC)
Great choice for multi-tenant applications (ie. SaaS applications). Here we use organization
as our tenant.
class DocumentPolicy {
authorize(ctx, object, action) {
if (ctx.actor === null) {
return false; // 1. anonymous users cannot perform anything
}
const isOrganizationOwner = ctx.actor.id === object.organization.owner_id;
if (isOrganizationOwner) {
return true; // 2. organization owner can perform any action
}
// 3. check if the actor is member of the related organization
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;
// 4. Even combine with the Attribute-Based Access Control (ABAC) model
case "update:title":
return member.permissions.canUpdateDocumentTitle;
default:
return false;
}
}
}
π 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))
β¨οΈ Usage
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 extends PunditPolicy<PolicyContext, Post, PostActions> {
constructor() {
super(Post);
}
authorize(context, post, action) {
switch (action) {
case "create":
return true;
case "delete":
return false; // to be implemented...
case "update":
return false; // to be implemented...
}
}
filter(ctx) {
// modify context
}
}
export class UserPolicy extends PunditPolicy<PolicyContext, User, UserActions> {
constructor() {
super(User);
}
authorize(context, post, action) {
switch (action) {
case "create":
return true;
case "delete":
return false; // to be implemented...
case "update":
return false; // to be implemented...
}
}
filter(ctx) {
// modify context or return some filter object...
}
}
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");
Earn $100 Fast: AI + Notion Templates
Do you want to make extra money quickly? This guide shows you how to create and sell Notion templates step by step. Perfect for beginners or anyone looking for an easy way to start earning online.
Why Download This Guide?
- Start Making Money Fast: Follow a simple process to create templates people want and will buy.
- Save Time with AI: Learn to use tools like ChatGPT to design and improve templates.
- Join a Growing Market: More people are using Notion every day, and they need templates to save time and stay organized.
Includes Helpful Tools:
- ChatGPT Prompts PDF: Ready-made prompts to spark ideas and create templates faster.
- Checklist PDF: Stay on track as you work.
Whatβs Inside?
- Clear Steps to Follow: Learn everything from idea to sale.
- How to Find Popular Ideas: Research trends and needs.
- Using AI to Create: Tips for improving templates with AI tools.
- Making Templates User-Friendly: Simple tips for better design.
- Selling Your Templates: Advice on sharing and selling on platforms like Gumroad or Etsy.
- Fixing Common Problems: Solutions for issues like low sales or tricky designs.
Who Is This For?
- Anyone who wants to make extra money online.
- People who love using Notion and want to share their ideas.
- Creators looking for a simple way to start selling digital products.
Get your copy now and start making money today!
π° Want to Earn 40% Commission?
Join our affiliate program and start making money by promoting well crafter prodicts! Earn 40% on every sale you refer.
π Sign up as an affiliate here: Become an Affiliate