npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@opuscapita/bouncer

v1.3.0

Published

API and express middleware for OpusCapita ACl service based access security.

Downloads

848

Readme

Expect this library to be split into front-end and back-end implementations in the near future

@opuscapita/bouncer

OpusCapita bouncer JS library provides security APIs interface for Andariel platform.

Bouncer can be used to define two different types of resources:

  • REST (Server side resources)
  • UI (Client side resources)

Using Bouncer

  • In order to use Bouncer in your project, you can either use Bouncer directly as an express middleware or through web-init.
  • For using Bouncer directly via API, please have a look at the API documentation wiki.

To use Bouncer with express, simply use the prepared middleware:

const Bouncer = require('@opuscapita/bouncer');

const bouncer = new Bouncer();
await bouncer.registerPermissions(); // Use this only on the initial startup as it will register all local permissions via Andariel event at the platform.

const app = express();
app.use(bouncer.middleware());

Bouncer requires the opuscapita namespace to be available inside the request object passed to the middleware by express. It ueses opuscapita.logger, opuscapita.serviceClient and opuscapita.userData() which, if used, also requires useridentity-middleware.


req.opuscapita methods

Bouncer extends the req.opuscapita namespace with several, request bound methods where some of which can only be used if useridentity-middleware is available.

Returns a list of users that have access to a certain resource group (permission) on a certain tenant.

req.opuscapita.getUsersByPermissionAndTenant(serviceName, resourceGroupId, tenantId) : Promise

Returns an array of tenants a user has access to on the current endpoint.

req.opuscapita.getUserTenants() : Promise

Returns an array of tenants a user has access to on a specific endpoint even in a foreign service.

req.opuscapita.getUserTenantsByUrl(url, serviceName = null) : Promise

Returns an array of objects containing either a supplierId or a customerId field depending on whenever a tenant is a supplier or a customer.

req.opuscapita.splitUserTenants(tenants)

The getUserTenants() functions are using the xroles feature of the user token (jwt) in order to provide this functionality. If the token is not available e.g. on a public resource, getUserTenants() will always return an empty array.

As of October 2018, a user's tenants can be configured using the user role editor in the BNP front end.

Usage example
async function myEndpoint(req, res)
{
    // Get all user tenants for the current endpoint.
    const tenants = await req.opuscapita.getUserTenants();

    // If the tenants array contains a *, access to all tenants is granted.
    if(tenants.includes('*'))
    {
        res.json({ message : 'Wildcard access granted.' });
    }
    else
    {
        // Split the list of tenants into customer and supplier IDs.
        // Returns [ { supplierId, customerId }, { supplierId, customerId }, ... ]
        const split = req.opuscapita.splitUserTenants(tenants);

        // Filter and map down to the raw IDs.
        const supplierIds = split.filter(split => split.supplierId).map(split => split.supplierId);
        const customerIds = split.filter(split => split.customerId).map(split => split.customerId);

        res.json({ supplierIds, customerIds });
    }
}

Defining REST resource groups for Bouncer.

By default, Bouncer tries to load the src/server/acl.json file to get its resource groups. The file has to be in JSON format and should contain the following structure:

