0.1.5 • Published 4 years ago

koa2-rbac-router v0.1.5

Weekly downloads
-
License
MIT
Repository
bitbucket
Last release
4 years ago

koa2-rbac-router

Koa2 router middleware with integrated role-based access control.

  • Classic routes definition using router.get, router.post, router.put, router.delete, etc.
  • Named URL parameters (with configurable marker).
  • Named routes (actions).
  • Multiple routers.
  • Nestable routers / middlewares.
  • ES7 async request handlers.
  • Out-of-box simple but yet flexible role-based access control (RBAC).
  • MIT License.

Intro

Main goal of this lib is to provide flexible Koa2 router with integrated role-based access control (RBAC). Basic idea is simple: to perform access control route should be defined with name (named routes are called 'actions'). Router automatically matches access to the route with RBAC definitions using route name as RBAC permission (ability) identifier. RBAC role, in general, is a simple list of accepted actions (route names). When request arrives router fetches context role(s) (through special callback function ctxRolesFetcher) and resolves them into list of allowed actions (route names), then verifies if current route name exists in resolved actions list. If action is allowed router passes request to downstream handlers or returns 403 Forbidden HTTP error otherwise. Additionally, RBAC supports roles inheritance and actions/roles exclusion.

For example:

RBAC.setup({
  // define `guest` role
  guest: 'index, signup, signin',
  // define common `user` role
  user: '@guest, ownAction, !signup, !signin'
});

Example above defines:

  • guest role with allowed actions: index, signup and signin;

  • user role which 'inherits' role guest (with @guest construct), defines own permission: ownAction and excludes permissions signup and signin inherited from guest role. Resulting user role permissions are: index and ownAction.

Roles might be excluded as well with construction !@<role>, what means exclusion of all <role> permissions recursively: permissions inherited by <role> get excluded as well.

Futhermore, any dependend role will be automatically adjusted if inherited role gets updated, for example above:

RBAC.apply('guest', 'index, signup, signin, welcome');

adjusts user role as well, and its resulting permissions set becomes: index, welcome, and ownAction.

Since roles definitions are simple strings (or arrays) it's easy to store them in files, DBs or any other storages: it's up to a programmer how to handle this. Same is about storing request context roles which are expected to be strings of space and/or comma separated list of role names (or array of role names). Obvious solution here is to use a session storage.

Installation

  • npm:
npm install koa2-rbac-router
  • yarn:
yarn add koa2-rbac-router

API Reference

  • Router

    • new Router ([opts])

    • Instance members

      • map(descriptor) => Router map([name], mapping, handler) => Router
      • get|post|put|delete|...|all([name], path, handler) => Router
      • use(prefix, mw) => Router|Function
    • Static members

      • Error
      • CTX_ACTION
      • CTX_PARAMS
      • HTTP_VERBS
      • PARAM_MARK
      • PATH_DELIM
  • Role-based access control (RBAC)

    • build([force=false]) => RBAC
    • imply(name, spec) => RBAC
    • match(perm, roles) => Boolean
    • resolve(name) => Set<String>
    • setup(specs, [prebuild=true]) => RBAC
    • unset(name) => RBAC
    • Error
    • EXCLUDE_MARK
    • ROLE_REF_MARK
    • RX_DELIMITER
  • Example


Router

Exported class.

new Router([opts])

Create a new router instance.

  • opts Object optional

    Router instance configuration properties:

    • ctxRolesFetcher [Async]Function optional

      Context roles fetcher. Automatic RBAC checks are disabled if this routine not set.

      Call signature:

        [async] function (ctx) {
            // example:
            return ctx.session.roles;
        }
  • prohibitHandler [Async]Function optional

    Request prohibit handler (see RBAC). By default (when this option is omitted) ctx.throw(403) is used.

    Call signature:

        [async] function (ctx) {
            // example:
            ctx.body = 'Access denied';
            ctx.throw(403);
        }
  • preambleHandler [Async]Function | Array<[Async]Function> optional

    Function (or array of functions) to be invoked before any request handlers in call chain.

    Call signature:

      [async] function (ctx, next) { ... }
  `next` is _asynchronous_ downstream invokation routine:
  
      async function (ctx, next) {
          // preprocess request
          ...
          // await for downstream handlers
          await next();
          // postprocess request
          ...
      }
  • notFoundHandler [Async]Function optional

    Request route not found handler.

    Call signature:

    [async] function (ctx) {
        // default behaviour:
        ctx.throw(404);
    }
  • noMethodHandler [Async]Function optional

    Request method not found handler. By default opts.notFoundHandler routine is used (see above).

    Call signature:

    [async] function (ctx) {
        // example:
        ctx.throw(501); // return `Not Implemented` HTTP error
    }

NOTE: [async] notation means optionally asynchronous.

Instance members

map(descriptor) => Router map([name], mapping, handler) => Router

