{"id":20968737,"url":"https://github.com/poowaa/aclatraz","last_synced_at":"2025-07-24T20:03:20.159Z","repository":{"id":40748700,"uuid":"340176335","full_name":"PoOwAa/aclatraz","owner":"PoOwAa","description":"Simple dependency-free module for ACL. It can handle hundreds of roles easily","archived":false,"fork":false,"pushed_at":"2023-03-05T21:37:43.000Z","size":2000,"stargazers_count":0,"open_issues_count":20,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-28T03:10:49.644Z","etag":null,"topics":["access-control","access-control-list","acl","allow","grant"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/aclatraz","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PoOwAa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-18T21:00:56.000Z","updated_at":"2021-07-14T18:12:40.000Z","dependencies_parsed_at":"2024-11-19T06:55:34.336Z","dependency_job_id":null,"html_url":"https://github.com/PoOwAa/aclatraz","commit_stats":{"total_commits":48,"total_committers":1,"mean_commits":48.0,"dds":0.0,"last_synced_commit":"9272427672e21612041c3a0198af0bc865da520c"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoOwAa%2Faclatraz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoOwAa%2Faclatraz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoOwAa%2Faclatraz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PoOwAa%2Faclatraz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PoOwAa","download_url":"https://codeload.github.com/PoOwAa/aclatraz/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243365586,"owners_count":20279212,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-control","access-control-list","acl","allow","grant"],"created_at":"2024-11-19T03:16:18.303Z","updated_at":"2025-03-13T07:43:28.477Z","avatar_url":"https://github.com/PoOwAa.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Aclatraz ![GitHub package.json dynamic](https://img.shields.io/github/package-json/keywords/PoOwAa/aclatraz)\n\n![example branch parameter](https://github.com/PoOwAa/aclatraz/actions/workflows/test.yml/badge.svg?branch=main)\n![Codecov](https://img.shields.io/codecov/c/github/PoOwAa/aclatraz?label=codecov\u0026logo=codecov)\n![npm](https://img.shields.io/npm/v/aclatraz?logo=javascript)\n![npm bundle size](https://img.shields.io/bundlephobia/min/aclatraz?logo=npm)\n\nSimple dependency-free package for ACL. It can handle hundreds of roles easily.\n\n## Features\n\n- dependency-free\n- built-in TypeScript support\n- can manipulate rules on the fly\n- the generated permission token can be saved on client side (for example in JWT token) to avoid database queries to verify permission\n- you can use as middleware in express/fastify/NestJS\n- unit tests, 100% coverage\n\n## Install\n\n`npm install aclatraz`\n\n## How it works\n\n1. Create the rules (they can be roles as well)\n2. Load them into Aclatraz\n3. Generate permission tokens based on rules\n4. Verify token\n\n## Documentation\n\n- [Examples](#Examples)\n  - [TypeScript example](#tsExample)\n  - [Express middleware](#expressMiddleware)\n  - [Fastify middleware](#fastifyMiddleware)\n  - [NestJS guard](#nestjsGuard)\n- [Interfaces](#interfaces)\n- [Methods](#Methods)\n  - [create new Instance](#constructor)\n  - [addRule](#addRule)\n  - [setRule](#setRule)\n  - [delRule](#delRule)\n  - [setOptions](#setOptions)\n  - [getRules](#getRules)\n  - [verify](#verify)\n  - [generateAclCode](#generateAclCode)\n  - [generateRuleTemplate](#generateRuleTemplate)\n  - [grantPermission](#grantPermission)\n  - [revokePermission](#revokePermission)\n\n## Examples\n\n\u003ca name=\"tsExample\"\u003e\u003c/a\u003e\n\n### TypeScript example\n\n```typescript\nimport { Aclatraz } from 'aclatraz';\n\n// Create an ACL instance. Don't forget to store\n// somewhere the rules (preferably in DB, or in a file)\nconst acl = new Aclatraz([\n  {\n    id: 1,\n    slug: 'READ_USER',\n  },\n  {\n    id: 2,\n    slug: 'SUPERADMIN',\n    name: 'Glorious superadmin rule',\n  },\n]);\n\n// Add new rule on the fly\nacl.addRule({\n  id: 3,\n  slug: 'CREATE_USER',\n  name: 'Protect user creation with this rule',\n});\n\n// User with this token can access id: 1 rule\nlet permissionToken = acl.generateAclCode([1]);\nconsole.log(acl.verify(permissionToken, 1)); // true\nconsole.log(acl.verify(permissionToken, 2)); // false\nconsole.log(acl.verify(permissionToken, 3)); // false\n\n// Grant superadmin permission to the user\npermissionToken = acl.grantPermission(permissionToken, [2, 3]);\nconsole.log(acl.verify(permissionToken, 1)); // true\nconsole.log(acl.verify(permissionToken, 2)); // true\nconsole.log(acl.verify(permissionToken, 3)); // true\n\n// Revoke the superadmin permission\npermissionToken = acl.revokePermission(permissionToken, [2]);\nconsole.log(acl.verify(permissionToken, 1)); // true\nconsole.log(acl.verify(permissionToken, 2)); // false\nconsole.log(acl.verify(permissionToken, 3)); // true\n```\n\n\u003ca name=\"expressMiddleware\"\u003e\u003c/a\u003e\n\n### How to use as an express middleware (here is a more detailed [example](examples/express-middleware/README.md))\n\n```js\nimport jwt from 'jsonwebtoken';\nimport express from 'express';\nimport { Aclatraz } from 'aclatraz';\n\nconst app = express();\n\n// Create the Aclatraz instance. Store the rules in redis/database etc.\nexport const acl = new Aclatraz([\n  {\n    id: 1,\n    slug: 'ADMIN',\n  },\n  {\n    id: 2,\n    slug: 'USER',\n  },\n  {\n    id: 3,\n    slug: 'READ_OTHER_USERS',\n  },\n  {\n    id: 4,\n    slug: 'CREATE_USER',\n  },\n]);\n\n// Create a guard which takes the permission from JWT\nfunction permissionGuard(...permissionList) {\n  return (req, res, next) =\u003e {\n    // Get the authorization header\n    const { authorization } = req.headers;\n\n    if (!authorization) {\n      return res.sendStatus(401);\n    }\n\n    // Get the token from the Bearer token\n    const [_, token] = authorization.split(' ');\n\n    // Decode the token\n    const user = jwt.decode(token, 'JWTSECRET');\n\n    // The Aclatraz permission token stored under user.permission in this example\n    const permission = user.permission;\n\n    // Check if the user permission matches any of the rules\n    let permissionGranted = false;\n    for (const ruleId of permissionList) {\n      if (acl.verify(permission, ruleId)) {\n        permissionGranted = true;\n        break;\n      }\n    }\n\n    // No match, then forbidden\n    if (!permissionGranted) {\n      return res.sendStatus(403);\n    }\n\n    // There is a match, we can let the user in\n    next();\n  };\n}\n\n// Actual endpoint. The second parameter is our guard as a middleware, where we can define the rules by their ID\napp.get('/guardedEndpoint', permissionGuard(1, 2), (req, res) =\u003e {\n  res.send('Nice! You have permission to see this!');\n});\n\napp.listen(3000, () =\u003e {\n  console.log('app is listening...');\n});\n```\n\n\u003ca name=\"fastifyMiddleware\"\u003e\u003c/a\u003e\n\n### How to use as a fastify middleware\n\n```js\nimport jwt from 'jsonwebtoken';\nimport fastify from 'fastify';\nimport { Aclatraz } from 'aclatraz';\n\nconst app = fastify({\n  logger: true,\n});\n\n// Create the Aclatraz instance. Store the rules in redis/database etc.\nexport const acl = new Aclatraz([\n  {\n    id: 1,\n    slug: 'ADMIN',\n  },\n  {\n    id: 2,\n    slug: 'USER',\n  },\n  {\n    id: 3,\n    slug: 'READ_OTHER_USERS',\n  },\n  {\n    id: 4,\n    slug: 'CREATE_USER',\n  },\n]);\n\n// Create a guard which takes the permission from JWT\nfunction permissionGuard(...permissionList) {\n  return (request, reply, done) =\u003e {\n    // Get the authorization header\n    const { authorization } = request.headers;\n\n    if (!authorization) {\n      return reply.status(401).send('Unauthorized');\n    }\n\n    // Get the token from the Bearer token\n    const [_, token] = authorization.split(' ');\n\n    // Decode the token\n    const user = jwt.decode(token, 'JWTSECRET');\n\n    // The Aclatraz permission token stored under user.permission in this example\n    const permission = user.permission;\n\n    // Check if the user permission matches any of the rules\n    let permissionGranted = false;\n    for (const ruleId of permissionList) {\n      if (acl.verify(permission, ruleId)) {\n        permissionGranted = true;\n        break;\n      }\n    }\n\n    // No match, then forbidden\n    if (!permissionGranted) {\n      return res.status(403).send({\n        status: 'error',\n        message: 'No permission granted for this endpoint',\n      });\n    }\n\n    // There is a match, we can let the user in\n    done();\n  };\n}\n\n// Actual endpoint. The second parameter is our guard as a middleware using onRequest hook, where we can define the rules by their ID\napp.get(\n  '/guardedEndpoint',\n  {\n    onRequest: permissionGuard(1, 2),\n  },\n  (request, reply) =\u003e {\n    reply.send('Nice! You have permission to see this!');\n  }\n);\n\napp.listen(3000, (err) =\u003e {\n  if (err) {\n    app.log.error(err);\n    process.exit(1);\n  }\n});\n```\n\n\u003ca name=\"nestjsGuard\"\u003e\u003c/a\u003e\n\n### How to use as a NestJS guard\n\n_Soon..._\n\n## Interfaces\n\n```typescript\ninterface AclRule {\n  id: number;\n  name?: string;\n  slug: string;\n}\n\ninterface AclConfig {\n  chunkSize: number;\n  encoding: number;\n  padding: number;\n  paddingChar: string;\n}\n\ninterface AclRuleTemplate {\n  [key: number]: {\n    slug: string;\n    name?: string;\n  };\n}\n```\n\n## Methods\n\n\u003ca name=\"constructor\"\u003e\u003c/a\u003e\n\n### `constructor(aclRules?: AclRule[], options?: Partial\u003cAclConfig\u003e)`\n\nCreate a new Aclatraz instance.\n\n---\n\n\u003ca name=\"addRule\"\u003e\u003c/a\u003e\n\n### `addRule(aclRule: AclRule): void;`\n\nAdd a rule.\n\n---\n\n\u003ca name=\"setRule\"\u003e\u003c/a\u003e\n\n### `setRule(id: number, aclRule: Partial\u003cAclRule\u003e): void;`\n\nUpdate a rule by its ID.\n\n---\n\n\u003ca name=\"delRule\"\u003e\u003c/a\u003e\n\n### `delRule(id: number): void;`\n\nDelete a rule.\n\n---\n\n\u003ca name=\"setOptions\"\u003e\u003c/a\u003e\n\n### `setOptions(aclConfig: Partial\u003cAclConfig\u003e): void;`\n\nUpdate the Aclatraz options.\n\n---\n\n\u003ca name=\"getRules\"\u003e\u003c/a\u003e\n\n### `getRules(): AclRule[];`\n\nList the rules.\n\n---\n\n\u003ca name=\"verify\"\u003e\u003c/a\u003e\n\n### `verify(permission: string, ruleId: number): boolean;`\n\nCheck if the given permission token has access to the rule.\n\n---\n\n\u003ca name=\"generateAclCode\"\u003e\u003c/a\u003e\n\n### `generateAclCode(ruleIdList: number[]): string;`\n\nGenerate the permission token from the given rules.\n\n---\n\n\u003ca name=\"generateRuleTemplate\"\u003e\u003c/a\u003e\n\n### `generateRuleTemplate(): string;`\n\nSoon...\n\n---\n\n\u003ca name=\"grantpermission\"\u003e\u003c/a\u003e\n\n### `grantPermission(currentPermission: string, ruleList: number[]): string;`\n\nGrant permission to an existing token. You can use this to create a new one, give empty string as currentPermission.\n\n---\n\n\u003ca name=\"revokePermission\"\u003e\u003c/a\u003e\n\n### `revokePermission(currentPermission: string, ruleList: number[]): string;`\n\nRevoke permission from an existing token.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoowaa%2Faclatraz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoowaa%2Faclatraz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoowaa%2Faclatraz/lists"}