@superawesome/permissions
Version:
Fine grained permissions / access control with ownerships & attribute picking, done right.
905 lines (738 loc) • 42.2 kB
HTML
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>SuperAwesome Permissions (@superawesome/permissions)</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="../images/favicon.ico">
<link rel="stylesheet" href="../styles/style.css">
<link rel="stylesheet" href="../styles/postmark.css">
</head>
<body>
<div class="navbar navbar-default navbar-fixed-top visible-xs">
<a href="../" class="navbar-brand">SuperAwesome Permissions (@superawesome/permissions)</a>
<button type="button" class="btn btn-default btn-menu ion-ios-menu" id="btn-menu"></button>
</div>
<div class="xs-menu menu" id="mobile-menu">
<div id="book-search-input" role="search"><input type="text" placeholder="Type to search"></div> <compodoc-menu></compodoc-menu>
</div>
<div class="container-fluid main">
<div class="row main">
<div class="hidden-xs menu">
<compodoc-menu mode="normal"></compodoc-menu>
</div>
<!-- START CONTENT -->
<div class="content class">
<div class="content-data">
<ol class="breadcrumb">
<li>Classes</li>
<li>PermissionDefinition_DOCS</li>
</ol>
<ul class="nav nav-tabs" role="tablist">
<li class="active">
<a href="#info" role="tab" id="info-tab" data-toggle="tab" data-link="info">Info</a>
</li>
<li >
<a href="#source" role="tab" id="source-tab" data-toggle="tab" data-link="source">Source</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade active in" id="c-info">
<p class="comment">
<h3>File</h3>
</p>
<p class="comment">
<code>src/PermissionDefinitions.ts</code>
</p>
<p class="comment">
<h3>Description</h3>
</p>
<p class="comment">
<p><strong>NOTE: This class <code>PermissionDefinition_DOCS</code> is a dummy one, it is only the placeholder for docs for the real <a href="/miscellaneous/typealiases.html#PermissionDefinition">PermissionDefinition</a> which is a type alias</strong>.</p>
<p>A <code>PermissionDefinition</code> (in short <strong>PD</strong>) is the fundamental way to define <strong>permission rules</strong>:</p>
<blockquote>
<p>It grants which <strong>Actions</strong> (eg <code>approve</code>, <code>follow</code>) a <strong>Role</strong> (eg <code>EMPLOYEE</code>) can perform on a <strong>Resource</strong> (eg <code>Document</code>), whether on <strong>any</strong> Resource or only on <strong>own</strong> ones.</p>
</blockquote>
<p>The grants can also restrict some <em>Role-Action-Resource</em> declarations to:</p>
<ul>
<li><p>touch only specific <strong>attributes</strong>, defined as an array of attributes names (eg <code>['title', 'price']</code>) & optionally glob notation (eg <code>'*'</code> or <code>'!confidentialNotes'</code>).</p>
</li>
<li><p>perform an action only on <strong>OWN Resources</strong>, where Ownership is defined as <strong>ownership</strong> hooks (i.e async callbacks) that define how the give roles own an particular resource.</p>
</li>
</ul>
<p><strong>Note</strong> : when you use <strong>any ownership hook</strong>, the following rules apply:</p>
<ul>
<li><p>you 'll need to implement <code>isOwner</code> for sure.</p>
</li>
<li><p>also need to implement one of <code>listOwned</code> OR <code>limitOwned</code>, but not both.</p>
<p>i.e its an all or nothing, also enforced as runtime check - see the reasoning in the <a href="/additional-documentation/faq,-gotchas-&-caveats.html">FAQ</a>.</p>
</li>
</ul>
<p><strong>Note</strong>: A PermissionDefinition is a loose definition, meaning that all of the props are <strong>optional</strong>. This is because users can use <a href="/classes/PermissionDefinitionDefaults.html">defaults</a>, use <a href="/classes/PermissionDefinition_DOCS.html#grant">shortcut syntax for grant actions</a> etc.</p>
<p>At runtime though, your complete PDs are validated thoroughly at the earliest possible - see <a href="/additional-documentation/philosophy,-principles-&-architecture.html">Principles</a>.</p>
<h2 id="example">Example</h2>
<div><pre class="line-numbers"><code class="language-typescript"> {
roles: ['EMPLOYEE', 'REGISTERED_USER'],
resource: 'document',
descr: "I can CRUD only OWN Documents (i.e created by me)." +
"I can read all but 'confidential' fields.",
isOwner: async ({ user, resourceId }) => await isUserOwnerOfDocument({ user, resourceId }),
listOwned: async user => await listOfUserOwnedDocumentIds(user),
grant: {
'create:own': ['*'],
'read:own': ['*', '!confidential'],
'update:own': ['*', '!confidential'],
'delete:own': ['*'],
},
}</code></pre></div>
</p>
<section>
<h3 id="index">Index</h3>
<table class="table table-sm table-bordered index-table">
<tbody>
<tr>
<td class="col-md-4">
<h6><b>Properties</b></h6>
</td>
</tr>
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<span class="modifier">Optional</span>
<a href="#attributes">attributes</a>
</li>
<li>
<span class="modifier">Optional</span>
<a href="#descr">descr</a>
</li>
<li>
<span class="modifier">Optional</span>
<a href="#grant">grant</a>
</li>
<li>
<a href="#isOwner">isOwner</a>
</li>
<li>
<a href="#limitOwned">limitOwned</a>
</li>
<li>
<a href="#listOwned">listOwned</a>
</li>
<li>
<span class="modifier">Optional</span>
<a href="#possession">possession</a>
</li>
<li>
<span class="modifier">Optional</span>
<a href="#resource">resource</a>
</li>
<li>
<span class="modifier">Optional</span>
<a href="#roles">roles</a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</section>
<section>
<h3 id="inputs">
Properties
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="attributes"></a>
<span class="name">
<b>
<span class="modifier">Optional</span>
attributes</b>
<a href="#attributes"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../miscellaneous/typealiases.html#TAttributes" target="_self" >TAttributes</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="128" class="link-to-prism">src/PermissionDefinitions.ts:128</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Acts as the <em>default</em> attributes on the PD's <code>grant</code> actions, useful when using the shortcut <code>string[]</code> for grant.</p>
<div><pre class="line-numbers"><code class="language-typescript"> {
attributes: ['fooProp', 'is', 'tender'],
grant: {
fooAction: null,
barAction: ['bar', '!tender'],
anotherAction: null
}
}</code></pre></div><p><code>fooAction</code> will be internally represented as</p>
<div><pre class="line-numbers"><code class="language-typescript"> {
grant: {
fooAction: ['fooProp', 'is', 'tender'],
barAction: ['bar', '!tender']
anotherAction: ['fooProp', 'is', 'tender'],
}
}</code></pre></div></div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="descr"></a>
<span class="name">
<b>
<span class="modifier">Optional</span>
descr</b>
<a href="#descr"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/string" target="_blank" >string</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="86" class="link-to-prism">src/PermissionDefinitions.ts:86</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>The Human readable description (i.e both Product/Business Owners & Developers) of the <em>PermissionDefinition</em>, roughly following a standard structure:</p>
<p><code>I <can | cannot> <ACTIONS> <any | own + definition> <Resource></code></p>
<p>for example:</p>
<p> <code>I can CREATE, VIEW, EDIT or DELETE any Document</code></p>
<p> <code>I can CREATE, VIEW, EDIT or DELETE all Documents that are created by me OR any User that I manage.</code></p>
<p>This is plain English and not parsable, so it can be anything, but it's good to keep it non-ambiguous and lean.</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="grant"></a>
<span class="name">
<b>
<span class="modifier">Optional</span>
grant</b>
<a href="#grant"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>TGrants | TActionsList</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="99" class="link-to-prism">src/PermissionDefinitions.ts:99</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>A list of actions granted (i.e CRUD operations <code>'read'</code>, <code>'create'</code>, <code>'delete'</code>, <code>'update'</code> but also anything your domain dictates such as <code>'like'</code>, <code>'follow'</code>, <code>'approve'</code> etc) that the Role can perform & the attributes allowed for each action.</p>
<p>Also it can contain the Possession (after a colon), i.e whether they can perform each action only on their <em>own resources</em> or on <em>any resources</em>, for example <code>create:own</code> or <code>delete:any</code>. If an action is missing the Possession "any" or "own" then it inherits it from <code>possession</code> field of this PD which acts as the PDs default (the local one in the action overrides this the PD default). If there is no default set, it is interpreted as 'any' (reason is that 'own' requires ownership hooks).</p>
<p>The format is compatible with <a href="https://github.com/onury/accesscontrol#actions-and-action-attributes">Access-Control action rules</a> but can also be a shortcut syntax of a <code>string[]</code> of actions (eg <code>['approve', 'like', 'dislike']</code> in which case <code>possession</code> and <code>attributes</code> are inherited from corresponding fields in the PD (or the defaults).</p>
<p>Note that by listing an action as a grant on PD, it becomes a legitimate action on your subsequent locked <code>build()</code>-ed SAPermission instance. So be careful with naming!</p>
<p>You can also use the '<em>' for an action (eg {..., grant: {'</em>:own'}, ...} which will grant the Role(s) of this PD to any <strong>known action</strong> of this SAPermission instance.</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="isOwner"></a>
<span class="name">
<b>
isOwner</b>
<a href="#isOwner"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../miscellaneous/typealiases.html#TisOwner" target="_self" >TisOwner<TUserId | TResourceId></a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="171" class="link-to-prism">src/PermissionDefinitions.ts:171</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Ownership hooks - stored in PermissionDefinitionWithOwnershipInternal - PLEASE KEEP IN SYNC </p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="limitOwned"></a>
<span class="name">
<b>
limitOwned</b>
<a href="#limitOwned"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../miscellaneous/typealiases.html#TlimitOwned" target="_self" >TlimitOwned<TUserId | any></a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="198" class="link-to-prism">src/PermissionDefinitions.ts:198</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>A <strong>synchronous function</strong> containing conditions, restrictions or clauses (a.k.a an ownership hook) that configures the actual <em>query</em> (or a <em>filter</em>) in an accumulating way, that is then used to filter / limit the data from the data layer (eg the DB), for users that have this Role(s).</p>
<p>The purpose of <code>limitOwn</code> is to filter the resources for the given role(s) <strong>lazily</strong> - in contrast with <code>listOwn()</code> which aggressively fetches all owned resourceIds of the User.</p>
<p>All compatible <code>limitOwned</code> functions found for a user, are composed when <code>Permit:limitOwn()</code> is called to configure the query or generate the filter that is used to filter the actual data lazily.</p>
<p>See <code>Permit:limitOwn()</code> for detailed description and examples.</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="listOwned"></a>
<span class="name">
<b>
listOwned</b>
<a href="#listOwned"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../miscellaneous/typealiases.html#TlistOwned" target="_self" >TlistOwned<TUserId | TResourceId></a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="187" class="link-to-prism">src/PermissionDefinitions.ts:187</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>An <strong>async function</strong> returning a list of owned resourceIds (i.e <code>number[]</code> or <code>string[]</code>) for users that have this Role(s).</p>
<p>Like <code>isOwner</code>, it must be implemented by the app developer, based on the app's business rules of what ownership means for the role(s).</p>
<p>It's mandatory to have a <code>listOwned</code> hook for roles with OWN possession, unless <code>limitOwn</code> (see below) exists. Note that and both <code>listOwned</code> and <code>limitOwned</code> CAN NOT COEXIST for a Role set & a resource.</p>
<p>It is <em>optional</em> if possession is ANY or if the Role(s) being defined already have a <code>listOwned</code> defined in another PermissionDefinition for the same resource.</p>
<p>Note that all unique <code>listOwned</code> hooks, of all <code>PermissionDefinition</code> found to match resource and all Roles of the user, are executed when <code>Permit::listOwn()</code> is called, and the result is the <strong>union</strong> of all resourceIds of all <code>listOwned()</code> calls.</p>
<p>Note: This hook can become a bottleneck, if a User potentially has a huge number of owned resources. This can be solved by using</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="possession"></a>
<span class="name">
<b>
<span class="modifier">Optional</span>
possession</b>
<a href="#possession"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="../miscellaneous/enumerations.html#EPossession" target="_self" >EPossession | "own" | "any"</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="156" class="link-to-prism">src/PermissionDefinitions.ts:156</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Acts as the <em>default</em> possession on the PD. For example the PD:</p>
<div><pre class="line-numbers"><code class="language-typescript"> {
possession: EPossession.own;
grant: {
fooAction: ['*'],
'barAction:any': ['*'],
anotherAction: ['tada'],
}
}</code></pre></div><p>will be internally resolved as</p>
<div><pre class="line-numbers"><code class="language-typescript"> {
grant: {
'fooAction:own': ['*'],
'barAction:any': ['*'],
'anotherAction:own': ['tada'],
}
}</code></pre></div></div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="resource"></a>
<span class="name">
<b>
<span class="modifier">Optional</span>
resource</b>
<a href="#resource"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/string" target="_blank" >string</a></code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="71" class="link-to-prism">src/PermissionDefinitions.ts:71</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>The name of the resource, eg 'document', 'comment' or 'campaign'</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="roles"></a>
<span class="name">
<b>
<span class="modifier">Optional</span>
roles</b>
<a href="#roles"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>string | string[]</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="66" class="link-to-prism">src/PermissionDefinitions.ts:66</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description"><p>Base PermissionDefinition, i.e without out Ownership hooks - stored in PermissionDefinitionNoOwnershipInternal - PLEASE KEEP IN SYNC</p>
<p>/**
The name(s) of the Role we are defining (eg <code>['ADMIN', 'EMPLOYEE']</code>).</p>
</div>
</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="tab-pane fade tab-source-code" id="c-source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import { RequireExactlyOne, MergeExclusive } from 'type-fest';
import {
EPossession,
TActionsList,
TAttributes,
TlimitOwned,
TGrants,
TisOwner,
TlistOwned,
Tid,
} from './types';
/**
__NOTE: This class `PermissionDefinition_DOCS` is a dummy one, it is only the placeholder for docs for the real [PermissionDefinition](/miscellaneous/typealiases.html#PermissionDefinition) which is a type alias__.
A `PermissionDefinition` (in short **PD**) is the fundamental way to define **permission rules**:
> It grants which **Actions** (eg `approve`, `follow`) a **Role** (eg `EMPLOYEE`) can perform on a **Resource** (eg `Document`), whether on **any** Resource or only on **own** ones.
The grants can also restrict some *Role-Action-Resource* declarations to:
- touch only specific **attributes**, defined as an array of attributes names (eg `['title', 'price']`) & optionally glob notation (eg `'*'` or `'!confidentialNotes'`).
- perform an action only on **OWN Resources**, where Ownership is defined as **ownership** hooks (i.e async callbacks) that define how the give roles own an particular resource.
__Note__ : when you use **any ownership hook**, the following rules apply:
- you 'll need to implement `isOwner` for sure.
- also need to implement one of `listOwned` OR `limitOwned`, but not both.
i.e its an all or nothing, also enforced as runtime check - see the reasoning in the [FAQ](/additional-documentation/faq,-gotchas-&-caveats.html).
__Note__: A PermissionDefinition is a loose definition, meaning that all of the props are **optional**. This is because users can use [defaults](/classes/PermissionDefinitionDefaults.html), use [shortcut syntax for grant actions](/classes/PermissionDefinition_DOCS.html#grant) etc.
At runtime though, your complete PDs are validated thoroughly at the earliest possible - see [Principles](/additional-documentation/philosophy,-principles-&-architecture.html).
## Example
```typescript
{
roles: ['EMPLOYEE', 'REGISTERED_USER'],
resource: 'document',
descr: "I can CRUD only OWN Documents (i.e created by me)." +
"I can read all but 'confidential' fields.",
isOwner: async ({ user, resourceId }) => await isUserOwnerOfDocument({ user, resourceId }),
listOwned: async user => await listOfUserOwnedDocumentIds(user),
grant: {
'create:own': ['*'],
'read:own': ['*', '!confidential'],
'update:own': ['*', '!confidential'],
'delete:own': ['*'],
},
}
```
*/
// eslint-disable-next-line @typescript-eslint/class-name-casing
class PermissionDefinition_DOCS<TUserId extends Tid = number, TResourceId extends Tid = number> {
/** Base PermissionDefinition, i.e without out Ownership hooks - stored in PermissionDefinitionNoOwnershipInternal - PLEASE KEEP IN SYNC
/**
* The name(s) of the Role we are defining (eg `['ADMIN', 'EMPLOYEE']`).
*/
roles?: string | string[];
/**
* The name of the resource, eg 'document', 'comment' or 'campaign'
*/
resource?: string;
/**
The Human readable description (i.e both Product/Business Owners & Developers) of the *PermissionDefinition*, roughly following a standard structure:
`I <can | cannot> <ACTIONS> <any | own + definition> <Resource>`
for example:
* `I can CREATE, VIEW, EDIT or DELETE any Document`
* `I can CREATE, VIEW, EDIT or DELETE all Documents that are created by me OR any User that I manage.`
This is plain English and not parsable, so it can be anything, but it's good to keep it non-ambiguous and lean.
*/
descr?: string;
/**
A list of actions granted (i.e CRUD operations `'read'`, `'create'`, `'delete'`, `'update'` but also anything your domain dictates such as `'like'`, `'follow'`, `'approve'` etc) that the Role can perform & the attributes allowed for each action.
Also it can contain the Possession (after a colon), i.e whether they can perform each action only on their *own resources* or on *any resources*, for example `create:own` or `delete:any`. If an action is missing the Possession "any" or "own" then it inherits it from `possession` field of this PD which acts as the PDs default (the local one in the action overrides this the PD default). If there is no default set, it is interpreted as 'any' (reason is that 'own' requires ownership hooks).
The format is compatible with [Access-Control action rules](https://github.com/onury/accesscontrol#actions-and-action-attributes) but can also be a shortcut syntax of a `string[]` of actions (eg `['approve', 'like', 'dislike']` in which case `possession` and `attributes` are inherited from corresponding fields in the PD (or the defaults).
Note that by listing an action as a grant on PD, it becomes a legitimate action on your subsequent locked `build()`-ed SAPermission instance. So be careful with naming!
You can also use the '*' for an action (eg {..., grant: {'*:own'}, ...} which will grant the Role(s) of this PD to any **known action** of this SAPermission instance.
*/
grant?: TGrants | TActionsList;
/**
Acts as the *default* attributes on the PD's `grant` actions, useful when using the shortcut `string[]` for grant.
```typescript
{
attributes: ['fooProp', 'is', 'tender'],
grant: {
fooAction: null,
barAction: ['bar', '!tender'],
anotherAction: null
}
}
```
`fooAction` will be internally represented as
```typescript
{
grant: {
fooAction: ['fooProp', 'is', 'tender'],
barAction: ['bar', '!tender']
anotherAction: ['fooProp', 'is', 'tender'],
}
}
```
*/
attributes?: TAttributes;
/**
Acts as the *default* possession on the PD. For example the PD:
```typescript
{
possession: EPossession.own;
grant: {
fooAction: ['*'],
'barAction:any': ['*'],
anotherAction: ['tada'],
}
}
```
will be internally resolved as
```typescript
{
grant: {
'fooAction:own': ['*'],
'barAction:any': ['*'],
'anotherAction:own': ['tada'],
}
}
```
*/
possession?: EPossession | 'own' | 'any';
/** Ownership hooks - stored in PermissionDefinitionWithOwnershipInternal - PLEASE KEEP IN SYNC */
/**
An **async function**, to determine ownership of the resource by a User that has one the Roles of this PD.
The function should be bound to its own context and should return `true` **if User is an owner** of the particular `resourceId` queried for the particular roles, `false` otherwise.
Must be implemented by the app developer, based on the app's business rules of what ownership means for the role(s).
It's *optional* if the Role asked already has `isOwner` defined in another PD (for same resource etc). At the same time, we can add `isOwner` to another PD, in which case BOTH are executed when `Permit::isOwn()` is called (i.e the extending one is not shadowing the extended one). As soon as any of those returns true, `Permit::isOwn()` returns true.
But, if `own` is used (even in one action), `isOwner` must be defined, otherwise an error is thrown at at `build()` time.
*/
isOwner: TisOwner<TUserId, TResourceId>;
/**
An **async function** returning a list of owned resourceIds (i.e `number[]` or `string[]`) for users that have this Role(s).
Like `isOwner`, it must be implemented by the app developer, based on the app's business rules of what ownership means for the role(s).
It's mandatory to have a `listOwned` hook for roles with OWN possession, unless `limitOwn` (see below) exists. Note that and both `listOwned` and `limitOwned` CAN NOT COEXIST for a Role set & a resource.
It is *optional* if possession is ANY or if the Role(s) being defined already have a `listOwned` defined in another PermissionDefinition for the same resource.
Note that all unique `listOwned` hooks, of all `PermissionDefinition` found to match resource and all Roles of the user, are executed when `Permit::listOwn()` is called, and the result is the **union** of all resourceIds of all `listOwned()` calls.
Note: This hook can become a bottleneck, if a User potentially has a huge number of owned resources. This can be solved by using
*/
listOwned: TlistOwned<TUserId, TResourceId>;
/**
A **synchronous function** containing conditions, restrictions or clauses (a.k.a an ownership hook) that configures the actual *query* (or a *filter*) in an accumulating way, that is then used to filter / limit the data from the data layer (eg the DB), for users that have this Role(s).
The purpose of `limitOwn` is to filter the resources for the given role(s) **lazily** - in contrast with `listOwn()` which aggressively fetches all owned resourceIds of the User.
All compatible `limitOwned` functions found for a user, are composed when `Permit:limitOwn()` is called to configure the query or generate the filter that is used to filter the actual data lazily.
See `Permit:limitOwn()` for detailed description and examples.
*/
limitOwned: TlimitOwned<TUserId, any>;
}
/**
This is the actual PermissionDefinition implementation, but the docs are in [PermissionDefinition_DOCS](/classes/PermissionDefinition_DOCS.html).
Implements the ownership hooks rules.
*/
export type PermissionDefinition<
TUserId extends Tid = number,
TResourceId extends Tid = number
> = MergeExclusive<
PermissionDefinitionNoOwnershipInternal,
RequireExactlyOne<
PermissionDefinitionWithOwnershipInternal<TUserId, TResourceId>,
'listOwned' | 'limitOwned'
>
>;
/**
* @internal
* This is an internal class - see [PermissionDefinition_DOCS](/classes/PermissionDefinition_DOCS.html)
*/
class PermissionDefinitionNoOwnershipInternal {
roles?: string | string[];
resource?: string;
descr?: string;
grant?: TGrants | TActionsList;
attributes?: TAttributes;
possession?: EPossession | 'own' | 'any';
}
/**
* @internal
* This is an internal class - see [PermissionDefinition_DOCS](/classes/PermissionDefinition_DOCS.html)
*/
class PermissionDefinitionWithOwnershipInternal<
TUserId extends Tid,
TResourceId extends Tid
> extends PermissionDefinitionNoOwnershipInternal {
isOwner: TisOwner<TUserId, TResourceId>;
listOwned: TlistOwned<TUserId, TResourceId>;
limitOwned: TlimitOwned<TUserId, any>;
}
/**
The optional `PermissionDefinitionDefaults` is a single object (a Partial of [`PermissionDefinition`](/classes/PermissionDefinition_DOCS.html)) whose property values are merged with each [`PermissionDefinition`](/classes/PermissionDefinition_DOCS.html) instance, if an instance's property value is missing.
For example, in the code below:
```typescript
const pdDefaults: PermissionDefinitionDefaults = { resource: 'document' };
permissions.addDefinitions([ {PD1}, {PD2}, ..., {PDn} ], pdDefaults);
```
all PDs that are missing the `resource` property, they will end up with the `{ resource: 'document' }`.
*/
export class PermissionDefinitionDefaults {
// @todo: define with typescript's Pick & modifiers or equivalent
roles?: string | string[];
resource?: string; // a.k.a resourceType eg 'document'
possession?: EPossession;
attributes?: TAttributes;
}
export interface ICompletePermissionDefinitions<
TUserId extends Tid = number,
TResourceId extends Tid = number
> {
defaults?: PermissionDefinitionDefaults;
definitions: PermissionDefinition<TUserId, TResourceId>[];
}
/**
@internal
All `PermissionDefinition` are converted internally to a set of `PermissionDefinitionInternal`, after some consolidation takes place to settle defaults, remove duplicates etc.
A `PermissionDefinitionInternal` is **strict** and **self complete**, i.e it has settled/inherited the defaults and thus nas no missing props.
*/
export class PermissionDefinitionInternal<
TUserId extends Tid = number,
TResourceId extends Tid = number
> {
// @todo: define with typescript's Pick & modifiers or equivalent
roles: string[];
resource: string;
descr: string;
isOwner?: TisOwner<TUserId, TResourceId>;
listOwned?: TlistOwned<TUserId, TResourceId>;
limitOwned?: TlimitOwned<TUserId, any>;
grant: TGrants;
}
</code></pre>
</div>
</div>
</div><div class="search-results">
<div class="has-results">
<h1 class="search-results-title"><span class='search-results-count'></span> result-matching "<span class='search-query'></span>"</h1>
<ul class="search-results-list"></ul>
</div>
<div class="no-results">
<h1 class="search-results-title">No results matching "<span class='search-query'></span>"</h1>
</div>
</div>
</div>
<!-- END CONTENT -->
</div>
</div>
<script>
var COMPODOC_CURRENT_PAGE_DEPTH = 1;
var COMPODOC_CURRENT_PAGE_CONTEXT = 'class';
var COMPODOC_CURRENT_PAGE_URL = 'future-roadmap.html';
</script>
<script src="../js/libs/custom-elements.min.js"></script>
<script src="../js/libs/lit-html.js"></script>
<!-- Required to polyfill modern browsers as code is ES5 for IE... -->
<script src="../js/libs/custom-elements-es5-adapter.js" charset="utf-8" defer></script>
<script src="../js/menu-wc.js" defer></script>
<script src="../js/libs/bootstrap-native.js"></script>
<script src="../js/libs/es6-shim.min.js"></script>
<script src="../js/libs/EventDispatcher.js"></script>
<script src="../js/libs/promise.min.js"></script>
<script src="../js/libs/zepto.min.js"></script>
<script src="../js/compodoc.js"></script>
<script src="../js/tabs.js"></script>
<script src="../js/menu.js"></script>
<script src="../js/libs/clipboard.min.js"></script>
<script src="../js/libs/prism.js"></script>
<script src="../js/sourceCode.js"></script>
<script src="../js/search/search.js"></script>
<script src="../js/search/lunr.min.js"></script>
<script src="../js/search/search-lunr.js"></script>
<script src="../js/search/search_index.js"></script>
<script src="../js/lazy-load-graphs.js"></script>
</body>
</html>