Define new route.

  • descriptor Object

    Route descriptor object of following options:

  • name String optional

    Unique (in scope of all Router instances) route name, which is used for matching access permissions (see RBAC below). Unnamed routes are handled unrestricted (with no RBAC checks).

  • mapping String required

    Route mapping of form: '[<METHOD>] <PATH>', where:

    • <METHOD> is HTTP method name: GET, POST, PUT, DELETE, etc; omitted or specified as '*' value means wildcard or default (fallback) HTTP method handler.
    • <PATH> is route path of form { /<chunk>|:<param> }, where: <chunk> is path chunk and :<param> is path named parameter (additionally, see PARAM_MARK).

      Important: route params MUST be same-named in same route paths. For example, following mappings cause parameters collision error, due to attempt of use different names for same parameter:

      'GET /items/:serNum'

      'PUT /items/:serNum'

      'DELETE /items/:itemId' - ERROR: parameter should be :serNum as in other routes of path /items.

  • handler [Async]Function | Array<[Aync]Function> required

    Asynchronous (optionally) request handling routine (or array of routines).

    Call signature:

    [async] function (ctx, next) { ... }

    next is asynchronous downstream invocation routine:

    async function (ctx, next) {
        // preprocess request
        ...
        // await for downstream handlers
        await next();
        // postprocess request
        ...
    }

    Important: Before calling request specific handler(s) router invokes its opts.preambleHandler if it was configured (see new Router([opts])).

router.get|post|put|delete|...|all([name], path, handler) => Router

Route classic definition helpers. Functions determine corresponding route HTTP methods. The list of exposed functions is specified by Router.HTTP_VERBS parameter.

  • name - see map(...) name

  • path - see map(...) mapping <PATH>

  • handler - see map(...) handler

router.use(prefix, target) => Router | Function

Mount sub-router or middleware function on specified prefix.

  • prefix String required

    Target mount point. This prefix will be cut off from ctx.path before passing control to downstream handlers.

    NOTE: at the moment parametrized prefixes are not supported.

  • target Router | [Async]Function required

    Target Router instance or Koa2 common middleware function to be used on specified prefix.

Important: Sub-routers are not enforced to define own config options with new Router([opts]). Each subrouter recursively 'inherits' unspecified config options from its parent router(s).

Static members

Router.Error class

Router-specific error class.

Router.CTX_ACTION String

Koa context property containing request action (route name). Default: 'action'.

Router.CTX_PARAMS String

Koa context property containing request path parameters. Default: 'params'.

Router.HTTP_VERBS Array<String>

List of HTTP verbs to expose as old-style Router methods. Default: ['get', 'post', 'put', 'delete'].

Router.PARAM_MARK String

Route parameter marker char. Default: ':'.

Router.PATH_DELIM String|RegExp

Delimiter used for splitting route path into chunks. Default: /\/+/.


Role-based access control (RBAC)

Following methods, classes and options are members of RBAC namespace.

RBAC.build([force=false]) => RBAC

Preprocess and compile roles (see setup(...)).

  • force Boolean optional

    All roles re-compilation forcing flag.

RBAC.apply(name, spec) => RBAC

Apply role (new) spec, initiate dependent roles recompilation.

  • name String required

    Role name.

  • spec String|Array<String> required

    Role spec in one of following formats:

    • String: space and/or comma separated list of spec tokens;

    • Array<String>: array of spec tokens.

    Spec tokens could be:

    • route action (named route) inclusion, meaning that specified action is permitted by the role;

    • another role inclusion, marked by role reference mark (see ROLE_REF_MARK, default: @), meaning recursive inclusion of all actions from specified role;

    • exclusion of action or role, marked by exclude mark (see EXCLUDE_MARK, default: !), meaning removal of an action or recursive removal of all actions from specified role (marked as: !@excludedRoleName).

    Important: appearance order of tokens in role spec DOES matter: precedence grows from left to right, what means, for example, inclusion of role that permits (contains) action someAction after exclusion of this action (!someAction) leads to presense of someAction in subject role.

RBAC.match(action, roles) => Boolean

Check if action is permitted by specified role(s).

  • action String required

    Action name to match.

  • roles String|Array<String> required

    Single role name or space and/or comma separated list or array of roles to match.

This method is used internally by Router to match access permissions (see ctxRolesFetcher).

RBAC.resolve(name) => Set<String>

Resolve role to a set of permitted actions.

  • name String required

    Name of role to resolve.

RBAC.setup(specs, [prebuild=true]) => RBAC

Reset RBAC controller with provided roles specs batch.

  • specs Object required

    Role spec object, where property name is treated as role name and value as role spec (see RBAC.apply).

  • prebuild Boolean optional

    Roles prebuild flag (default: true). If set to false none role spec is preprocessed until RBAC.resolve(...) or RBAC.build() invoked.

RBAC.unset(name) => RBAC

Undefine role and initiate dependent roles recompilation.

RBAC.Error

RBAC-specifix error class.

RBAC.EXCLUDE_MARK

Action/role exclude mark. Default: !.

RBAC.ROLE_REF_MARK

Role reference mark. Default: @.

RBAC.RX_DELIMITER

Spec delimiter regexp. Default: /[,\s]+/.

Tests

npm run test

or

yarn test

Tests with coverage:

npm run test+cover

or

yarn test+cover

Example

Please visit koa2-rbac-router-example

License and Copyright

This software is a subject of MIT License:

Copyright 2019 Igor V. Dyukov

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0.1.5

4 years ago

0.1.4

5 years ago

0.1.3

5 years ago

0.1.2

5 years ago

0.1.1

5 years ago

0.1.0

5 years ago