UNPKG

@superawesome/permissions

Version:

Fine grained permissions / access control with ownerships & attribute picking, done right.

170 lines (137 loc) 10.3 kB
<!doctype 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 &amp; <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 &amp; reusable as possible.</p> </li> <li><p>tight to <strong>code</strong> &amp; <strong>execution environment</strong>, so it&#39;s extendable &amp; powerful.</p> </li> <li><p><strong>code based</strong> (PDs &amp; ownership hooks) so these can be committed, reviewed &amp; 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 &amp; runtime, instead of some external, obscure &amp; hard to maintain &amp; 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 &amp; 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: [&#39;YOGA_INSTRUCTOR&#39;] }</code>). Rationale is its just a role we aren&#39;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-&amp;-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 &amp; <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 &amp; the library itself are tested with hundreds of tests.</strong> </p> <h1 id="architecture--prior-art">Architecture &amp; 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 &amp; issues while drastically <strong>restricting its fluent and verbose API</strong>.</p> <p>Although SuperAwesome Permissions is modelled &amp; 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&#39;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> &amp; <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-&amp;-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>