@superawesome/permissions
Version:
Fine grained permissions / access control with ownerships & attribute picking, done right.
170 lines (137 loc) • 10.3 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="philosophy">Philosophy</h1>
<p>The philosophy of SuperAwesome Permissions & <code>PermissionDefinitions</code>:</p>
<ul>
<li><p>as <strong>simple</strong> and as <strong>powerful</strong> as possible.</p>
</li>
<li><p>as <strong>clear</strong>, declarative & reusable as possible.</p>
</li>
<li><p>tight to <strong>code</strong> & <strong>execution environment</strong>, so it's extendable & powerful.</p>
</li>
<li><p><strong>code based</strong> (PDs & ownership hooks) so these can be committed, reviewed & versioned.</p>
</li>
<li><p><strong>forgiving</strong> of trivialities, but failing early for serious flaws.</p>
</li>
</ul>
<h1 id="principles">Principles</h1>
<ul>
<li><p>Be <strong>part of the Service</strong> (Resource Server / API) in code & runtime, instead of some external, obscure & hard to maintain & keep at parity system. This helps have environment parity in terms of <code>PermissionDefinition</code> rules: they live in your app that gets deployed in multiple environments, instead of some XML configuration file that lives somewhere in your deployed environment and can deviate between environments.</p>
</li>
<li><p>Be applicable to backends (or frontends) <strong>without a DB</strong> at all (eg other APIs, storage systems, or generic <code>can-user-do-action-on-resource</code> type questions etc). SA-Permissions is also separate from REST & URI semantics, so it can be used in non-REST and non-API world, such as Frontends, GraphQL servers nad other standalone apps, even games.</p>
</li>
<li><p>Be <strong>forgiving</strong> of trivialities:</p>
<ul>
<li><p>Users with <strong>empty roles</strong> are legitimate (eg <code>{ id: 123, roles: [] }</code>) . Rationale is it may represent a new User on our service, or a User from a different OAuth service or federation with filtered roles etc. There is no need to ban them, they will just not be able to do anything privileged. <strong>Note: behavior deviates from <a href="https://github.com/onury/accesscontrol">AccessControl</a> lib.</strong></p>
</li>
<li><p>Users with <strong>unknown roles</strong> are legitimate (eg <code>{ id: 123, roles: ['YOGA_INSTRUCTOR'] }</code>). Rationale is its just a role we aren't aware of, other services like YogaClass might be though, and why have a different user just for the yoga system? It causes us no trouble, the role is just ignored in this service, no need to fail. We only <strong>warn about it</strong> once per execution. <strong>Note: behavior deviates from <a href="https://github.com/onury/accesscontrol">AccessControl</a> lib.</strong></p>
</li>
</ul>
</li>
<li><p><strong>Fail early</strong>, for serious reasons:</p>
<ul>
<li><p>inconsistencies in <code>PermissionDefinition</code> throw on either <a href="/classes/Permissions.html#addDefinitions"><code>addDefinitions()</code></a> or <a href="/classes/Permissions.html#build"><code>build()</code></a> as early as possible. It fails if your PDs are inconsistent, for example you if</p>
<ul>
<li><p>you are missing essential information, like <code>roles</code>, <code>resource</code> or any <code>grant</code> actions.</p>
</li>
<li><p>you have <code>own</code> possession declared but you dont have ownership hooks (ie you need both an <a href="/classes/PermissionDefinition_DOCS.html#isOwner"><code>isOwner</code></a> and one of <a href="/classes/PermissionDefinition_DOCS.html#listOwned"><code>listOwned</code></a> or a <a href="/classes/PermissionDefinition_DOCS.html#limitOwned"><code>limitOwned</code></a> (but not both).</p>
</li>
<li><p>you try to redefine the same <code>action</code> attributes for a given <code>role</code> + <code>resource</code> in a different PermissionDefinition. </p>
</li>
</ul>
</li>
<li><p>bad requests at runtime (i.e on <a href="/classes/Permissions.html#grantPermit"><code>permissions.grantPermit()</code></a>):</p>
<ul>
<li><p>invalid <code>action</code> (i.e unknown) throws. Only actions that have been <strong>seen at least once</strong> at the <a href="/classes/Permissions.html#addDefinitions"><code>permissions.addPersmissions()</code></a> stage are valid.</p>
<p><strong>Note:</strong> actions are NOT tight to resources - see <a href="/additional-documentation/faq,-gotchas-&-caveats.html">Caveat 1. Leaky actions</a></p>
</li>
<li><p>invalid <code>resource</code> (i.e <code>UnknownThing</code>) throws on <code>permit.grantPermit()</code>, cause invalid resources means something is wrong/missing from your PermissionDefinitions. This is different than how <a href="https://github.com/onury/accesscontrol">AccessControl</a> lib & <a href="https://github.com/anodynos/accesscontrol-re">AccessControl-Re</a> treats those.</p>
</li>
<li><p>invalid <code>user</code>, i.e not complying to <code>{id: number | string, roles: string[]}</code></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>All of the above behaviors & the library itself are tested with hundreds of tests.</strong> </p>
<h1 id="architecture--prior-art">Architecture & Prior Art</h1>
<p>Consider the top level architecture of SuperAwesome Permissions below:</p>
<p><img src="/images/architecture-top-level.svg" alt="architecture-top-level" class="img-responsive" title="SuperAwesome Permissions: top level architecture"></p>
<p>SuperAwesome Permissions is directly using <a href="https://github.com/anodynos/accesscontrol-re">AccessControl-Re</a> which is a <strong>facade</strong> of <a href="https://github.com/onury/accesscontrol">AccessControl lib</a> (both MIT licensed), solving various limitations & issues while drastically <strong>restricting its fluent and verbose API</strong>.</p>
<p>Although SuperAwesome Permissions is modelled & was initially inspired by <a href="https://github.com/onury/accesscontrol">AccessControl lib</a>, it now <strong>exposes only its own public API</strong>, completely isolated from it or AccessControl-Re, which means that a subsequent SA-Permissions version could <strong>replace them with some other implementation</strong>, without breaking compatibility with existing code (if that was needed for some reason).</p>
<p>Internally, SA-Permissions is using <strong>only</strong>:</p>
<ul>
<li><p><code>addAccessInfo()</code> from the restricted <a href="https://github.com/anodynos/accesscontrol-re">AccessControl-Re</a> API.</p>
</li>
<li><p><code>ac.permission({...})</code> plus some trivial introspective methods from the locked <a href="https://github.com/onury/accesscontrol">AccessControl</a> instance (returned internally by AccessControl-Re's <code>build()</code>)</p>
</li>
</ul>
<p>The focus of SA-Permissions is to build on top of those functionalities and to provide the missing link: <em>Possession</em> & <em>Ownership</em> control in the JavaScript world.</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 = 'philosophy,-principles-&-architecture.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>