@superawesome/permissions
Version:
Fine grained permissions / access control with ownerships & attribute picking, done right.
162 lines (124 loc) • 10.1 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 additional-page">
<div class="content-data">
<h1 id="basic-usage">Basic Usage</h1>
<p>SuperAwesome Permissions is not opinionated about where & how it is used. It can be used in standalone JS/TS apps, alongside or separately or with OAuth 2.0, in frontend or mobile apps, REST or GraphQL or other and backend apps using vanilla nodejs, ExpressJs, Koa, NestJs as well as any data layer such as SQL, NOSQL, other APIs etc. </p>
<p>The higher the abstraction of your framework, the better. Our backend framework of choice is <a href="http://nestjs.com/">NestJs</a> for which specifically we have a sister project with native Guard & Decorators at <a href="http://permissions-nestjs.docs.superawesome.com">permissions-nestjs</a> that adheres to the Aspect Oriented philosophy of SuperAwesome Permissions. </p>
<p>You're more than welcome to write your own middleware/plugin etc and share it with the community.</p>
<h2 id="installation">Installation</h2>
<p>To install simply:</p>
<div><pre class="line-numbers"><code class="language-bash"> $ npm install @superawesome/permissions --save</code></pre></div><h2 id="import">Import</h2>
<p>In CommonJS (old vanilla nodejs) do:</p>
<div><pre class="line-numbers"><code class="language-js">const { Permissions } = require('@superawesome/permissions');</code></pre></div><p>On ESx/TS you can import:</p>
<div><pre class="line-numbers"><code class="language-typescript">import { Permissions } from '@superawesome/permissions';</code></pre></div><h2 id="operation-modes">Operation Modes</h2>
<p>SuperAwesome Permissions operation is super simple, as it has only 2 main operation modes:</p>
<h3 id="1-adding-permissiondefinitions--build">1. Adding PermissionDefinitions & build()</h3>
<blockquote>
<p>Define & add permissions for <strong>Roles</strong>: what <strong>Actions</strong> they can do on <strong>Resources</strong>.</p>
</blockquote>
<p>At the application bootstrap we add all <a href="/classes/PermissionDefinition_DOCS.html"><strong>PermissionDefinitions</strong></a> (in short <em>PDs</em>) that we need, onto a <code>Permissions</code> instance:</p>
<div><pre class="line-numbers"><code class="language-typescript"> // create an instance with some initial `permissionDefinitions` & optionally `permissionDefinitionDefaults`:
const permissions = new Permissions({ permissionDefinitions, permissionDefinitionDefaults });
// or add as many `permissionDefinitions` as needed to an existing `permissions` instance
const permissions = new Permissions();
permissions.addDefinitions({..PD..}, {..PDdefaults..}); // one by one
permissions.addDefinitions([{..PD1..}, {..PD2..}, ...], {..PDdefaults..}); // multiple</code></pre></div><p>The <code>permissionDefinitions</code> can either be a single <a href="/classes/PermissionDefinition_DOCS.html"><code>PermissionDefinition</code></a> object or an array of them. You can optionally use <a href="/classes/PermissionDefinitionDefaults.html"><code>permissionDefinitionDefaults</code></a> to keep it DRY.</p>
<p>Once all <a href="/classes/PermissionDefinition_DOCS.html"><strong>PermissionDefinitions</strong></a> are added, we need to <code>.build()</code> <em>before</em> we start querying it:</p>
<div><pre class="line-numbers"><code class="language-typescript"> permissions.build();</code></pre></div><p>and you can start querying it to <em>grant permits</em> (see below).</p>
<p>Note that:</p>
<ul>
<li><p>before <code>build()</code>, you can't call <code>grantPermit()</code> (it throws)</p>
</li>
<li><p>after <code>build()</code>, you can't call <code>addDefinitions()</code> (it throws)</p>
</li>
</ul>
<p>You can do the whole thing in one line:</p>
<div><pre class="line-numbers"><code class="language-typescript"> const permissions = new Permissions({ permissionDefinitions, permissionDefinitionDefaults }).build();</code></pre></div><h3 id="2-granting-permissions">2. Granting Permissions</h3>
<blockquote>
<p>Can User 123 Read Document 123e4567 ?</p>
</blockquote>
<p>At runtime, for a particular <strong>User</strong> (of type <a href="/interfaces/IUser.html">IUser</a>), with zero or more <strong>Roles</strong> attached, we'll ask the <strong>Permissions</strong> instance to <a href="/classes/Permissions.html#grantPermit"><code>grantPermit()</code></a> for a specific <strong>Action</strong> on a specific <strong>Resource</strong> (and optionally a <strong>resourceId</strong>). We pack this query as a <a href="/classes/GrantPermitQuery.html"><code>GrantPermitQuery</code></a>:</p>
<div><pre class="line-numbers"><code class="language-typescript">const grantPermitQuery = {
user: { id: 123, roles: ['EMPLOYEE', 'EMPLOYEE_MANAGER']},
action: 'read', // action without ownership, always checks both
resource: 'document',
resourceId: '123e4567', // optional, forces to check `permit.isOwn()` for specific resourceId
}
const permit = await permissions.grantPermit(grantPermitQuery); // returns a Promise, so we need to `await` or `.then` it.</code></pre></div><h3 id="3-checking-ownership-filtering-picking">3. Checking ownership, filtering, picking</h3>
<p><strong>Permissions</strong> will respond with a <a href="/classes/Permit.html">Permit</a> instance that has the all the information the userland app needs.</p>
<div><pre class="line-numbers"><code class="language-typescript"> permit.granted // true if action on resource is granted at all
permit.anyGranted // true if action is granted on an **any** or only **own** resources
await permit.isOwn('8910abc1112') // true if document '8910abc1112' is owned by User 123.
await permit.attributes('8910abc1112') // returns the attributes that can be accessed, depending on ownership of document '8910abc1112'
await permit.pick({ // returns a partial clone of the object passed (i.e a resource item),
id: '8910abc1112', // only with the attributes allowed, depending on ownership of 8910abc1112.
title: 'Im a Document', ... // If 8910abc1112 is owned, we'll get the attributes granted by "own" action,
}) // or "any" if not owned but "any" is granted.
// Otherwise (worse case scenario, not "own" & "any" not granted), we get an empty object!
// and more...</code></pre></div><p>Read more at <a href="/additional-documentation/detailed-usage-&-examples.html">Detailed Usage Example</a>.</p>
</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 = 'additional-page';
var COMPODOC_CURRENT_PAGE_URL = 'basic-usage.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>