npm package: @leisurelink/claims
Description: LeisureLink's object model and utilities for querying security claims in nodejs.
Author: Phillip Clark
Latest version: 1.0.6

# `claims`

LeisureLink's object model and utilities for querying security claims in nodejs.

## Claims Based Security

In the context of LeisureLink's federated security, the term `Claim` refers to a statement that a `security principal` makes about itself or another `security principal`. Every user of the system, whether a human user or a system user, is authenticated as a `security principal`, and access-control, where applicable, relies upon a principal's claims.

## Security Principals

Presently, there are two types of _security principals_; `user principals` and `endpoint principals`.

*_User principals_* represent the human users that log in to our websites, applications, and APIs and otherwise use our services.

*_Endpoint principals_* represent system users. Each of our internal API's, websites, hubs, applications, CLIs, as well as external systems that connect with us are represented in the security system as _endpoint principals_.

## Claimsets

Within the security system, _claims_ are organized into _claimsets_. A _claimset_ is a collection of _claims_ defined in a [`claimset specification`](#user-content-claimset-specification) and administered by a [`claims commissioner`](#user-content-claims-commissioner).

Claimsets and their constutuent claims are identified by the claimset's identfier (`csid`).

## Claims

`Claims` are trusted, system-wide data points attributed to a _principal_. Logically, they are similar to _key-value_ pairs; they have two parts:

* `clid` - the claim's unique identifier
* `value` - the claim's value

### `clid` (Claim Identifiers)

LeisureLink's federated security is a distributed authority system. This means that there is not a single service that catalogues the entirety of a principal's claims. Instead, various services within the operating environment perform 
the role of `claims commissioner` and proffer subsets of a principal's claims to the system. This scheme enables _seperation of concern_ in the security system; e.g. the `pmc-account-api` is responsible for associating principals 
with PMCs, and establishing a principal's permissions for conducting operations against rental properties. One of the mechanisms that enables the system to understand the separation of security concerns is the way a `clid` is constructed.

Claims identifiers are encoded similar to [JSON Pointer _fragement identifiers_](https://tools.ietf.org/html/rfc6901). This encoding enables us to _namespace_ the security related data points and enables the 
security system to lookup the `claim commissioner` and otherwise trust the veracity of claims.

It is important to note that claim identfiers may be templated. Templated ids are frequently used to represent the relationship between a principal and another generated id within the system. For example, a PMC (property management company) may be
represented by a specific entity within the system. The specification for a user's relationship with a PMC would then use a templated claim in order indicate that a claim is valid for a variety of different PMC identfiers.

**Examples**

The _fragment identifier_ ```#/pmc/adm``` is a valid `clid`. The first segment of the `clid` indicates which `claims commissioner` is the authority over the claim. This first segment is referred to as the `csid` (_Claimset Identifier_). Additional segments in a `clid` denote the claim's scope and meaning to the corresponding commissioner.

### Claim Types

The system understands 3 types of claims:

* [`fact` - a claim that bears a corresponding value](#user-content-fact-claim)
* [`role` - a claim that denotes membership](#user-content-role-claim)
* [`permissions` - a claim composed of permission flags](#user-content-permissions-claim)

#### Fact Claim

A _fact claim_ is a data point related to the principal that the system asserts to be factual, some of these are `principalId`, `first-name`, `last-name`, `email-address`, etc.

Facts always have a corresponding text value. Take for example the following `claim specification` for the principal's email address:

```json
...
   {
     "clid": "#/sys/em",
     "kind": "fact",
     "name": "Email address"
   }
```

Javascript code using a principal's email address might be:

```javascript
// ... assuming systemId is a principal's systemId...

Claims.forPrincipal(systemId)
  .get('#/sys/em', function (err, claim) {
  	if (!err && 'me@my.com' === claim) {
  	  console.log('Yay, its me!');
  	}
  });
```

#### Role Claim

A _role claim_ denotes that the principal has been granted membership in the identified role.

Roles always have a boolean value of `true` or `false`, indicating whether the principal is a member. Take for example the following `claim specification` for the PMC administrator:

```json
...
  {
    "clid": "#/pmc/adm",
    "kind": "role",
    "name": "PMC Administrator"
  }
```

Javascript code verifying a principal's 'PMC Administrator' membership:

```javascript
// ... assuming systemId is a principal's systemId...

Claims.forPrincipal(systemId)
  .get('#/pmc/adm', function (err, claim) {
  	if (claim) {
  	  console.log("Yay, I'm an admin!");
  	}
  });
```

Role claims may frequently be specified with template parameters, given the above example we may create a claim specification for a specific PMC's administrator using the following JSON:

```json
...
    {
        "clid": "#/pmc/{pmcId}/adm",
        "kind": "role",
        "name": "PMC Administrator"
    }
...
```

Verifying the presence of this claim can be done in a couple ways. The most straightforward way to query for a templated claim is done when we want to query if the principal is a related to a 
specific id.

```javascript
// ... assuming systemId is a principal's systemId...

Claims.forPrincipal(systemId)
  .get('#/pmc/12/adm', function (err, claim) {
  	if (claim) {
  	  console.log("Yay, I'm an admin!");
  	}
  });
```

Alternatively, we may want to know if the principal is just an Administrator of *some* PMC rather than any specific id, this may be accomplished with the following JS:

```javascript
// ... assuming systemId is a principal's systemId...

Claims.forPrincipal(systemId)
  .get('#/pmc/{pmcId}/adm', function (err, claim) {
  	if (claim) {
  	  console.log("Yay, I'm an admin!");
  	}
  });
```

#### Permissions Claim

A _permissions claim_ is composed of a set of permissions that have been granted to a principal.

Permissions have a value that is the set of permissions defined in the claim's specification that have been granted a principal. Take for example the following `claim specification` for a PMC's rental unit's data permissions:

```json
...
  {
    "clid": "#/pmc/{pmcId}/units/[]",
    "kind": "permissions",
    "name": "PMC Rental Unit Permissions",
    "permissions": [{
      "flag": "c",
      "description": "Permission to create the PMC's unit data."
    }, {
      "flag": "r",
      "description": "Permission to read the PMC's unit data."
    }, {
      "flag": "u",
      "description": "Permission to update the PMC's unit data."
    }, {
      "flag": "d",
      "description": "Permission to delete the PMC's unit data."
    }],
    "parameters": [{
      "name": "pmcId",
      "position": 1,
      "type": "string"
    }]
  ```

Each discreet permission is defined in the claim's specification. Javascript code testing whether a principal has permission to _read_ and _update_ rental units might be:

```javascript
// ... assuming systemId is a principal's systemId...

Claims.forPrincipal(systemId)
  .get('#/pmc/123/units/[ru]', function (err, claim) {
  	if (!err && claim === '[ru]') {
  	  console.log("Whew, I can read and update PMC 123's rental units!");
  	}
  });
```

## Object Model

The object model implements the business logic necessary to pull claim-specs and claims close to where security demands are being made. It embodies logic that implements select-through caching at many levels in order to reduce unnecessary network requests. The caching adheres to _time-to-live_ declarations made in the `claimset specifications`.

`claims` defines the following objects:

* [`Claims` - entrypoint class for working with claims; binds the object model to resolver implementations that enable specs, claimsets, and claims to be queried.](#user-content-claims-class)

* `Claim` - base Claim class
  * `FactClaim` - a specialized Claim class for facts
  * `PermissionsClaim` - a specialized Claim class for permissions
  * `RoleClaim` - a specialized Claim class for roles

* `ClaimsetSpec` - encapsulates a claimset's specification
* `ClaimSpec` - encapsulates a claim's specification

* `PrincipalClaims` - a short-lived, principal-specific context for querying claims and  making security demands.

* `PrincipalClaimset` - a short-lived, principal-specific utility class that aggregates and caches the results of claim queries.

### Claims Class

The `Claims` class is the entrypoint for client code working with claims.

* `#forPrincipal` - factory method; creates a _principal_ specific claims context (`PrincipalClaims`) for the principal specified by _systemId_
* `#getClaimetSpecs` - gets the `claimset specification` corresponding to the specified `csid` and constructs a `ClaimsetSpec`
* `#getClaimSpecs` - gets the `claim specification` corresponding to the specified `clid` and constructs a `ClaimSpec`

The `Claims` class' constructor requires that three _resolver_ functions be specified, one that resolves _claimsets_ and two others that resolves a principal's _claims_:

```javascript
// assuming the two resolvers are already defined in the runspace:

var claims = new Claims({
  claimsetSpecResolver: getClaimsetSpecs,
  claimResolver: getClaim,
  claimsResolver: getClaims
});
```

The `Claims` class intentionally does not care how these functions are implemented; it only cares about the functions' contract as specified below.

#### Required Claimset Specification Resolver

A _claimset specification resolver_ retrieves a specified `claimset`. It is a function that takes two parameters:

* `csid` - the _claimset_'s identifier
* `callback` - a _nodejs_ style callback function where results are returned

```javascript
function getClaimsetSpecById( csid, callback ) {
  //... implementation elided.
}
```

During normal operation, the resolver _must not_ throw an exception. Error conditions may be communicated back to the caller via the callback's first argument.

The _resolver_'s success response  _must be_ a javascript object constructed from the requested _claimset_'s specification. Since _claimsets_ are specified in JSON Schema format, the direct result of `JSON.parse` is what is expected as the top-level success result provided as the callback's second argument.

#### Required Claim Resolvers

A _claim resolver_ retrieves a principal's claims. 

* `clid` - the _claim_'s identifier (or claims' identifiers). Please note that this method should support being called with an array.
* `systemId` - the _principal_'s `systemId`
* `callback` - a _nodejs_ style callback function where results are returned

```javascript
function getClaim( clid, systemId, callback ) {
  //... implementation elided
  // _N.B._ clid may be a single value _or_ an array.
}
```

During normal operation, the resolver _must not_ throw an exception. Error conditions may be communicated back to the caller via the callback's first argument.

The _resolver_'s success response _must be_ the claim's value, according the the claim's type, and returned as the callback's second argument.