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

@sapl/sapl-nodejs

v0.1.6

Published

SAPL Node.js library

Downloads

100

Readme

sapl-nodejs

This project provides a client that enables the integration of SAPL in NodeJs.

What is SAPL

SAPL is a further development of ABAC that uses a publish/subscribe model in addition to the classic request-response model.

When a client sends a authorization subscription to the Policy Decision Point (PDP), it receives either a simple response or a data stream of decisions. If the result of the authorization decision changes, this is communicated immediately via the data stream.

What SAPL looks like in Node.js

The SAPL-Node.js package handles the enforcement of policies and related decisions in the background, allowing developers to focus on policy creation. Comprehensive SAPL documentation can be found here, including a playground for writing policies.

Installation preparations

Install an appropriate NodeJs version on your device.

Installation

# install SAPL for NodeJs

npm i @sapl/sapl-nodejs

Usage

Prerequisite for using SAPL in NodeJs

A prerequisite for using SAPL in NodeJs is one of the SAPL servers listed:

  • Server LT
  • Server CE
  • Server EE (This is made available via sapl.io and demos can be requested)

Decorators

The SAPL client provides 5 decorators that can be used in the coding.

PreEnforce

The @PreEnforce annotation establishes a policy enforcement point (PEP) before the invocation of the annotated method.

PostEnforce

The @PostEnforce annotation establishes a policy enforcement point (PEP) after the invocation of the annotated method, and alters the return value if indicated by the policy decision point (PDP), if possible based on the structure of the returned JSON object.

EnforceTillDenied, EnforceDropWhileDenied and EnforceRecoverableIfDenied

The @EnforceTillDenied, @EnforceDropWhileDenied and @EnforceRecoverableIfDenied annotations establishes a policy enforcement point (PEP) for data streams. The PEP is only applicable to methods returning a

For a more detailed explanation of the decorators, please refer to the SAPL documentation.

Use of decorators

The decorators supplied are method decorators. In order for the decorators to work properly, the PDP must be instantiated in the form of a remote PDP.

public pdp: RemotePdp;

this.pdp = RemotePdp
            .create()
            .host('https://localhost')
            .port(8443)
            .setAuthorizationManager("http://localhost:3000/user");

After the instantiation of the PDP, it can be used for specific credentials or users.

// simple example of a login routine
if (user) {
    const userAuthorizationData = require("../db/user_auth.json");
    const userAuth = userAuthorizationData.users.find(
        (u) => u.username === user.username
      );

    serviceHandler
      .getRemotePdp()
      .bearerToken(userAuth.sapl_token) // token for SAPL server authorization
      .setUsername(user.username); // username for name of the subject

    // alternative variant if not accessed via token
    serviceHandler
      .getRemotePdp()
      .basicAuth("username", "password")
}

Authorization Manager

As already shown earlier the PDP can also be provided with the user's authorizations. The Authorization Manager must be set for this.

this.pdp.setAuthorizationManager('http://localhost:3000/user');

The result of the service should be an array with the authorizations.

HTTP/1.1 200 OK
X-Powered-By: Express
Vary: Origin
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
Content-Length: 33
ETag: W/"21-5BRwB+FgCzXfa3iTJJj0hquEbIo"
Set-Cookie: connect.sid=s%3ALUXSqk0u2pubkx_uvKX-Keqsqax_hjJ9.%2BgPAazqp0k6BsBDgNX8QCTw15stFRaHL4jAeJrnQ1%2B8; Path=/; HttpOnly
Date: Fri, 28 Jun 2024 12:50:08 GMT
Connection: close

[
  "admin",
  "god",
  "editor",
  "viewer"
]

Using the PDP outside the decorators

The PDP can also be used outside the decorators. This can be referred to as "manual" use.

// decide example
this.pdp
    .decide(AuthorizationSubscription.create('user1', 'read', 'resource1'))
    .then((response) => {
        //do something
        console.log(response);
    });

Various API paths can be addressed via the corresponding methods:

  • decide
  • decideOnce
  • multiDecide

For a more detailed explanation of the different API endpoints, please refer to the SAPL documentation.

Constraint Handling

Upon receipt of the decision from the PDP, it can be linked to constraints that can be categorized either as obligations or as advice. obligations must be fulfilled, otherwise an PERMIT will be a DENY. Advices should be fulfilled but have no influence on the decision made, even if they are not fulfilled.

To handle these constraints, the library provides a class called ConstraintEnforcementService that can manage them. This class is used to register the implemented constraint handler.

this.pdp
    .getConstraintEnforcementService()
    .addOnNextConstraintHandler(new LogConstraintHandlerProvider())
    .addOnErrorConstraintHandler(new EmailConstraintHandlerProvider())
    .addMappingConstraintHandler(new CustomReplaceHandlerProvider());

Depending on the point at which you want to hook in, the corresponding interface must be used. Here are a few example implementations.

import { ConsumerConstraintHandlerProvider } from '@sapl/sapl-nodejs';

export class LogConstraintHandlerProvider
  implements ConsumerConstraintHandlerProvider
{
    isResponsible(constraint: any): boolean {
        if (constraint['type'] === 'logAccess') {
            return true;
    } else {
        return false;
    }
  }

  getHandler(constraint: object) {
      return () => {
          this.log(constraint['message']);
    };
  }

  log(message: string) {
      console.log(message);
  }
}
import { ErrorHandlerProvider } from '@sapl/sapl-nodejs';

export class EmailConstraintHandlerProvider implements ErrorHandlerProvider {
  isResponsible(constraint: any): boolean {
    if (constraint['type'] === 'sendMail') {
      return true;
    } else {
      return false;
    }
  }

  getHandler(constraint: object) {
    return () => {
      this.sendEmail(
        constraint['recipient'],
        constraint['subject'],
        constraint['message'],
      );
    };
  }

  sendEmail(recipient: string, subject: string, message: string) {
    console.log(
      'Sending email to ' +
        recipient +
        ' with subject ' +
        subject +
        ' and message ' +
        message,
    );
  }
}
import { HasPriority } from '@sapl/sapl-nodejs';
import { MappingConstraintHandlerProvider } from '@sapl/sapl-nodejs';

export class CustomReplaceHandlerProvider
  implements MappingConstraintHandlerProvider
{
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  getHandler(constraint: object) {
    const customReplaceConfig = constraint;
    return (content) => {
      return this.customReplaceHandler(content, customReplaceConfig);
    };
  }
  isResponsible(constraint: object): boolean {
    if (constraint['type'] === 'customReplace') {
      return true;
    } else {
      return false;
    }
  }
  getPriority(): number {
    return 0;
  }
  compareTo(other: HasPriority): number {
    return other.getPriority() < this.getPriority()
      ? -1
      : other.getPriority() === this.getPriority()
        ? 0
        : 1;
  }

  customReplaceHandler(content: any, constraint: object) {
    content.data.diagnosis = constraint['replacement'];
    return content;
  }
}

For a more detailed explanation of how constraints work, please refer to the SAPL documentation.