diff --git a/README.md b/README.md index 7c38dd6..2696fc4 100644 --- a/README.md +++ b/README.md @@ -39,15 +39,30 @@ fastify.route({ }) ``` +### Route options + +This plugin introduces new route option `casbin.rest`. It can be either a `true` value (which enables default configuration) or an object. +Supported object options: + +| Option | Type | Description | Default | +| -------- | ------------------- | -------------------------------- | --------------------------- | +| `getSub` | `Request => string` | Extracts `sub` from the request | Value from plugin options | +| `getObj` | `Request => string` | Extracts `obj` from the request | Value from plugin options | +| `getAct` | `Request => string` | Extracts `act` from the request | Value from plugin options | + +### Plugin options + The API exposed by this plugin is the configuration options: -| Option | Type | Description | Default | -| -------- | ----------------------------------------------------------- | ------------------------------------------------- | ------------------------------- | -| `getSub` | `Request => string` | Extracts `sub` from the request | `r => r.user` | -| `getObj` | `Request => string` | Extracts `obj` from the request | `r => r.url` | -| `getAct` | `Request => string` | Extracts `act` from the request | `r => r.method` | -| `onDeny` | `(Reply, sub, obj, act) => any` | Invoked when Casbin's `enforce` resolves to false | Returns a `403 Forbidden` error | -| `hook` | `'onRequest' | 'preParsing' | preValidation` | 'preHandler' | Which lifecycle to use for performing the check | 'onRoute' | +| Option | Type | Description | Default | +| -------- | ---------------------------------------------------------- | ------------------------------------------------- | ------------------------------- | +| `getSub` | `Request => string` | Extracts `sub` from the request | `r => r.user` | +| `getObj` | `Request => string` | Extracts `obj` from the request | `r => r.url` | +| `getAct` | `Request => string` | Extracts `act` from the request | `r => r.method` | +| `onDeny` | `(Reply, sub, obj, act) => any` | Invoked when Casbin's `enforce` resolves to false | Returns a `403 Forbidden` error | +| `hook` | `'onRequest', 'preParsing', 'preValidation', 'preHandler'` | Which lifecycle to use for performing the check | `'preHandler'` | + +Note that extraction rules defined within route options take precedence over the rules defined in the plugin options. ## Examples @@ -94,9 +109,11 @@ fastify.get( { // ensure user is authenticated preValidation: [fastify.authenticate], - // enable fastify-casbin-rest plugin on this route + // enable fastify-casbin-rest plugin on this route, override default "getObj" rule casbin: { - rest: true + rest: { + getObj: request => request.userId + }, } }, async () => `You're in!` diff --git a/plugin.js b/plugin.js index 2c55c7a..033666f 100644 --- a/plugin.js +++ b/plugin.js @@ -9,7 +9,8 @@ const defaultOptions = { getAct: request => request.method, onDeny: (reply, sub, obj, act) => { throw new Forbidden(`${sub} not allowed to ${act} ${obj}`) - } + }, + hook: 'preHandler' } async function fastifyCasbinRest (fastify, options) { @@ -21,7 +22,7 @@ async function fastifyCasbinRest (fastify, options) { // fastify-swagger for an example // i.e. enforceForAllRoutes if (routeOptions.casbin && routeOptions.casbin.rest) { - const hook = options.hook || 'preHandler' + const { hook } = options if (!routeOptions[hook]) { routeOptions[hook] = [] } @@ -29,10 +30,14 @@ async function fastifyCasbinRest (fastify, options) { routeOptions[hook] = [routeOptions[hook]] } + const getSub = routeOptions.casbin.rest.getSub || options.getSub + const getObj = routeOptions.casbin.rest.getObj || options.getObj + const getAct = routeOptions.casbin.rest.getAct || options.getAct + routeOptions[hook].push(async (request, reply) => { - const sub = options.getSub(request) - const obj = options.getObj(request) - const act = options.getAct(request) + const sub = getSub(request) + const obj = getObj(request) + const act = getAct(request) fastify.log.info({ sub, obj, act }, 'Invoking casbin enforce') diff --git a/test/casbinRest.test.js b/test/casbinRest.test.js index 09c0768..47fadc7 100644 --- a/test/casbinRest.test.js +++ b/test/casbinRest.test.js @@ -204,3 +204,41 @@ test('supports specifying custom hooks', t => { fastify.close() }) }) + +test('supports overriding plugin rules on route level', t => { + t.plan(4) + + const fastify = Fastify() + + fastify.register(makeStubCasbin()) + fastify.register(plugin, { + hook: 'onRequest', + getSub: request => request.user, + getObj: request => request.url, + getAct: request => request.method + }) + + fastify.get('/', { + casbin: { + rest: { + getSub: request => request.method, + getObj: request => request.user, + getAct: request => request.url + } + } + }, () => 'ok') + + fastify.ready(async err => { + t.error(err) + + fastify.casbin.enforce.callsFake((sub, obj, act) => { + t.equal(sub, 'GET') + t.equal(obj, undefined) + t.equal(act, '/') + return Promise.resolve(false) + }) + + await fastify.inject('/') + fastify.close() + }) +})