UNPKG

@superawesome/permissions

Version:

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

183 lines (150 loc) 14.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="introduction">Introduction</h1> <p><strong>SuperAwesome Permissions</strong> is a JavaScript (written in TypeScript) library for:</p> <ul> <li><p><strong>Permissions of Controlled Access to Resources</strong></p> </li> <li><p>with <strong>Fine Grained and Object Attribute Level Access Restrictions</strong></p> </li> <li><p>and <strong>Dynamic Ownership / Possession rules</strong></p> </li> </ul> <p>It solves the problem of <strong>Permissions</strong> &amp; <strong>Authorization</strong> (Authorization as in Permissions, Access Control, Privileges) in an organized way, within your main code &amp; execution environment, so you dont have to pollute your code with permissions logic.</p> <p>It is meant to be used by Apps such as API backends but also frontends and other apps. It is agnostic of backend or frontend frameworks, notation patterns, URIs or REST semantics, OAuth Services etc and hence can be used everywhere JS runs.</p> <h2 id="auth-is-divided">Auth is divided</h2> <p>To most people Auth means one thing. But there are two entirely different words and concepts hidden in this acronym: </p> <p><strong>Authentication</strong>, from the Greek word <strong>authentikos</strong> which means &quot;genuine&quot; or &quot;original&quot;, is simply proving who you claim to be. In broad terms, you exchange a username &amp; password, or a fingerprint etc for an authenticated user (eg an object, a record, a token etc in the system). In the real world you show your ID/passport to the airport passport control, you&#39;re allowed in the country. What you can do in the country, in the Bank etc, its NOT up to <strong>authentication</strong>. </p> <p><strong>Authorization</strong>, from the Anglo-French word <strong>authorize</strong>, which basically means permission. This defines what you allowed to do, in each service or facility you use. This is probably the hardest unsolved problem to solve: many major data breaches were due to users having excessive permissions (and NOT authentication). </p> <p>You can read more in this neat article about <a href="https://www.okta.com/identity-101/authentication-vs-authorization/">Authentication VS Authorization</a> or see this slide.</p> <p><img src="/images/authentication-vs-authorization.png" alt="authentication-vs-authorization" class="img-responsive" title="Authentication VS Authorization"></p> <h3 id="authorization-is-broken">Authorization is broken</h3> <p>Its easy to get confused with the duality of <strong>Auth</strong>, because OAuth 2.0 claims it solves both aspects and uses both words in its vocabulary, often in a very misleading way. </p> <p>SuperAwesome Permissions <strong>does not</strong> attempt to solve <strong>Authentication</strong> at all. That&#39;s already solved brilliantly by OAuth 2.0, with various <a href="https://auth0.com/docs/api-auth/which-oauth-flow-to-use">workflows</a>, free &amp; paid server implementations, SaaS platforms, client libraries for backend and frontend etc. </p> <p>SuperAwesome Permissions solves ONLY <strong>Authorization</strong>, since it&#39;s what&#39;s broken in OAuth 2.0 (it just makes it immensely complicated to implement) and what is mostly missing in 2020 (except the Corona Virus vaccine :). </p> <h2 id="overview">Overview</h2> <p><strong>SuperAwesome Permissions</strong> allows you to easily define &amp; use permission rules on your service, that include dynamic ownership rules. For example:</p> <blockquote> <p>As an <strong>EMPLOYEE</strong>, I can <strong>create</strong>, <strong>read</strong>, <strong>update</strong>, <strong>list</strong> only my OWN <strong>Documents</strong> (created by me), all document attributes except <strong>confidential</strong>.</p> </blockquote> <blockquote> <p>As a <strong>EMPLOYEE_MANAGER</strong>, I can <strong>read</strong>, <strong>list</strong> and <strong>review</strong> all <strong>Documents</strong> that are created by <strong>any User</strong> that I&#39;m <strong>managing</strong>, with all document attributes.</p> </blockquote> <p>These are just the human business rules, but the actual JS definitions are quite easy to derive: you just express the above as a JSON/JS <a href="/classes/PermissionDefinition_DOCS.html"><code>PermissionDefinition</code></a> object!</p> <p>Since <strong>Ownership</strong> is involved (e.g. <code>&quot;all Documents of my company&#39;s Users&quot;</code>) you need to provide the ownership rules (as callbacks, using JS code). Usually these rules evaluate dynamically &amp; and optimistically (i.e if one User&#39;s <strong>Role</strong> has it, the <strong>User</strong> has it) , depending on the User and the state of your data layer. This way SA-Permissions can evaluate what <strong>&quot;my own documents&quot;</strong> means for each <strong>Role</strong> (and therefore, <strong>User</strong>).</p> <p>Read more about usage in <a href="/additional-documentation/basic-usage.html">Basic Usage</a> or the <a href="/additional-documentation/philosophy,-principles-&amp;-architecture.html">Philosophy, Principles &amp; Architecture</a> and <a href="/additional-documentation/inspiration,-problem,-prior-art.html">Inspiration, Problem and Prior Art</a>.</p> <h1 id="glossary">Glossary</h1> <p>Before we move on, a small glossary:</p> <ul> <li><p>A <strong>User</strong> represents any user in our system, having at least a unique <code>id</code> and a list of associated Roles (eg <code>{id: 123, roles: [&#39;ADMIN&#39;, &#39;CLERK&#39;]}</code> is a valid <a href="/interfaces/IUser.html"><code>IUser</code></a>). Note that <code>id</code> can also be a string, UUID etc.</p> </li> <li><p>A <strong>Role</strong> is a just a simple string tag (eg <code>&#39;ADMIN&#39;</code> or <code>&#39;CLERK&#39;</code>). On its own, a <strong>Role</strong> is just a tag and conveys no meaning at all. It gets its meaning by defining <strong>permissions</strong> against <strong>Resources</strong> for any <strong>User</strong> with that <strong>Role</strong>. A <strong>User</strong> can have zero or more associated <em>Roles</em>.</p> </li> <li><p>A <strong>Resource</strong>, another string tag, represents something the <strong>User</strong> needs to act upon. Usually it&#39;s an Entity (i.e a <strong>Model</strong>, a <strong>Table</strong> etc) of our System (eg. <code>Document</code>, <code>Comment</code>) but can also be anything else such a REST/GraphQL endpoint, a URL, an S3 file, a Device, something abstract like a <code>Drawer</code> or <code>VirtualPet</code> etc. At any interaction with the service, we can think of it as a <strong>User</strong> that wants to perform an <strong>Action</strong> against a particular <strong>Resource</strong>.</p> </li> <li><p>An <strong>Action</strong>, another string tag, is a discrete operation a <strong>User</strong> wants to perform against a <strong>Resource</strong>. Standard actions are <a href="https://onury.io/accesscontrol/?content=guide#actions-and-action-attributes">CRUD operations (<code>create</code>, <code>read</code>, <code>update</code>, <code>delete</code>) from AccessControl</a> lib, but SA-Permissions is flexible to support any arbitrary user-defined action for example <code>follow</code>, <code>list</code>, <code>approve</code>, <code>like</code>, <code>feed</code>, <code>share</code> etc.</p> </li> <li><p>A <a href="/classes/PermissionDefinition_DOCS.html"><strong>PermissionDefinition</strong></a> (in short <em>PD</em>) blends all the above together: it defines what <strong>Actions</strong> the <strong>Roles</strong> (or better <strong>Users</strong> with these <strong>Roles</strong>) can perform on a <strong>Resource</strong>.</p> <p>Its an affirmative declaration, i.e an explicit <strong>grant</strong>. Anything that hasn&#39;t been explicitly allowed is denied. A <strong>Role</strong> and <strong>Resource</strong> can have one or more <strong>PermissionDefinitions</strong> associated with them, and they all come into effect equally (order does NOT matter).</p> <p>A <a href="/classes/PermissionDefinition_DOCS.html"><strong>PermissionDefinition</strong></a> can be expressed in plain English, for example:</p> <blockquote> <p>As a SECRETARY I can <strong>read</strong> the <strong>Calendar</strong> of anyone, but only &#39;title&#39; &amp; &#39;date&#39; attributes. But for people in <strong>my team</strong>, I can <strong>read</strong> &amp; <strong>update</strong> all <strong>Calendar</strong> attributes except &#39;confidential&#39;.</p> </blockquote> <p>where:</p> <ul> <li><p><code>SECRETARY</code> is a <strong>Role</strong>,</p> </li> <li><p><code>Calendar</code> is a <strong>Resource</strong></p> </li> <li><p><code>read</code> and <code>update</code> are <strong>Actions</strong></p> </li> <li><p><code>people in my team</code> is the <strong>ownership restriction</strong></p> </li> <li><p><code>title</code> &amp; <code>date</code> &amp; <code>all attributes except &#39;confidential&#39;</code> are the allowed attributes for each action &amp; ownership.</p> </li> </ul> <p>This english description, can be trivially turned into a JavaScript <a href="/classes/PermissionDefinition_DOCS.html"><code>PermissionDefinition</code></a> object to give us the fine grain <strong>ownership control</strong> within our app&#39;s domain. </p> <p>For example the resulting <a href="/classes/PermissionDefinition_DOCS.html"><code>PermissionDefinition</code></a> from above would look like:</p> <div><pre class="line-numbers"><code class="language-js"> { roles: [&#39;SECRETARY&#39;], resource: &#39;Calendar&#39;, isOwner: isCalendarIdInMyTeam, listOwned: calendarIdsThatBelongToMyTeam, grant: { &#39;read:any&#39;: [&#39;title&#39;, &#39;date&#39;], &#39;read:own&#39;: [&#39;*&#39;, &#39;!confidential&#39;], &#39;update:own&#39;: [&#39;*&#39;, &#39;!confidential&#39;], } }</code></pre></div><p>As you can see, <strong>actions</strong> can have <strong>attributes</strong> associated with them, to restrict the number of <strong>allowed resource attributes</strong> to be used for a particular granted <strong>role + action + resource</strong> combination, as well as ownership (i.e you can access different attributes on your &quot;own&quot; Calendar than those on &quot;any&quot;). </p> <p>Also note that <code>isCalendarIdInMyTeam</code> &amp; <code>calendarIdsThatBelongToMyTeam</code> are the ownership hooks (simple JavaScript async functions) that you need to implement (only when ownership is involved), in order to resolve if a particular resource item belongs to a user.</p> <p>Read more at the <a href="/additional-documentation/basic-usage.html">Basic</a> or the <a href="/additional-documentation/detailed-usage-&amp;-examples.html">Detailed Usage &amp; Examples</a>.</p> </li> </ul> </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 = 'introduction-&amp;-glossary.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>