ProvenPin Install

casbin RBAC enforcer: working versions (tested 2026-10-17)

Build a casbin RBAC model from a string, add policies and roles in code, then assert allow, deny, role inheritance and dynamic policy removal decisions offline.

passed7 passed · 0 failed2026-10-17 05:58 UTC
Status
passed
Last passed
2026-10-17 05:58 UTC
Tests
7 / 7
Language
TypeScript
Runtime
node v22.23.3
pinned versions3
casbin@5.51.1@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 39008163431

The code

Exactly the files that passed. Your agent can pull them with get_recipe("casbin-rbac-enforcer") (install), or see them on GitHub.

package.json
{
  "name": "casbin-rbac-enforcer",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "casbin": "5.51.1"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15"
  }
}
src/rbac.ts
import { newEnforcer, newModelFromString, type Enforcer } from "casbin";

export const RBAC_MODEL = `
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act, eft

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
`;

export const ALLOW = "allow";
export const DENY = "deny";

export async function createEnforcer(): Promise<Enforcer> {
  const model = newModelFromString(RBAC_MODEL);
  const enforcer = await newEnforcer(model);

  await enforcer.addPolicy("alice", "data1", "read", ALLOW);
  await enforcer.addPolicy("alice", "data1", "write", ALLOW);
  await enforcer.addPolicy("data_admin", "data2", "read", ALLOW);
  await enforcer.addPolicy("data_admin", "data2", "write", ALLOW);
  await enforcer.addPolicy("banned", "data2", "read", DENY);

  await enforcer.addGroupingPolicy("bob", "data_admin");
  await enforcer.addGroupingPolicy("carol", "data_admin");
  await enforcer.addGroupingPolicy("dave", "carol");
  await enforcer.addGroupingPolicy("eve", "banned");

  return enforcer;
}

export async function can(
  enforcer: Enforcer,
  sub: string,
  obj: string,
  act: string,
): Promise<boolean> {
  return enforcer.enforce(sub, obj, act);
}
test/rbac.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { can, createEnforcer } from "../src/rbac.ts";

test("allows a direct policy match", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "alice", "data1", "read"), true);
  assert.equal(await can(e, "alice", "data1", "write"), true);
});

test("denies when no policy matches", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "alice", "data2", "read"), false);
  assert.equal(await can(e, "mallory", "data1", "read"), false);
});

test("resolves role inheritance for direct and nested roles", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "bob", "data2", "write"), true);
  assert.equal(await can(e, "carol", "data2", "read"), true);
  assert.equal(await can(e, "dave", "data2", "read"), true);
  assert.equal(await can(e, "dave", "data2", "write"), true);
});

test("applies a deny rule that overrides an inherited allow", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "eve", "data2", "read"), false);
  assert.equal(await can(e, "data_admin", "data2", "read"), true);
});

test("removes a policy at runtime and denies later requests", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "alice", "data1", "write"), true);

  const removed = await e.removePolicy("alice", "data1", "write", "allow");
  assert.equal(removed, true);
  assert.equal(await can(e, "alice", "data1", "write"), false);
  assert.equal(await can(e, "alice", "data1", "read"), true);

  const again = await e.removePolicy("alice", "data1", "write", "allow");
  assert.equal(again, false);
});

test("removes a grouping policy at runtime and revokes inherited access", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "bob", "data2", "read"), true);

  const removed = await e.removeGroupingPolicy("bob", "data_admin");
  assert.equal(removed, true);
  assert.equal(await can(e, "bob", "data2", "read"), false);
});

test("adds a policy at runtime and grants new access", async () => {
  const e = await createEnforcer();
  assert.equal(await can(e, "frank", "data3", "read"), false);

  const added = await e.addPolicy("frank", "data3", "read", "allow");
  assert.equal(added, true);
  assert.equal(await can(e, "frank", "data3", "read"), true);
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.