{
    "my-resource-group-read": {
        "name": {
            "en": "My resource group.",
            "de": "Meine Resource Group."
        },
        "description": {
            "en": "A full description of my resrouce group e.g. for UI permission editors.",
            "de": "Eine komplette Beschreibung meiner Resource Group z.B. für UI Editoren."
        },
        "resources": [{
            "type": [ "rest", "ui" ],
            "resourceId": "^/api/myEndpoint$",
            "actions": ["view"],
            "requestFields": {
                "allow": ["field1", "field3", "field4"]                
            },
            "responseFields": {
                "allow" : ["field1", "field2"]
            }
        }]
    },
    "my-resource-group-write": {
        "name": {
            "en": "My resource group.",
            "de": "Meine Resource Group."
        },
        "description": {
            "en": "A full description of my resrouce group e.g. for UI permission editors.",
            "de": "Eine komplette Beschreibung meiner Resource Group z.B. für UI Editoren."
        },
        "resources": [{
            "type": "rest",
            "resourceId": "^/api/myEndpoint(/.*)?$",
            "actions": ["create", "edit"],
            "requestFields": {
                "remove": ["createdBy", "updatedBy", "createdAt", "updatedAt"]                
            },
            "responseFields": {
                "allow" : ["field1", "field2"]
            }
        }]
    },
    "foreign-service-group": {
        "service": "foreign-service",
        "name": {
            "en": "Foreign resource group.",
            "de": "Fremde Resource Group."
        },
        "description": {
            "en": "A full description of my resrouce group e.g. for UI permission editors.",
            "de": "Eine komplette Beschreibung meiner Resource Group z.B. für UI Editoren."
        },
        "resources": [{
            "type": "rest",
            "resourceId": "^/api/enpoint$",
            "actions": ["create"],
            "requestFields": {
                "remove": ["createdBy", "updatedBy", "createdAt", "updatedAt"]                
            }
        }]
    }
}

The first level of this structure defines the actual resource group. The key set here is the so called resourceGroupId.

On the second level you have the resource group's readable name and description. Both translated to all languages supported by the system. If the service property is defined, this resource group will be assigned to the service named there and will not be used for the local service. This feature allows defining resource groups for other services.

The same level defines the resources (endpoints) the resource group applies to. These resources can be defined using type, resourceId, actions, [requestFields [allow], [remove]], [responseFields [allow], [remove]], [service]. The type field can either define "rest" or "ui" types or both as an array definition ["rest", "ui"]. The last three are optional. The resourceId field is used to describe the actual endpoint using RegExp.

The resource group Actions can either be create (POST), view (GET), edit (PUT), delete (DELETE), head (HEAD), options (OPTIONS), patch (PATCH). The requestFields and responseFields definition can contain a list of allowed (whitelisted) or removed (blacklisted) fields. These functionality only applies to objects and arrays of objects. Keys can also describe nested properties e.g. 'key1.key2.key3'. Blacklisted keys will always outweigh whitelisted.

ATTENTION: In case a user has access to multiple resource groups within the same service and the requested resource matches multiple resource groups, these groups get merged internally in order to grant the sum of all permissions on that resource.


Defining UI resource groups for Bouncer.

By default, Bouncer tries to load the src/server/acl.json file to get its resource groups. The file has to be in JSON format and should contain the following structure. The main differences to the REST resource definition is located in the type property and the not existing requestFields and responseFields properties. If desired, you can also define the type as an array containing both "rest" and "ui" (["rest", "ui"]).

{
    "my-resource-group": {
        "name": {
            "en": "My resource group.",
            "de": "Meine Resource Group."
        },
        "description": {
            "en": "A full description of my resrouce group e.g. for UI permission editors.",
            "de": "Eine komplette Beschreibung meiner Resource Group z.B. für UI Editoren."
        },
        "resources": [{
            "type": "ui",
            "resourceId": "^/(?!api).*",
            "actions": ["view", "create", "edit", "delete"]
        }]
    }
}

Services required to use Bouncer

In order to use Bouncer, the following additional services have to be available in a productive environment.

  • Consul
  • ACL
  • RabbitMQ

For detailed service definitions, please have a look at the docker-compose.yml file.


Consul keys required

The following consul configuration (kv) keys have to be set in order to use bouncer:

  • mq/user
  • mq/password

Events raised by Bouncer

  • bouncer.permissionsReady

Events subscribed by Bouncer

None


Default configuration

{
    serviceName : config.serviceName,
    permissions : process.cwd() + '/src/server/acl.json',
    aclServiceName : 'acl',
    logger : new Logger(),
    roles : {
        alwaysAllow : [ ],
        alwaysDeny : [ ]
    },
    publicPaths : [
        '^/public',
        '^/static',
        '^/api/health/check$',
        '^/api/list/apis$'
    ]
}