@superawesome/permissions
Version:
Fine grained permissions / access control with ownerships & attribute picking, done right.
460 lines (353 loc) • 16.4 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 interface">
<div class="content-data">
<ol class="breadcrumb">
<li>Interfaces</li>
<li>IUser</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/types.ts</code>
</p>
<p class="comment">
<h3>Description</h3>
</p>
<p class="comment">
<p>All the info we need to know about a User, for example:</p>
<div><pre class="line-numbers"><code class="language-typescript"> const user: IUser = { id: 123, roles: ['ADMIN'] };</code></pre></div><p>If your user.id is of type string:</p>
<div><pre class="line-numbers"><code class="language-typescript"> const user: IUser<string> = { id: 'abc123', roles: ['ADMIN'], anyOtherField: 'foo' };</code></pre></div><p>If you're on vanilla JavaScript worry not: just use number or string for id and you're fine.</p>
</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>
<a href="#id">id</a>
</li>
<li>
<a href="#roles">roles</a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</section>
<section>
<h3 id="inputs">Indexable</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<code>[key: string]: <code><a href="https://www.typescriptlang.org/docs/handbook/basic-types.html" target="_blank" >any</a></code>
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="23" class="link-to-prism">src/types.ts:23</a></div>
</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="id"></a>
<span class="name"><b>id</b><a href="#id"><span class="icon ion-ios-link"></span></a></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<code>id: <code>TUserId</code>
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>TUserId</code>
</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>roles</b><a href="#roles"><span class="icon ion-ios-link"></span></a></span>
</td>
</tr>
<tr>
<td class="col-md-4">
<code>roles: <code>string[]</code>
</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>string[]</code>
</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">export type Tid = number | string;
/**
All the info we need to know about a User, for example:
```typescript
const user: IUser = { id: 123, roles: ['ADMIN'] };
```
If your user.id is of type string:
```typescript
const user: IUser<string> = { id: 'abc123', roles: ['ADMIN'], anyOtherField: 'foo' };
```
If you're on vanilla JavaScript worry not: just use number or string for id and you're fine.
*/
export interface IUser<TUserId extends Tid = number> {
id: TUserId;
roles: string[];
[key: string]: any;
}
/**
* Checks if the user value is a correct [`IUser` type](/interfaces/IUser.html)
*
* @param user the user object to test
*/
export const isValidIUser = <TUserId extends Tid>(user: IUser<TUserId>) =>
!!user &&
['number', 'string'].includes(typeof user.id) &&
Array.isArray(user?.roles) &&
user.roles.every((role) => typeof role === 'string');
/**
* @internal
*/
export interface IResourceItemWithId<TResourceId extends Tid> {
id: TResourceId;
[key: string]: any;
}
/**
* @internal
*/
export interface IResourceItemWithOptionalId<TResourceId extends Tid> {
id?: TResourceId;
[key: string]: any;
}
/**
The interface of the [`listOwned` ownership hook](/classes/PermissionDefinition_DOCS.html#listOwned).
Should return an array of ids (see [`Tid`](/miscellaneous/typealiases.html#Tid) eg `[1, 2, 3]` or `['abc123', 'def456']`.
Example:
```js
async (user) => documentService.findWhere({ createdBy: user.id })
```
*/
export type TlistOwned<TUserId extends Tid = number, TResourceId extends Tid = number> = (
user: IUser<TUserId>
) => Promise<TResourceId[]>;
// Used to signify where `context` should be returned in limitOwn() & limitOwned hooks
export type IContext<Tctx = any> = Tctx;
/**
The interface of the [`limitOwned` ownership hook](/classes/PermissionDefinition_DOCS.html#limitOwned).
See [`Permit.limitOwn()`](/classes/Permit.html#limitOwn) and [Examples 5 & 6](/additional-documentation/detailed-usage-&-examples.html) for how it is used in practice.
__Notes__:
The `context` is of type any (IContext is just an alias for semantics).
Its up to the user what the context is (and how you add and retrieve data from it).
For instance, in an ORM scenario (consider TypeORM as an example) it could be your `query` or `subquery` object, to which you subsequently add `orWhere` expressions.
In a collection (eg array) filtering example, it might be just a bunch of filter functions that you accumulate in an array (i.e the `context`) and then somehow compose (eg with _.overSome).
*/
export type TlimitOwned<TUserId extends Tid = number, Tctx = any> = ({
user,
context,
}: {
user?: IUser<TUserId>;
context?: IContext<Tctx>;
}) => IContext<Tctx>;
/**
The interface of the `limitOwnReduce` you can pass to override the built in `permit.limitOwn()` implementation.
See [Example 6](/additional-documentation/detailed-usage-&-examples.html) for how it is used in practice.
*/
export type TlimitOwnReduce<TUserId extends Tid = number, Tctx = any> = ({
user,
limitOwneds,
context,
}: {
user: IUser<TUserId>;
limitOwneds: TlimitOwned<TUserId, Tctx>[];
context?: IContext<Tctx>;
}) => any;
/**
The interface of the [`isOwner` ownership hook](/classes/PermissionDefinition_DOCS.html#isOwner).
Should return `true` if `user` is owner of the `resourceId`, false otherwise - example:
```js
async ({user, resourceId}) => (await documentService.findById(resourceId).createdBby === user.id;
```
*/
export type TisOwner<TUserId extends Tid = number, TResourceId extends Tid = number> = ({
user,
resourceId,
}: {
user: IUser<TUserId>;
resourceId: TResourceId;
}) => Promise<boolean>;
/**
Eg `['*', '!price', '!confidential']`
*/
export type TAttributes = string[];
/**
The "light" array of actions (and optional possession) that we can assign to `PermissionDefinition.grant`, granting these **actions**.
Internally all actions will inherit by default:
- the **attributes** of `PermissionDefinition.attributes` or default to `['*']`.
- the **possession** of `PermissionDefinition.possession` or default to `any`.
Example:
```js
{
// a PermissionDefinition object
...
grants: ['read', 'update:own', 'like:any', 'follow']
...
}
```
Also see [`TGrants`](/miscellaneous/typealiases.html#TGrants)
*/
export type TActionsList = string[];
/**
The "full" object we can assign to `PermissionDefinition.grant`, granting **actions** and their corresponding **attributes**:
```js
{
'read': ['*'],
'read:own': ['*'],
'read:any': ['*', '!price', '!confidential'],
'delete:own': ['*'],
...
}
```
__Note__: actions optionally accept possession "any" or "own" after the colon. Internally all actions missing the possession part, will inherit the **possession** of `PermissionDefinition.possession` or default to `any`.
Also see [`TActionsList`](/miscellaneous/typealiases.html#TActionsList)
*/
export type TGrants = { [grantAction: string]: TAttributes };
export enum EPossession {
own = 'own',
any = 'any',
}
/**
The object that you pass to [permissions.grantPermit()](/classes/Permissions.html#grantPermit).
Expresses the "can **User** do **action** on **resource** [and optionally **resourceId**]".
See [Basic Usage](/additional-documentation/basic-usage.html)
*/
export class GrantPermitQuery<TUserId extends Tid = number, TResourceId extends Tid = number> {
/**
A user object with at least `{id, roles}`
*/
user: IUser<TUserId>;
/**
The action name, eg `'update'`.
__Note__: it **does NOT accept possession** (eg `update:own`), the `permissions.grantPermit()` call always checks for both.
*/
action: string;
/**
The name of the resource, eg `'document'`. It must be a valid resource, defined in your `PermissionDefinitions` or you'll get a runtime error.
*/
resource: string;
/**
The unique identifier (ID) of a specific resource for which access is queried.
It's optional:
* if not passed it only checks for general access to the resource. You can always call `permit.isOwn(resourceId)` at any later stage on that Permit.
* if passed, it actually calls `permit.isOwn()` for you.
*/
resourceId?: TResourceId;
}
</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 = 'interface';
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>