@pcg/auth 1.0.0-alpha.1 → 1.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,14 +21,14 @@ User object `user` must contain `resolvedPemissions` array with array of objects
21
21
 
22
22
  ```tsx
23
23
  user.resolvedPermission = [
24
- {
25
- id: "js:episodes:get",
26
- scopes: []
27
- },
28
- {
29
- id: "js:episodes:list",
30
- scopes: ["published"]
31
- }
24
+ {
25
+ id: "js:episodes:get",
26
+ scopes: []
27
+ },
28
+ {
29
+ id: "js:episodes:list",
30
+ scopes: ["published"]
31
+ }
32
32
  ],
33
33
 
34
34
  ```
@@ -37,8 +37,8 @@ But, is real life we store permissions in database as array of string, like this
37
37
 
38
38
  ```tsx
39
39
  [
40
- "js:episodes:get",
41
- "js:episodes[@published]:list"
40
+ "js:episodes:get",
41
+ "js:episodes[@published]:list"
42
42
  ]
43
43
  ```
44
44
 
@@ -48,8 +48,8 @@ Library has special helper, to resolve permissions:
48
48
  import { resolvePermissions } from '@deep/auth';
49
49
 
50
50
  const permissions = [
51
- "js:episodes:get",
52
- "js:episodes[@published]:list"
51
+ "js:episodes:get",
52
+ "js:episodes[@published]:list"
53
53
  ]
54
54
 
55
55
  user.resolvedPermission = resolvePermissions(permissions)
@@ -66,7 +66,7 @@ import { isGranted, PermissionContext } from '@deep/auth';
66
66
  const ctx = new PermissionContext();
67
67
 
68
68
  if (filter.published) {
69
- ctx.addScope('published');
69
+ ctx.addScope('published');
70
70
  }
71
71
 
72
72
  isGranted(user, 'js:episodes:list'); // boolean
@@ -77,8 +77,8 @@ Otherwise, we can build query based on user rights. In this case, we add system
77
77
  ```tsx
78
78
  import { isGranted } from '@deep/auth'
79
79
  if(isGranted(user, 'js:roles:get', { scopes: ['system'] })) {
80
- where.or.push({
80
+ where.or.push({
81
81
  type: 'system'
82
82
  })
83
83
  }
84
- ```
84
+ ```
@@ -0,0 +1,96 @@
1
+ //#region src/permissions/functional-scopes.ts
2
+ /**
3
+ * Create an ActionScopesArray from individual scope items.
4
+ *
5
+ * @example
6
+ * scopes(org('jsorg:hci'))
7
+ * // => ['org#jsorg:hci']
8
+ *
9
+ * scopes(org('jsorg:hci'), entityId('ep:123'))
10
+ * // => ['org#jsorg:hci', 'id#ep:123']
11
+ *
12
+ * const o = org('jsorg:hci');
13
+ * scopes(o, and(o, 'published'))
14
+ * // => ['org#jsorg:hci', ['org#jsorg:hci', 'published']]
15
+ */
16
+ function scopes(...items) {
17
+ return items;
18
+ }
19
+ /**
20
+ * Create a wildcard scope that matches any permission scope.
21
+ *
22
+ * @example
23
+ * isGranted(user, 'js:core:episodes:list', anyScope())
24
+ */
25
+ function anyScope() {
26
+ return ["*"];
27
+ }
28
+ /**
29
+ * Create an organization scope.
30
+ *
31
+ * @example
32
+ * org('jsorg:hci') // => 'org#jsorg:hci'
33
+ */
34
+ function org(id$1) {
35
+ return `org#${id$1}`;
36
+ }
37
+ /**
38
+ * Create an entity id scope.
39
+ *
40
+ * @example
41
+ * id('ep:123') // => 'id#ep:123'
42
+ */
43
+ function id(entityId) {
44
+ return `id#${entityId}`;
45
+ }
46
+ /**
47
+ * Create a user scope.
48
+ *
49
+ * @example
50
+ * user('hcu:xxx') // => 'user#hcu:xxx'
51
+ */
52
+ function user(id$1) {
53
+ return `user#${id$1}`;
54
+ }
55
+ /**
56
+ * Create a form scope.
57
+ *
58
+ * @example
59
+ * form('contact') // => 'form#contact'
60
+ */
61
+ function form(id$1) {
62
+ return `form#${id$1}`;
63
+ }
64
+ /**
65
+ * Create a group scope.
66
+ *
67
+ * @example
68
+ * group('hcgrp:ZT9') // => 'grp#hcgrp:ZT9'
69
+ */
70
+ function group(id$1) {
71
+ return `grp#${id$1}`;
72
+ }
73
+ /**
74
+ * Create a named scope with an optional ID.
75
+ *
76
+ * @example
77
+ * scope('published') // => 'published'
78
+ * scope('orggroup', 'hcgrp:ZT9mt8j9lSR') // => 'orggroup#hcgrp:ZT9mt8j9lSR'
79
+ */
80
+ function scope(name, id$1) {
81
+ return id$1 ? `${name}#${id$1}` : name;
82
+ }
83
+ /**
84
+ * Combine multiple scopes with AND logic (all must match).
85
+ *
86
+ * @example
87
+ * and(org('jsorg:hci'), 'published')
88
+ * // => ['org#jsorg:hci', 'published']
89
+ */
90
+ function and(...items) {
91
+ return items;
92
+ }
93
+
94
+ //#endregion
95
+ export { id as a, scopes as c, group as i, user as l, anyScope as n, org as o, form as r, scope as s, and as t };
96
+ //# sourceMappingURL=functional-scopes-CsZNJMXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functional-scopes-CsZNJMXW.js","names":["id"],"sources":["../src/permissions/functional-scopes.ts"],"sourcesContent":["import { type ActionScopesArray } from './action-scopes.js';\n\nexport type ScopeItem = string | string[];\n\n/**\n * Create an ActionScopesArray from individual scope items.\n *\n * @example\n * scopes(org('jsorg:hci'))\n * // => ['org#jsorg:hci']\n *\n * scopes(org('jsorg:hci'), entityId('ep:123'))\n * // => ['org#jsorg:hci', 'id#ep:123']\n *\n * const o = org('jsorg:hci');\n * scopes(o, and(o, 'published'))\n * // => ['org#jsorg:hci', ['org#jsorg:hci', 'published']]\n */\nexport function scopes(...items: ScopeItem[]): ActionScopesArray {\n return items;\n}\n\n/**\n * Create a wildcard scope that matches any permission scope.\n *\n * @example\n * isGranted(user, 'js:core:episodes:list', anyScope())\n */\nexport function anyScope(): ActionScopesArray {\n return ['*'];\n}\n\n/**\n * Create an organization scope.\n *\n * @example\n * org('jsorg:hci') // => 'org#jsorg:hci'\n */\nexport function org(id: string): string {\n return `org#${id}`;\n}\n\n/**\n * Create an entity id scope.\n *\n * @example\n * id('ep:123') // => 'id#ep:123'\n */\nexport function id(entityId: string): string {\n return `id#${entityId}`;\n}\n\n/**\n * Create a user scope.\n *\n * @example\n * user('hcu:xxx') // => 'user#hcu:xxx'\n */\nexport function user(id: string): string {\n return `user#${id}`;\n}\n\n/**\n * Create a form scope.\n *\n * @example\n * form('contact') // => 'form#contact'\n */\nexport function form(id: string): string {\n return `form#${id}`;\n}\n\n/**\n * Create a group scope.\n *\n * @example\n * group('hcgrp:ZT9') // => 'grp#hcgrp:ZT9'\n */\nexport function group(id: string): string {\n return `grp#${id}`;\n}\n\n/**\n * Create a named scope with an optional ID.\n *\n * @example\n * scope('published') // => 'published'\n * scope('orggroup', 'hcgrp:ZT9mt8j9lSR') // => 'orggroup#hcgrp:ZT9mt8j9lSR'\n */\nexport function scope(name: string, id?: string): string {\n return id ? `${name}#${id}` : name;\n}\n\n/**\n * Combine multiple scopes with AND logic (all must match).\n *\n * @example\n * and(org('jsorg:hci'), 'published')\n * // => ['org#jsorg:hci', 'published']\n */\nexport function and(...items: string[]): string[] {\n return items;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,SAAgB,OAAO,GAAG,OAAuC;AAC/D,QAAO;;;;;;;;AAST,SAAgB,WAA8B;AAC5C,QAAO,CAAC,IAAI;;;;;;;;AASd,SAAgB,IAAI,MAAoB;AACtC,QAAO,OAAOA;;;;;;;;AAShB,SAAgB,GAAG,UAA0B;AAC3C,QAAO,MAAM;;;;;;;;AASf,SAAgB,KAAK,MAAoB;AACvC,QAAO,QAAQA;;;;;;;;AASjB,SAAgB,KAAK,MAAoB;AACvC,QAAO,QAAQA;;;;;;;;AASjB,SAAgB,MAAM,MAAoB;AACxC,QAAO,OAAOA;;;;;;;;;AAUhB,SAAgB,MAAM,MAAc,MAAqB;AACvD,QAAOA,OAAK,GAAG,KAAK,GAAGA,SAAO;;;;;;;;;AAUhC,SAAgB,IAAI,GAAG,OAA2B;AAChD,QAAO"}
@@ -0,0 +1,133 @@
1
+ //#region src/permissions/action-scopes.d.ts
2
+ type ActionScopesArray = (string | string[])[];
3
+ /**
4
+ * Action scopes are used to describe
5
+ * what scopes are required to execute an action.
6
+ */
7
+ declare class ActionScopes {
8
+ array: ActionScopesArray;
9
+ anyScope: boolean;
10
+ private idsMap;
11
+ constructor(scopes?: ActionScopesArray);
12
+ /**
13
+ * Set id scope (e.g. 'id#jsu:123')
14
+ * @param id - entity id
15
+ * @returns - this
16
+ * @example
17
+ * scopes.setEntityId('jsu:123')
18
+ *
19
+ * // scopes.getArray() = ['id#jsu:123']
20
+ */
21
+ setEntityId(id: string): this;
22
+ /**
23
+ * Set scope (e.g. 'org', 'org+review')
24
+ * @param scope - scope name (e.g. 'org', 'org+review')
25
+ * @param id - entity id (e.g. 'jsorg:hci')
26
+ * @returns - this
27
+ * @example
28
+ * scopes.set('my')
29
+ *
30
+ * // scopes.getArray() = ['my']
31
+ *
32
+ * scopes.set('org', 'jsorg:hci')
33
+ *
34
+ * // scopes.getArray() = ['org#jsorg:hci']
35
+ */
36
+ set(scope: string, id?: string): this;
37
+ has(scope: string): boolean;
38
+ getArray(): ActionScopesArray;
39
+ /**
40
+ * Permission scopes must cover auth scopes determined in auth context.
41
+ * @param permissionScopes
42
+ */
43
+ canBeExecutedInScopes(permissionScopes: readonly (string | string[])[]): boolean;
44
+ /**
45
+ * Resolve presaved scopes
46
+ * @example
47
+ * authCtx.setScope('org', 'jsorg:hci')
48
+ * [org#hci]
49
+ *
50
+ * authCtx.setScope('org+review');
51
+ * [org#hci, [org#hci, review]]
52
+ **/
53
+ private resolveScopeId;
54
+ }
55
+ //#endregion
56
+ //#region src/permissions/functional-scopes.d.ts
57
+ type ScopeItem = string | string[];
58
+ /**
59
+ * Create an ActionScopesArray from individual scope items.
60
+ *
61
+ * @example
62
+ * scopes(org('jsorg:hci'))
63
+ * // => ['org#jsorg:hci']
64
+ *
65
+ * scopes(org('jsorg:hci'), entityId('ep:123'))
66
+ * // => ['org#jsorg:hci', 'id#ep:123']
67
+ *
68
+ * const o = org('jsorg:hci');
69
+ * scopes(o, and(o, 'published'))
70
+ * // => ['org#jsorg:hci', ['org#jsorg:hci', 'published']]
71
+ */
72
+ declare function scopes(...items: ScopeItem[]): ActionScopesArray;
73
+ /**
74
+ * Create a wildcard scope that matches any permission scope.
75
+ *
76
+ * @example
77
+ * isGranted(user, 'js:core:episodes:list', anyScope())
78
+ */
79
+ declare function anyScope(): ActionScopesArray;
80
+ /**
81
+ * Create an organization scope.
82
+ *
83
+ * @example
84
+ * org('jsorg:hci') // => 'org#jsorg:hci'
85
+ */
86
+ declare function org(id: string): string;
87
+ /**
88
+ * Create an entity id scope.
89
+ *
90
+ * @example
91
+ * id('ep:123') // => 'id#ep:123'
92
+ */
93
+ declare function id(entityId: string): string;
94
+ /**
95
+ * Create a user scope.
96
+ *
97
+ * @example
98
+ * user('hcu:xxx') // => 'user#hcu:xxx'
99
+ */
100
+ declare function user(id: string): string;
101
+ /**
102
+ * Create a form scope.
103
+ *
104
+ * @example
105
+ * form('contact') // => 'form#contact'
106
+ */
107
+ declare function form(id: string): string;
108
+ /**
109
+ * Create a group scope.
110
+ *
111
+ * @example
112
+ * group('hcgrp:ZT9') // => 'grp#hcgrp:ZT9'
113
+ */
114
+ declare function group(id: string): string;
115
+ /**
116
+ * Create a named scope with an optional ID.
117
+ *
118
+ * @example
119
+ * scope('published') // => 'published'
120
+ * scope('orggroup', 'hcgrp:ZT9mt8j9lSR') // => 'orggroup#hcgrp:ZT9mt8j9lSR'
121
+ */
122
+ declare function scope(name: string, id?: string): string;
123
+ /**
124
+ * Combine multiple scopes with AND logic (all must match).
125
+ *
126
+ * @example
127
+ * and(org('jsorg:hci'), 'published')
128
+ * // => ['org#jsorg:hci', 'published']
129
+ */
130
+ declare function and(...items: string[]): string[];
131
+ //#endregion
132
+ export { group as a, scope as c, ActionScopes as d, ActionScopesArray as f, form as i, scopes as l, and as n, id as o, anyScope as r, org as s, ScopeItem as t, user as u };
133
+ //# sourceMappingURL=functional-scopes-DygK315q.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functional-scopes-DygK315q.d.ts","names":[],"sources":["../src/permissions/action-scopes.ts","../src/permissions/functional-scopes.ts"],"sourcesContent":[],"mappings":";KAAY,iBAAA;AAAZ;AAMA;;;AA6Dc,cA7DD,YAAA,CA6DC;EAAiB,KAAA,EA5Df,iBA4De;;;uBAxDR;ECTX;AAgBZ;AAUA;AAUA;AAUA;AAUA;AAUA;AAUA;AAWA;EAWgB,WAAG,CAAA,EAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;;;;;;;;;;;;;;;;cDjCL;;;;;;;;;;;;;;;;;;;AAnEF,KCEA,SAAA,GDFiB,MAAA,GAAA,MAAA,EAAA;AAM7B;;;;;;;;ACJA;AAgBA;AAUA;AAUA;AAUA;AAUA;AAUgB,iBAlDA,MAAA,CAkDI,GAAA,KAAA,EAlDa,SAkDb,EAAA,CAAA,EAlD2B,iBAkD3B;AAUpB;AAWA;AAWA;;;;iBAxEgB,QAAA,CAAA,GAAY;;;;;;;iBAUZ,GAAA;;;;;;;iBAUA,EAAA;;;;;;;iBAUA,IAAA;;;;;;;iBAUA,IAAA;;;;;;;iBAUA,KAAA;;;;;;;;iBAWA,KAAA;;;;;;;;iBAWA,GAAA"}
package/dist/index.d.ts CHANGED
@@ -1,59 +1,7 @@
1
- //#region src/permissions/action-scopes.d.ts
2
- type ActionScopesArray = (string | string[])[];
3
- /**
4
- * Action scopes are used to describe
5
- * what scopes are required to execute an action.
6
- */
7
- declare class ActionScopes {
8
- array: ActionScopesArray;
9
- anyScope: boolean;
10
- private idsMap;
11
- constructor(scopes?: ActionScopesArray);
12
- /**
13
- * Set id scope (e.g. 'id#jsu:123')
14
- * @param id - entity id
15
- * @returns - this
16
- * @example
17
- * scopes.setEntityId('jsu:123')
18
- *
19
- * // scopes.getArray() = ['id#jsu:123']
20
- */
21
- setEntityId(id: string): this;
22
- /**
23
- * Set scope (e.g. 'org', 'org+review')
24
- * @param scope - scope name (e.g. 'org', 'org+review')
25
- * @param id - entity id (e.g. 'jsorg:hci')
26
- * @returns - this
27
- * @example
28
- * scopes.set('my')
29
- *
30
- * // scopes.getArray() = ['my']
31
- *
32
- * scopes.set('org', 'jsorg:hci')
33
- *
34
- * // scopes.getArray() = ['org#jsorg:hci']
35
- */
36
- set(scope: string, id?: string): this;
37
- has(scope: string): boolean;
38
- getArray(): ActionScopesArray;
39
- /**
40
- * Permission scopes must cover auth scopes determined in auth context.
41
- * @param permissionScopes
42
- */
43
- canBeExecutedInScopes(permissionScopes: readonly (string | string[])[]): boolean;
44
- /**
45
- * Resolve presaved scopes
46
- * @example
47
- * authCtx.setScope('org', 'jsorg:hci')
48
- * [org#hci]
49
- *
50
- * authCtx.setScope('org+review');
51
- * [org#hci, [org#hci, review]]
52
- **/
53
- private resolveScopeId;
54
- }
55
- //#endregion
1
+ import { a as group, c as scope, d as ActionScopes, f as ActionScopesArray, i as form, l as scopes, n as and, o as id, r as anyScope, s as org, t as ScopeItem, u as user } from "./functional-scopes-DygK315q.js";
2
+
56
3
  //#region src/types/permissions.d.ts
4
+
57
5
  /**
58
6
  * Resolved permission string to object with scopes
59
7
  * @example
@@ -242,5 +190,5 @@ declare class ScopesBulder {
242
190
  replacePrefix(from: string, to: string): void;
243
191
  }
244
192
  //#endregion
245
- export { ActionScopes, ActionScopesArray, IUser, ReadonlyResolvedPermission, ResolvedPermission, ResolvedPermissionGroup, ScopesBulder, concatScopes, encodeScopes, injectScopesIntoPermission, isGranted, mergeResolvedPermissions, replaceScope, resolvePermission, resolvePermissionGroup, resolvePermissionGroups, resolvePermissions };
193
+ export { ActionScopes, ActionScopesArray, IUser, ReadonlyResolvedPermission, ResolvedPermission, ResolvedPermissionGroup, ScopeItem, ScopesBulder, and, anyScope, concatScopes, encodeScopes, form, group, id, injectScopesIntoPermission, isGranted, mergeResolvedPermissions, org, replaceScope, resolvePermission, resolvePermissionGroup, resolvePermissionGroups, resolvePermissions, scope, scopes, user };
246
194
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/permissions.ts","../src/types/user.ts","../src/permissions/helpers.ts","../src/permissions/scopes-bulder.ts"],"sourcesContent":[],"mappings":";;;;;;;AAKA;AAUA;AAKiB,UAfA,kBAAA,CAe0B;;;;AClB3C;;;;ACoCA;AACQ,UFxBS,uBAAA,CEwBT;EAEQ,EAAA,EAAA,MAAA;EAAoB,MAAA,EAAA,CAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA;;AAgCvB,UFrDI,0BAAA,CE2DhB;EAWY,SAAA,EAAA,EAAA,MAAA;EAqBA,SAAA,MAqBZ,EAAA,SAAA,CAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA;AAUD;;;UD5IiB,KAAA;;ADGjB;AAUA;AAKA;;;;EClBiB,EAAA,EAAA,MAAK;;;;ACoCtB;;;;EAGqD,WAAA,EAAA,SAAA,MAAA,EAAA;EAgCxC;AAiBb;AAqBA;AA+BA;AAkCA;AAiCA;AAaA;AAOA;;;EAE8B,mBAAA,EDxMP,0BCwMO,EAAA;;;;AFlO9B;AAUA;AAKA;;;;AClBA;;;;ACoCA;;;;;AAmCa,cAnCA,SAyCZ,EAAA,CAAA,IAAA,EAxCO,KAwCP,EAAA,UAAA,EAAA,MAAA,EAAA,YAAA,CAAA,EAtCe,iBAsCf,GAtCmC,YAsCnC,EAAA,GAAA,OAAA;AAWY,cAjBA,YA4BZ,EAAA,CAAA,MAAA,EAAA,CAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA,EAAA,GAAA,MAAA;AAUD;AA+BA;AAkCA;AAiCA;AAaA;AAOA;;;;AAE8B,cA7IjB,0BA6IiB,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,cAAA,EAAA,CAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA,EAAA,GAAA,MAAA;AAmD9B;;;;ACxRA;;;;AAmCyC,cD0E5B,YC1E4B,EAAA,CAAA,GAAA,EAAA,CAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA,EAAA,KAAA,EAAA,CAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA,EAAA,GAAA,IAAA;;;;;;;;;cDyG5B,2CAA0C;;;;;;;;;cAkC1C,+CAEV;;;;;;;;;cA+BU,qDAAiD;;;;;;;;;cAajD,yDAEV;cAKU,mCACH,8BACA,yBAAoB;;;;;;;;;;;cAmDjB;;;;;cCxRA,YAAA;;;EHGI,OAAA,UAAA,CAAA,OAAkB,EGAN,YHAM,CAAA,EGAM,YHAN;EAUlB,KAAA,CAAA,CAAA,EAAA,CAAA,MAAA,GAAA,MAAA,EAAuB,CAAA,EAAA;EAKvB,KAAA,CAAA,CAAA,EGPV,YHOU;;;;AClBjB;;;;ACoCA;;EAGgB,MAAA,CAAA,KAAA,EAAA,MAAA,GAAA,MAAA,EAAA,CAAA,EAAA,IAAA;EAAoB;;AAgCpC;AAiBA;AAqBA;AA+BA;AAkCA;EAiCa,MAAA,CAAA,MAAA,EAAA,CAAA,MAAA,GAGZ,MAAA,EAAA,CAAA,EAAA,GC/KwC,YD4KqB,CAAA,EAAA,IAG7D;EAUY;AAOb;;;;;AAqDA;;;;ECxRa"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { a as id, c as scopes, i as group, l as user, n as anyScope, o as org, r as form, s as scope, t as and } from "./functional-scopes-CsZNJMXW.js";
2
+
1
3
  //#region src/permissions/action-scopes.ts
2
4
  /**
3
5
  * Action scopes are used to describe
@@ -7,8 +9,8 @@ var ActionScopes = class {
7
9
  array = [];
8
10
  anyScope = false;
9
11
  idsMap = /* @__PURE__ */ new Map();
10
- constructor(scopes) {
11
- if (scopes) this.array = scopes;
12
+ constructor(scopes$1) {
13
+ if (scopes$1) this.array = scopes$1;
12
14
  }
13
15
  /**
14
16
  * Set id scope (e.g. 'id#jsu:123')
@@ -19,8 +21,8 @@ var ActionScopes = class {
19
21
  *
20
22
  * // scopes.getArray() = ['id#jsu:123']
21
23
  */
22
- setEntityId(id) {
23
- this.set(`id#${id}`);
24
+ setEntityId(id$1) {
25
+ this.set(`id#${id$1}`);
24
26
  return this;
25
27
  }
26
28
  /**
@@ -37,16 +39,16 @@ var ActionScopes = class {
37
39
  *
38
40
  * // scopes.getArray() = ['org#jsorg:hci']
39
41
  */
40
- set(scope, id) {
41
- if (id && /^[A-Za-z_]+$/.test(scope)) this.idsMap.set(scope, id);
42
- if (scope.includes("+")) {
43
- const subscopes = scope.split("+");
42
+ set(scope$1, id$1) {
43
+ if (id$1 && /^[A-Za-z_]+$/.test(scope$1)) this.idsMap.set(scope$1, id$1);
44
+ if (scope$1.includes("+")) {
45
+ const subscopes = scope$1.split("+");
44
46
  this.array.push(subscopes.map((subscope) => this.resolveScopeId(subscope)));
45
- } else this.array.push(this.resolveScopeId(scope));
47
+ } else this.array.push(this.resolveScopeId(scope$1));
46
48
  return this;
47
49
  }
48
- has(scope) {
49
- return this.array.includes(scope);
50
+ has(scope$1) {
51
+ return this.array.includes(scope$1);
50
52
  }
51
53
  getArray() {
52
54
  return [...this.array];
@@ -86,9 +88,9 @@ var ActionScopes = class {
86
88
  * authCtx.setScope('org+review');
87
89
  * [org#hci, [org#hci, review]]
88
90
  **/
89
- resolveScopeId(scope) {
90
- const id = this.idsMap.get(scope);
91
- return id !== void 0 ? `${scope}#${id}` : scope;
91
+ resolveScopeId(scope$1) {
92
+ const id$1 = this.idsMap.get(scope$1);
93
+ return id$1 !== void 0 ? `${scope$1}#${id$1}` : scope$1;
92
94
  }
93
95
  };
94
96
 
@@ -101,9 +103,9 @@ var ActionScopes = class {
101
103
  *
102
104
  * scopes.set('my')
103
105
  */
104
- const normalizeScopes = (scopes) => {
105
- if (!(scopes instanceof ActionScopes)) return new ActionScopes(scopes);
106
- return scopes;
106
+ const normalizeScopes = (scopes$1) => {
107
+ if (!(scopes$1 instanceof ActionScopes)) return new ActionScopes(scopes$1);
108
+ return scopes$1;
107
109
  };
108
110
  /**
109
111
  * Check if user has access to permission
@@ -120,18 +122,18 @@ const normalizeScopes = (scopes) => {
120
122
  * @param actionScopes - action scopes (e.g. ['org#hcorg:hci'] or ActionScopes instance)
121
123
  * @returns - true if user has access to permission
122
124
  */
123
- const isGranted = (user, permission, actionScopes = []) => {
125
+ const isGranted = (user$1, permission, actionScopes = []) => {
124
126
  const [service, module, resource, action] = permission.split(":");
125
- if (user.resolvedPermissions.length === 0) return false;
126
- const userPermissions = user.resolvedPermissions;
127
- const scopes = normalizeScopes(actionScopes);
128
- const permissionCanBeExecutedInScopes = (perm, scopes$1) => perm.scopes.length === 0 || scopes$1.canBeExecutedInScopes(perm.scopes);
127
+ if (user$1.resolvedPermissions.length === 0) return false;
128
+ const userPermissions = user$1.resolvedPermissions;
129
+ const scopes$1 = normalizeScopes(actionScopes);
130
+ const permissionCanBeExecutedInScopes = (perm, scopes$2) => perm.scopes.length === 0 || scopes$2.canBeExecutedInScopes(perm.scopes);
129
131
  const regexp = /* @__PURE__ */ new RegExp(`^(${service}|\\*):(${module}|\\*):(${resource}|\\*):(${action}|\\*)$`);
130
- if (userPermissions.some((perm) => regexp.test(perm.id) && permissionCanBeExecutedInScopes(perm, scopes))) return true;
132
+ if (userPermissions.some((perm) => regexp.test(perm.id) && permissionCanBeExecutedInScopes(perm, scopes$1))) return true;
131
133
  return false;
132
134
  };
133
- const encodeScopes = (scopes) => {
134
- const scopesString = scopes.map((s) => Array.isArray(s) ? s.join("+") : s).join(",");
135
+ const encodeScopes = (scopes$1) => {
136
+ const scopesString = scopes$1.map((s) => Array.isArray(s) ? s.join("+") : s).join(",");
135
137
  return scopesString.length > 0 ? `[${scopesString}]` : "";
136
138
  };
137
139
  /**
@@ -144,10 +146,10 @@ const encodeScopes = (scopes) => {
144
146
  * // js:core:episodes[org,published]:get
145
147
  */
146
148
  const injectScopesIntoPermission = (permission, scopesToInject) => {
147
- const { id, scopes } = resolvePermission(permission);
148
- const [service, module, resource, action] = id.split(":");
149
- concatScopes(scopes, scopesToInject);
150
- return `${service}:${module}:${resource}${encodeScopes(scopes)}:${action}`;
149
+ const { id: id$1, scopes: scopes$1 } = resolvePermission(permission);
150
+ const [service, module, resource, action] = id$1.split(":");
151
+ concatScopes(scopes$1, scopesToInject);
152
+ return `${service}:${module}:${resource}${encodeScopes(scopes$1)}:${action}`;
151
153
  };
152
154
  /**
153
155
  * Concat scopes
@@ -159,7 +161,7 @@ const injectScopesIntoPermission = (permission, scopesToInject) => {
159
161
  */
160
162
  const concatScopes = (set, items) => {
161
163
  for (const item of items) if (Array.isArray(item)) {
162
- if (!set.some((scope) => Array.isArray(scope) && scope.length === item.length && scope.every((s) => item.includes(s)))) set.push(item);
164
+ if (!set.some((scope$1) => Array.isArray(scope$1) && scope$1.length === item.length && scope$1.every((s) => item.includes(s)))) set.push(item);
163
165
  } else if (!set.includes(item)) set.push(item);
164
166
  };
165
167
  /**
@@ -173,13 +175,13 @@ const concatScopes = (set, items) => {
173
175
  const resolvePermission = (permission) => {
174
176
  const matches = /\[(?<scopes>.*?)\]/u.exec(permission);
175
177
  if (matches?.[0] && matches.groups?.scopes) {
176
- const scopes = matches.groups.scopes.split(",").map((s) => {
178
+ const scopes$1 = matches.groups.scopes.split(",").map((s) => {
177
179
  if (s.includes("+")) return s.split("+");
178
180
  return s;
179
181
  });
180
182
  return {
181
183
  id: permission.replace(matches[0], ""),
182
- scopes
184
+ scopes: scopes$1
183
185
  };
184
186
  }
185
187
  return {
@@ -198,12 +200,12 @@ const resolvePermission = (permission) => {
198
200
  const resolvePermissions = (permissions) => {
199
201
  const resolvedPermissions = [];
200
202
  for (const permission of permissions) {
201
- const { id, scopes } = resolvePermission(permission);
202
- const currentPermission = resolvedPermissions.find((p) => p.id === id);
203
- if (currentPermission) concatScopes(currentPermission.scopes, scopes);
203
+ const { id: id$1, scopes: scopes$1 } = resolvePermission(permission);
204
+ const currentPermission = resolvedPermissions.find((p) => p.id === id$1);
205
+ if (currentPermission) concatScopes(currentPermission.scopes, scopes$1);
204
206
  else resolvedPermissions.push({
205
- id,
206
- scopes
207
+ id: id$1,
208
+ scopes: scopes$1
207
209
  });
208
210
  }
209
211
  return resolvedPermissions;
@@ -241,11 +243,11 @@ const mergeResolvedPermissions = (array1, array2) => {
241
243
  });
242
244
  continue;
243
245
  }
244
- const scopes = [...rp1.scopes];
245
- for (const rp2 of rps2) concatScopes(scopes, rp2.scopes);
246
+ const scopes$1 = [...rp1.scopes];
247
+ for (const rp2 of rps2) concatScopes(scopes$1, rp2.scopes);
246
248
  result.push({
247
249
  id: rp1.id,
248
- scopes
250
+ scopes: scopes$1
249
251
  });
250
252
  }
251
253
  for (const rp2 of array2) if (!result.some((rp) => rp.id === rp2.id)) result.push(rp2);
@@ -261,20 +263,20 @@ const mergeResolvedPermissions = (array1, array2) => {
261
263
  * replaceScope(['org#jsorg:xxx', ['org#jsorg:xxx', 'published']], 'org#jsorg:xxx', 'org#jsorg:hci')
262
264
  * // ['org#jsorg:hci', ['org#jsorg:hci', 'published']]
263
265
  */
264
- const replaceScope = (scopes, from, to) => {
266
+ const replaceScope = (scopes$1, from, to) => {
265
267
  const result = [];
266
- for (const scope of scopes) if (Array.isArray(scope)) result.push(replaceScope(scope, from, to));
267
- else if (scope === from) if (Array.isArray(to)) result.push(...to);
268
+ for (const scope$1 of scopes$1) if (Array.isArray(scope$1)) result.push(replaceScope(scope$1, from, to));
269
+ else if (scope$1 === from) if (Array.isArray(to)) result.push(...to);
268
270
  else result.push(to);
269
- else result.push(scope);
271
+ else result.push(scope$1);
270
272
  return result;
271
273
  };
272
274
 
273
275
  //#endregion
274
276
  //#region src/permissions/scopes-bulder.ts
275
277
  var ScopesBulder = class ScopesBulder {
276
- constructor(scopes = []) {
277
- this.scopes = scopes;
278
+ constructor(scopes$1 = []) {
279
+ this.scopes = scopes$1;
278
280
  }
279
281
  static fromBulder(builder) {
280
282
  return new ScopesBulder(builder.scopes.slice());
@@ -294,8 +296,8 @@ var ScopesBulder = class ScopesBulder {
294
296
  * append(['org#hci', 'lang#en])
295
297
  * // ['org#xxx'] => ['org#xxx', ['org#hci', 'lang#en']]
296
298
  */
297
- append(scope) {
298
- this.scopes.push(scope);
299
+ append(scope$1) {
300
+ this.scopes.push(scope$1);
299
301
  }
300
302
  /**
301
303
  * Extend current scopes with the given scopes
@@ -304,9 +306,9 @@ var ScopesBulder = class ScopesBulder {
304
306
  * extend(['lang#en', 'lang#de'])
305
307
  * // ['published', 'draft'] => ['published', 'draft', 'lang#en', 'lang#de']
306
308
  */
307
- extend(scopes) {
308
- if (Array.isArray(scopes)) concatScopes(this.scopes, scopes);
309
- else if (scopes instanceof ScopesBulder) concatScopes(this.scopes, scopes.scopes);
309
+ extend(scopes$1) {
310
+ if (Array.isArray(scopes$1)) concatScopes(this.scopes, scopes$1);
311
+ else if (scopes$1 instanceof ScopesBulder) concatScopes(this.scopes, scopes$1.scopes);
310
312
  }
311
313
  /**
312
314
  * Join all scopes with the given scope
@@ -322,8 +324,8 @@ var ScopesBulder = class ScopesBulder {
322
324
  const scopesToJoin = Array.isArray(scopeOrScopes) ? scopeOrScopes : [scopeOrScopes];
323
325
  if (scopesToJoin.length === 0) return;
324
326
  const currentScopesCopy = this.build();
325
- for (const scope of scopesToJoin) {
326
- const scopeToJoin = Array.isArray(scope) ? scope : [scope];
327
+ for (const scope$1 of scopesToJoin) {
328
+ const scopeToJoin = Array.isArray(scope$1) ? scope$1 : [scope$1];
327
329
  for (const s of currentScopesCopy) if (Array.isArray(s)) result.push(pos === "before" ? [...scopeToJoin, ...s] : [...s, ...scopeToJoin]);
328
330
  else result.push(pos === "before" ? [...scopeToJoin, s] : [s, ...scopeToJoin]);
329
331
  }
@@ -339,12 +341,12 @@ var ScopesBulder = class ScopesBulder {
339
341
  */
340
342
  replacePrefix(from, to) {
341
343
  const result = [];
342
- for (const scope of this.scopes) if (Array.isArray(scope)) result.push(scope.map((s) => s.replace(`${from}#`, `${to}#`)));
343
- else result.push(scope.replace(`${from}#`, `${to}#`));
344
+ for (const scope$1 of this.scopes) if (Array.isArray(scope$1)) result.push(scope$1.map((s) => s.replace(`${from}#`, `${to}#`)));
345
+ else result.push(scope$1.replace(`${from}#`, `${to}#`));
344
346
  this.scopes = result;
345
347
  }
346
348
  };
347
349
 
348
350
  //#endregion
349
- export { ActionScopes, ScopesBulder, concatScopes, encodeScopes, injectScopesIntoPermission, isGranted, mergeResolvedPermissions, replaceScope, resolvePermission, resolvePermissionGroup, resolvePermissionGroups, resolvePermissions };
351
+ export { ActionScopes, ScopesBulder, and, anyScope, concatScopes, encodeScopes, form, group, id, injectScopesIntoPermission, isGranted, mergeResolvedPermissions, org, replaceScope, resolvePermission, resolvePermissionGroup, resolvePermissionGroups, resolvePermissions, scope, scopes, user };
350
352
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["scopes","scopes: (string | string[])[]","resolvedPermissions: ResolvedPermission[]","result: ResolvedPermission[]","result: (string | string[])[]","scopes: (string | string[])[]","result: (string | string[])[]"],"sources":["../src/permissions/action-scopes.ts","../src/permissions/helpers.ts","../src/permissions/scopes-bulder.ts"],"sourcesContent":["export type ActionScopesArray = (string | string[])[];\n\n/**\n * Action scopes are used to describe\n * what scopes are required to execute an action.\n */\nexport class ActionScopes {\n public array: ActionScopesArray = [];\n public anyScope = false;\n private idsMap = new Map<string, string>();\n\n constructor(scopes?: ActionScopesArray) {\n if (scopes) {\n this.array = scopes;\n }\n }\n\n /**\n * Set id scope (e.g. 'id#jsu:123')\n * @param id - entity id\n * @returns - this\n * @example\n * scopes.setEntityId('jsu:123')\n *\n * // scopes.getArray() = ['id#jsu:123']\n */\n setEntityId(id: string): this {\n this.set(`id#${id}`);\n\n return this;\n }\n\n /**\n * Set scope (e.g. 'org', 'org+review')\n * @param scope - scope name (e.g. 'org', 'org+review')\n * @param id - entity id (e.g. 'jsorg:hci')\n * @returns - this\n * @example\n * scopes.set('my')\n *\n * // scopes.getArray() = ['my']\n *\n * scopes.set('org', 'jsorg:hci')\n *\n * // scopes.getArray() = ['org#jsorg:hci']\n */\n set(scope: string, id?: string): this {\n if (id && /^[A-Za-z_]+$/.test(scope)) {\n this.idsMap.set(scope, id);\n }\n\n if (scope.includes('+')) {\n const subscopes = scope.split('+');\n this.array.push(\n subscopes.map((subscope) => this.resolveScopeId(subscope)),\n );\n } else {\n this.array.push(this.resolveScopeId(scope));\n }\n\n return this;\n }\n\n has(scope: string): boolean {\n return this.array.includes(scope);\n }\n\n getArray(): ActionScopesArray {\n return [...this.array];\n }\n\n /**\n * Permission scopes must cover auth scopes determined in auth context.\n * @param permissionScopes\n */\n canBeExecutedInScopes(\n permissionScopes: readonly (string | string[])[],\n ): boolean {\n // if scopes has * then it can be executed in any scope\n if (this.array.includes('*')) {\n return true;\n }\n\n /* action scopes = ['user#jsu:123', 'org#hci', ['org#hci', 'draft']]\n /* permission scopes: ['user#jsu:123', ['org#hci', 'draft']]\n\n /**\n * empty user permission scopes = full access to action\n */\n if (permissionScopes.length === 0) {\n return true;\n }\n\n /**\n * Empty auth scopes but user has permission scopes = no access to action\n *\n * action scopes = []\n * permission scopes = ['org#hci']\n *\n * Example:\n * User try to get list of all entities\n * But user only has permission to entity from org#hci\n */\n if (this.array.length === 0) {\n return false;\n }\n\n return this.array.some((authScope) => {\n if (Array.isArray(authScope)) {\n // scopes ['org#hci', 'draft'] == permissionScope ['org#hci', 'draft']\n\n return permissionScopes.some((permissionScope) => {\n if (Array.isArray(permissionScope)) {\n return permissionScope.every(\n (sub) => authScope.includes(sub),\n );\n }\n\n return authScope.includes(permissionScope);\n });\n }\n\n return permissionScopes.includes(authScope); // 'user#jsu:123' == 'user#jsu:123'\n });\n }\n\n /**\n * Resolve presaved scopes\n * @example\n * authCtx.setScope('org', 'jsorg:hci')\n * [org#hci]\n *\n * authCtx.setScope('org+review');\n * [org#hci, [org#hci, review]]\n **/\n private resolveScopeId(scope: string) {\n const id = this.idsMap.get(scope);\n return id !== undefined\n ? `${scope}#${id}`\n : scope;\n }\n}\n","import {\n type ReadonlyResolvedPermission, type ResolvedPermission, type ResolvedPermissionGroup,\n} from '../types/permissions.js';\nimport { type IUser } from '../types/user.js';\nimport { ActionScopes, type ActionScopesArray } from './action-scopes.js';\n\n/**\n * Normilize scopes to ActionScopes\n * @example\n * const scopes = normalizeScopes(['org#hci', 'user#hcu:xxxxx') // ActionScopes\n *\n * scopes.set('my')\n */\nconst normalizeScopes = (\n scopes: ActionScopesArray | ActionScopes,\n): ActionScopes => {\n if (!(scopes instanceof ActionScopes)) {\n return new ActionScopes(scopes);\n }\n\n return scopes;\n};\n\n/**\n * Check if user has access to permission\n * @example\n *\n * // Check episode access\n * const scopes = new ActionScopes();\n * scopes.setEntityId(episode.id); // id#js:core:episodes:xxxxx\n * scopes.set('org', episode.organizationId); // org#hcorg:hci\n *\n * isGranted(user, 'js:core:episodes:get', scopes)\n * @param user - user object with resolvedPermissions\n * @param permission - permission string (e.g. 'js:core:episodes:get')\n * @param actionScopes - action scopes (e.g. ['org#hcorg:hci'] or ActionScopes instance)\n * @returns - true if user has access to permission\n */\nexport const isGranted = (\n user: IUser,\n permission: string,\n actionScopes: ActionScopesArray | ActionScopes = [],\n): boolean => {\n const [service, module, resource, action] = permission.split(':');\n\n if (user.resolvedPermissions.length === 0) {\n return false;\n }\n\n const userPermissions = user.resolvedPermissions;\n\n const scopes = normalizeScopes(actionScopes);\n\n const permissionCanBeExecutedInScopes = (perm: ReadonlyResolvedPermission, scopes: ActionScopes) =>\n (perm.scopes.length === 0 || scopes.canBeExecutedInScopes(perm.scopes));\n\n const regexp = new RegExp(`^(${service}|\\\\*):(${module}|\\\\*):(${resource}|\\\\*):(${action}|\\\\*)$`);\n\n // novajs:*:*:* or novajs:*:*[org]:*\n // novajs:module:*:* or novajs:module:*[org]:*\n // novajs:module:resource:* or novajs:module:resource:[org]:*\n // novajs:modules:resource:create\n // *:modules:resource:create\n if (userPermissions.some(\n (perm) => regexp.test(perm.id)\n && permissionCanBeExecutedInScopes(perm, scopes))\n ) {\n return true;\n }\n\n return false;\n};\n\nexport const encodeScopes = (scopes: (string | string[])[]): string => {\n const scopesString = scopes\n .map((s) => (Array.isArray(s) ? s.join('+') : s))\n .join(',');\n\n return scopesString.length > 0 ? `[${scopesString}]` : '';\n};\n\n/**\n * Inject scopes into permission\n * @param permission - permission string (e.g. 'js:core:episodes:get')\n * @param scopesToInject - scopes to inject (e.g. ['org', 'published'])\n * @returns - permission string with injected scopes (e.g. 'js:core:episodes[org,published]:get')\n * @example\n * injectScopesIntoPermission('js:core:episodes:get', ['org', 'published'])\n * // js:core:episodes[org,published]:get\n */\nexport const injectScopesIntoPermission = (\n permission: string,\n scopesToInject: (string | string[])[],\n) => {\n const { id, scopes } = resolvePermission(permission);\n\n const [service, module, resource, action] = id.split(':');\n\n concatScopes(scopes, scopesToInject);\n\n return `${service}:${module}:${resource}${encodeScopes(scopes)}:${action}`;\n};\n\n/**\n * Concat scopes\n * @param set - set of scopes\n * @param items - items to concat\n * @example\n * concatScopes(['org#hci'], ['org#dv'])\n * // ['org#hci', 'org#dv']\n */\nexport const concatScopes = (\n set: (string | string[])[],\n items: (string | string[])[],\n): void => {\n for (const item of items) {\n if (Array.isArray(item)) {\n // check if scopes not includes array with subscopes rp2Scope\n if (\n !set.some(\n (scope) =>\n Array.isArray(scope)\n && scope.length === item.length\n && scope.every((s) => item.includes(s)),\n )\n ) {\n set.push(item);\n }\n } else if (!set.includes(item)) {\n set.push(item);\n }\n }\n};\n\n/**\n * Resolve permission string to object with scopes\n * @param permission - permission string\n * @returns - resolved permission object\n * @example\n * resolvePermission('js:core:episodes[org,published]:get')\n * // { id: 'js:core:episodes:get', scopes: ['org', 'published'] }\n */\nexport const resolvePermission = (permission: string): ResolvedPermission => {\n const matches = /\\[(?<scopes>.*?)\\]/u.exec(permission);\n\n if (matches?.[0] && matches.groups?.scopes) {\n const scopes: (string | string[])[] = matches.groups.scopes\n .split(',') // scopes with OR logic\n .map((s) => {\n if (s.includes('+')) {\n return s.split('+'); // scopes with AND logic\n }\n\n return s;\n });\n\n return {\n id: permission.replace(matches[0], ''),\n scopes,\n };\n }\n\n return {\n id: permission,\n scopes: [],\n };\n};\n\n/**\n * Resolve permissions to array of resolved permissions\n * @param permissions - array of permissions\n * @returns - array of resolved permissions\n * @example\n * resolvePermissions(['js:core:episodes[org]:get', 'js:core:episodes[published]:get'])\n * // [{ id: 'js:core:episodes:get', scopes: ['org', 'published'] }]\n */\nexport const resolvePermissions = (\n permissions: string[],\n): ResolvedPermission[] => {\n const resolvedPermissions: ResolvedPermission[] = [];\n\n for (const permission of permissions) {\n const { id, scopes } = resolvePermission(permission);\n\n const currentPermission = resolvedPermissions.find(\n (p) => p.id === id,\n );\n\n if (currentPermission) {\n concatScopes(currentPermission.scopes, scopes);\n } else {\n resolvedPermissions.push({\n id,\n scopes,\n });\n }\n }\n\n return resolvedPermissions;\n};\n\n/**\n * Resolve permission group string to object with scopes\n * @param permissionGroup - permission group string\n * @returns - resolved permission group object\n * @example\n * resolvePermissionGroup('g:js:core:episodes[org]:read')\n * // { id: 'g:js:core:episodes:read', scopes: ['org'] }\n */\nexport const resolvePermissionGroup = (permissionGroup: string) => {\n // Currently it has the same algorithm\n return resolvePermission(permissionGroup);\n};\n\n/**\n * Resolve permission groups to array of resolved permission groups\n * @param permissionGroups - array of permission groups\n * @returns - array of resolved permission groups\n * @example\n * resolvePermissionGroups(['g:js:core:episodes[org]:read', 'g:js:core:episodes[published]:read'])\n * // [{ id: 'g:js:core:episodes:read', scopes: ['org', 'published'] }]\n */\nexport const resolvePermissionGroups = (\n permissionGroups: string[],\n): ResolvedPermissionGroup[] => {\n // Currently it has the same algorithm\n return resolvePermissions(permissionGroups);\n};\n\nexport const mergeResolvedPermissions = (\n array1: ResolvedPermission[],\n array2: ResolvedPermission[],\n) => {\n const result: ResolvedPermission[] = [];\n\n for (const rp1 of array1) {\n const rps2 = array2.filter((rp2) => rp2.id === rp1.id);\n\n /* Empty scopes = has full access */\n if (\n rp1.scopes.length === 0\n || rps2.some((rp2) => rp2.id === rp1.id && rp2.scopes.length === 0)\n ) {\n result.push({\n id: rp1.id,\n scopes: [],\n });\n\n continue;\n }\n\n // [org#hci, user#hcu:xxxxx, [org#hci, review]]\n const scopes = [...rp1.scopes];\n for (const rp2 of rps2) {\n concatScopes(scopes, rp2.scopes);\n }\n\n result.push({\n id: rp1.id,\n scopes,\n });\n }\n\n for (const rp2 of array2) {\n if (!result.some((rp) => rp.id === rp2.id)) {\n result.push(rp2);\n }\n }\n\n return result;\n};\n\n/**\n * Replace scope in array of scopes\n * @param scopes - array of scopes\n * @param from - scope to replace\n * @param to - scope to replace with\n * @returns - array of scopes with replaced scopes\n * @example\n * replaceScope(['org#jsorg:xxx', ['org#jsorg:xxx', 'published']], 'org#jsorg:xxx', 'org#jsorg:hci')\n * // ['org#jsorg:hci', ['org#jsorg:hci', 'published']]\n */\nexport const replaceScope = (\n scopes: (string | string[])[], // [org#jsorg:xxx, [org#jsorg:xxx, published]]\n from: string,\n to: string | (string | string[])[],\n) => {\n const result: (string | string[])[] = [];\n for (const scope of scopes) {\n if (Array.isArray(scope)) {\n result.push(replaceScope(scope, from, to) as string[]);\n } else if (scope === from) {\n if (Array.isArray(to)) {\n result.push(...to);\n } else {\n result.push(to);\n }\n } else {\n result.push(scope);\n }\n }\n\n return result;\n};\n","import { concatScopes } from './helpers.js';\n\nexport class ScopesBulder {\n constructor(private scopes: (string | string[])[] = []) {}\n\n static fromBulder(builder: ScopesBulder) {\n return new ScopesBulder(builder.scopes.slice());\n }\n\n build() {\n return JSON.parse(JSON.stringify(this.scopes)) as (string | string[])[];\n }\n\n clone() {\n return ScopesBulder.fromBulder(this);\n }\n\n /**\n * Append scope or scopes to the current scopes\n * @param scope - scope or array of scopes to append\n * @example\n * append('org#jsorg:hci')\n * // ['org#xxx'] => ['org#xxx', 'org#jsorg:hci']\n * append(['org#hci', 'lang#en])\n * // ['org#xxx'] => ['org#xxx', ['org#hci', 'lang#en']]\n */\n append(scope: string | string[]) {\n this.scopes.push(scope);\n }\n\n /**\n * Extend current scopes with the given scopes\n * @param scopes - scope or array of scopes to extend with\n * @example\n * extend(['lang#en', 'lang#de'])\n * // ['published', 'draft'] => ['published', 'draft', 'lang#en', 'lang#de']\n */\n extend(scopes: (string | string[])[] | ScopesBulder) {\n if (Array.isArray(scopes)) {\n concatScopes(this.scopes, scopes);\n } else if (scopes instanceof ScopesBulder) {\n concatScopes(this.scopes, scopes.scopes);\n }\n }\n\n /**\n * Join all scopes with the given scope\n * @param scope - scope to join with\n * @example\n * join('org#jsorg:hci', 'before')\n * // ['published', 'draft'] => [['org#jsorg:hci', 'published'], ['org#jsorg:hci', 'draft']]\n * join(['lang#en', 'lang#de'])\n * // ['published', 'draft'] => [['published', 'lang#en'], ['published', 'lang#de'], ['draft', 'lang#en'], ['draft', 'lang#de']]\n */\n join(scopeOrScopes: string | (string | string[])[], pos: 'before' | 'after' = 'after') {\n const result: (string | string[])[] = [];\n\n const scopesToJoin = Array.isArray(scopeOrScopes) ? scopeOrScopes : [scopeOrScopes];\n\n if (scopesToJoin.length === 0) {\n return;\n }\n\n const currentScopesCopy = this.build();\n for (const scope of scopesToJoin) {\n const scopeToJoin = Array.isArray(scope) ? scope : [scope];\n\n for (const s of currentScopesCopy) {\n if (Array.isArray(s)) {\n result.push(pos === 'before' ? [...scopeToJoin, ...s] : [...s, ...scopeToJoin]);\n } else {\n result.push(pos === 'before' ? [...scopeToJoin, s] : [s, ...scopeToJoin]);\n }\n }\n }\n\n this.scopes = result;\n }\n\n /**\n * Replace prefix in all scopes\n * @param from - prefix to replace\n * @param to - prefix to replace with\n * @example\n * replacePrefix('org', 'id')\n * ['org#jsorg:hci'] => ['id#jsorg:hci']\n */\n replacePrefix(from: string, to: string) {\n const result: (string | string[])[] = [];\n for (const scope of this.scopes) {\n if (Array.isArray(scope)) {\n result.push(scope.map((s) => s.replace(`${from}#`, `${to}#`)));\n } else {\n result.push(scope.replace(`${from}#`, `${to}#`));\n }\n }\n this.scopes = result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["scopes","id","scope","scopes","user","id","scope","scopes: (string | string[])[]","resolvedPermissions: ResolvedPermission[]","result: ResolvedPermission[]","result: (string | string[])[]","scopes: (string | string[])[]","scope","scopes","result: (string | string[])[]"],"sources":["../src/permissions/action-scopes.ts","../src/permissions/helpers.ts","../src/permissions/scopes-bulder.ts"],"sourcesContent":["export type ActionScopesArray = (string | string[])[];\n\n/**\n * Action scopes are used to describe\n * what scopes are required to execute an action.\n */\nexport class ActionScopes {\n public array: ActionScopesArray = [];\n public anyScope = false;\n private idsMap = new Map<string, string>();\n\n constructor(scopes?: ActionScopesArray) {\n if (scopes) {\n this.array = scopes;\n }\n }\n\n /**\n * Set id scope (e.g. 'id#jsu:123')\n * @param id - entity id\n * @returns - this\n * @example\n * scopes.setEntityId('jsu:123')\n *\n * // scopes.getArray() = ['id#jsu:123']\n */\n setEntityId(id: string): this {\n this.set(`id#${id}`);\n\n return this;\n }\n\n /**\n * Set scope (e.g. 'org', 'org+review')\n * @param scope - scope name (e.g. 'org', 'org+review')\n * @param id - entity id (e.g. 'jsorg:hci')\n * @returns - this\n * @example\n * scopes.set('my')\n *\n * // scopes.getArray() = ['my']\n *\n * scopes.set('org', 'jsorg:hci')\n *\n * // scopes.getArray() = ['org#jsorg:hci']\n */\n set(scope: string, id?: string): this {\n if (id && /^[A-Za-z_]+$/.test(scope)) {\n this.idsMap.set(scope, id);\n }\n\n if (scope.includes('+')) {\n const subscopes = scope.split('+');\n this.array.push(\n subscopes.map((subscope) => this.resolveScopeId(subscope)),\n );\n } else {\n this.array.push(this.resolveScopeId(scope));\n }\n\n return this;\n }\n\n has(scope: string): boolean {\n return this.array.includes(scope);\n }\n\n getArray(): ActionScopesArray {\n return [...this.array];\n }\n\n /**\n * Permission scopes must cover auth scopes determined in auth context.\n * @param permissionScopes\n */\n canBeExecutedInScopes(\n permissionScopes: readonly (string | string[])[],\n ): boolean {\n // if scopes has * then it can be executed in any scope\n if (this.array.includes('*')) {\n return true;\n }\n\n /* action scopes = ['user#jsu:123', 'org#hci', ['org#hci', 'draft']]\n /* permission scopes: ['user#jsu:123', ['org#hci', 'draft']]\n\n /**\n * empty user permission scopes = full access to action\n */\n if (permissionScopes.length === 0) {\n return true;\n }\n\n /**\n * Empty auth scopes but user has permission scopes = no access to action\n *\n * action scopes = []\n * permission scopes = ['org#hci']\n *\n * Example:\n * User try to get list of all entities\n * But user only has permission to entity from org#hci\n */\n if (this.array.length === 0) {\n return false;\n }\n\n return this.array.some((authScope) => {\n if (Array.isArray(authScope)) {\n // scopes ['org#hci', 'draft'] == permissionScope ['org#hci', 'draft']\n\n return permissionScopes.some((permissionScope) => {\n if (Array.isArray(permissionScope)) {\n return permissionScope.every(\n (sub) => authScope.includes(sub),\n );\n }\n\n return authScope.includes(permissionScope);\n });\n }\n\n return permissionScopes.includes(authScope); // 'user#jsu:123' == 'user#jsu:123'\n });\n }\n\n /**\n * Resolve presaved scopes\n * @example\n * authCtx.setScope('org', 'jsorg:hci')\n * [org#hci]\n *\n * authCtx.setScope('org+review');\n * [org#hci, [org#hci, review]]\n **/\n private resolveScopeId(scope: string) {\n const id = this.idsMap.get(scope);\n return id !== undefined\n ? `${scope}#${id}`\n : scope;\n }\n}\n","import {\n type ReadonlyResolvedPermission, type ResolvedPermission, type ResolvedPermissionGroup,\n} from '../types/permissions.js';\nimport { type IUser } from '../types/user.js';\nimport { ActionScopes, type ActionScopesArray } from './action-scopes.js';\n\n/**\n * Normilize scopes to ActionScopes\n * @example\n * const scopes = normalizeScopes(['org#hci', 'user#hcu:xxxxx') // ActionScopes\n *\n * scopes.set('my')\n */\nconst normalizeScopes = (\n scopes: ActionScopesArray | ActionScopes,\n): ActionScopes => {\n if (!(scopes instanceof ActionScopes)) {\n return new ActionScopes(scopes);\n }\n\n return scopes;\n};\n\n/**\n * Check if user has access to permission\n * @example\n *\n * // Check episode access\n * const scopes = new ActionScopes();\n * scopes.setEntityId(episode.id); // id#js:core:episodes:xxxxx\n * scopes.set('org', episode.organizationId); // org#hcorg:hci\n *\n * isGranted(user, 'js:core:episodes:get', scopes)\n * @param user - user object with resolvedPermissions\n * @param permission - permission string (e.g. 'js:core:episodes:get')\n * @param actionScopes - action scopes (e.g. ['org#hcorg:hci'] or ActionScopes instance)\n * @returns - true if user has access to permission\n */\nexport const isGranted = (\n user: IUser,\n permission: string,\n actionScopes: ActionScopesArray | ActionScopes = [],\n): boolean => {\n const [service, module, resource, action] = permission.split(':');\n\n if (user.resolvedPermissions.length === 0) {\n return false;\n }\n\n const userPermissions = user.resolvedPermissions;\n\n const scopes = normalizeScopes(actionScopes);\n\n const permissionCanBeExecutedInScopes = (perm: ReadonlyResolvedPermission, scopes: ActionScopes) =>\n (perm.scopes.length === 0 || scopes.canBeExecutedInScopes(perm.scopes));\n\n const regexp = new RegExp(`^(${service}|\\\\*):(${module}|\\\\*):(${resource}|\\\\*):(${action}|\\\\*)$`);\n\n // novajs:*:*:* or novajs:*:*[org]:*\n // novajs:module:*:* or novajs:module:*[org]:*\n // novajs:module:resource:* or novajs:module:resource:[org]:*\n // novajs:modules:resource:create\n // *:modules:resource:create\n if (userPermissions.some(\n (perm) => regexp.test(perm.id)\n && permissionCanBeExecutedInScopes(perm, scopes))\n ) {\n return true;\n }\n\n return false;\n};\n\nexport const encodeScopes = (scopes: (string | string[])[]): string => {\n const scopesString = scopes\n .map((s) => (Array.isArray(s) ? s.join('+') : s))\n .join(',');\n\n return scopesString.length > 0 ? `[${scopesString}]` : '';\n};\n\n/**\n * Inject scopes into permission\n * @param permission - permission string (e.g. 'js:core:episodes:get')\n * @param scopesToInject - scopes to inject (e.g. ['org', 'published'])\n * @returns - permission string with injected scopes (e.g. 'js:core:episodes[org,published]:get')\n * @example\n * injectScopesIntoPermission('js:core:episodes:get', ['org', 'published'])\n * // js:core:episodes[org,published]:get\n */\nexport const injectScopesIntoPermission = (\n permission: string,\n scopesToInject: (string | string[])[],\n) => {\n const { id, scopes } = resolvePermission(permission);\n\n const [service, module, resource, action] = id.split(':');\n\n concatScopes(scopes, scopesToInject);\n\n return `${service}:${module}:${resource}${encodeScopes(scopes)}:${action}`;\n};\n\n/**\n * Concat scopes\n * @param set - set of scopes\n * @param items - items to concat\n * @example\n * concatScopes(['org#hci'], ['org#dv'])\n * // ['org#hci', 'org#dv']\n */\nexport const concatScopes = (\n set: (string | string[])[],\n items: (string | string[])[],\n): void => {\n for (const item of items) {\n if (Array.isArray(item)) {\n // check if scopes not includes array with subscopes rp2Scope\n if (\n !set.some(\n (scope) =>\n Array.isArray(scope)\n && scope.length === item.length\n && scope.every((s) => item.includes(s)),\n )\n ) {\n set.push(item);\n }\n } else if (!set.includes(item)) {\n set.push(item);\n }\n }\n};\n\n/**\n * Resolve permission string to object with scopes\n * @param permission - permission string\n * @returns - resolved permission object\n * @example\n * resolvePermission('js:core:episodes[org,published]:get')\n * // { id: 'js:core:episodes:get', scopes: ['org', 'published'] }\n */\nexport const resolvePermission = (permission: string): ResolvedPermission => {\n const matches = /\\[(?<scopes>.*?)\\]/u.exec(permission);\n\n if (matches?.[0] && matches.groups?.scopes) {\n const scopes: (string | string[])[] = matches.groups.scopes\n .split(',') // scopes with OR logic\n .map((s) => {\n if (s.includes('+')) {\n return s.split('+'); // scopes with AND logic\n }\n\n return s;\n });\n\n return {\n id: permission.replace(matches[0], ''),\n scopes,\n };\n }\n\n return {\n id: permission,\n scopes: [],\n };\n};\n\n/**\n * Resolve permissions to array of resolved permissions\n * @param permissions - array of permissions\n * @returns - array of resolved permissions\n * @example\n * resolvePermissions(['js:core:episodes[org]:get', 'js:core:episodes[published]:get'])\n * // [{ id: 'js:core:episodes:get', scopes: ['org', 'published'] }]\n */\nexport const resolvePermissions = (\n permissions: string[],\n): ResolvedPermission[] => {\n const resolvedPermissions: ResolvedPermission[] = [];\n\n for (const permission of permissions) {\n const { id, scopes } = resolvePermission(permission);\n\n const currentPermission = resolvedPermissions.find(\n (p) => p.id === id,\n );\n\n if (currentPermission) {\n concatScopes(currentPermission.scopes, scopes);\n } else {\n resolvedPermissions.push({\n id,\n scopes,\n });\n }\n }\n\n return resolvedPermissions;\n};\n\n/**\n * Resolve permission group string to object with scopes\n * @param permissionGroup - permission group string\n * @returns - resolved permission group object\n * @example\n * resolvePermissionGroup('g:js:core:episodes[org]:read')\n * // { id: 'g:js:core:episodes:read', scopes: ['org'] }\n */\nexport const resolvePermissionGroup = (permissionGroup: string) => {\n // Currently it has the same algorithm\n return resolvePermission(permissionGroup);\n};\n\n/**\n * Resolve permission groups to array of resolved permission groups\n * @param permissionGroups - array of permission groups\n * @returns - array of resolved permission groups\n * @example\n * resolvePermissionGroups(['g:js:core:episodes[org]:read', 'g:js:core:episodes[published]:read'])\n * // [{ id: 'g:js:core:episodes:read', scopes: ['org', 'published'] }]\n */\nexport const resolvePermissionGroups = (\n permissionGroups: string[],\n): ResolvedPermissionGroup[] => {\n // Currently it has the same algorithm\n return resolvePermissions(permissionGroups);\n};\n\nexport const mergeResolvedPermissions = (\n array1: ResolvedPermission[],\n array2: ResolvedPermission[],\n) => {\n const result: ResolvedPermission[] = [];\n\n for (const rp1 of array1) {\n const rps2 = array2.filter((rp2) => rp2.id === rp1.id);\n\n /* Empty scopes = has full access */\n if (\n rp1.scopes.length === 0\n || rps2.some((rp2) => rp2.id === rp1.id && rp2.scopes.length === 0)\n ) {\n result.push({\n id: rp1.id,\n scopes: [],\n });\n\n continue;\n }\n\n // [org#hci, user#hcu:xxxxx, [org#hci, review]]\n const scopes = [...rp1.scopes];\n for (const rp2 of rps2) {\n concatScopes(scopes, rp2.scopes);\n }\n\n result.push({\n id: rp1.id,\n scopes,\n });\n }\n\n for (const rp2 of array2) {\n if (!result.some((rp) => rp.id === rp2.id)) {\n result.push(rp2);\n }\n }\n\n return result;\n};\n\n/**\n * Replace scope in array of scopes\n * @param scopes - array of scopes\n * @param from - scope to replace\n * @param to - scope to replace with\n * @returns - array of scopes with replaced scopes\n * @example\n * replaceScope(['org#jsorg:xxx', ['org#jsorg:xxx', 'published']], 'org#jsorg:xxx', 'org#jsorg:hci')\n * // ['org#jsorg:hci', ['org#jsorg:hci', 'published']]\n */\nexport const replaceScope = (\n scopes: (string | string[])[], // [org#jsorg:xxx, [org#jsorg:xxx, published]]\n from: string,\n to: string | (string | string[])[],\n) => {\n const result: (string | string[])[] = [];\n for (const scope of scopes) {\n if (Array.isArray(scope)) {\n result.push(replaceScope(scope, from, to) as string[]);\n } else if (scope === from) {\n if (Array.isArray(to)) {\n result.push(...to);\n } else {\n result.push(to);\n }\n } else {\n result.push(scope);\n }\n }\n\n return result;\n};\n","import { concatScopes } from './helpers.js';\n\nexport class ScopesBulder {\n constructor(private scopes: (string | string[])[] = []) {}\n\n static fromBulder(builder: ScopesBulder) {\n return new ScopesBulder(builder.scopes.slice());\n }\n\n build() {\n return JSON.parse(JSON.stringify(this.scopes)) as (string | string[])[];\n }\n\n clone() {\n return ScopesBulder.fromBulder(this);\n }\n\n /**\n * Append scope or scopes to the current scopes\n * @param scope - scope or array of scopes to append\n * @example\n * append('org#jsorg:hci')\n * // ['org#xxx'] => ['org#xxx', 'org#jsorg:hci']\n * append(['org#hci', 'lang#en])\n * // ['org#xxx'] => ['org#xxx', ['org#hci', 'lang#en']]\n */\n append(scope: string | string[]) {\n this.scopes.push(scope);\n }\n\n /**\n * Extend current scopes with the given scopes\n * @param scopes - scope or array of scopes to extend with\n * @example\n * extend(['lang#en', 'lang#de'])\n * // ['published', 'draft'] => ['published', 'draft', 'lang#en', 'lang#de']\n */\n extend(scopes: (string | string[])[] | ScopesBulder) {\n if (Array.isArray(scopes)) {\n concatScopes(this.scopes, scopes);\n } else if (scopes instanceof ScopesBulder) {\n concatScopes(this.scopes, scopes.scopes);\n }\n }\n\n /**\n * Join all scopes with the given scope\n * @param scope - scope to join with\n * @example\n * join('org#jsorg:hci', 'before')\n * // ['published', 'draft'] => [['org#jsorg:hci', 'published'], ['org#jsorg:hci', 'draft']]\n * join(['lang#en', 'lang#de'])\n * // ['published', 'draft'] => [['published', 'lang#en'], ['published', 'lang#de'], ['draft', 'lang#en'], ['draft', 'lang#de']]\n */\n join(scopeOrScopes: string | (string | string[])[], pos: 'before' | 'after' = 'after') {\n const result: (string | string[])[] = [];\n\n const scopesToJoin = Array.isArray(scopeOrScopes) ? scopeOrScopes : [scopeOrScopes];\n\n if (scopesToJoin.length === 0) {\n return;\n }\n\n const currentScopesCopy = this.build();\n for (const scope of scopesToJoin) {\n const scopeToJoin = Array.isArray(scope) ? scope : [scope];\n\n for (const s of currentScopesCopy) {\n if (Array.isArray(s)) {\n result.push(pos === 'before' ? [...scopeToJoin, ...s] : [...s, ...scopeToJoin]);\n } else {\n result.push(pos === 'before' ? [...scopeToJoin, s] : [s, ...scopeToJoin]);\n }\n }\n }\n\n this.scopes = result;\n }\n\n /**\n * Replace prefix in all scopes\n * @param from - prefix to replace\n * @param to - prefix to replace with\n * @example\n * replacePrefix('org', 'id')\n * ['org#jsorg:hci'] => ['id#jsorg:hci']\n */\n replacePrefix(from: string, to: string) {\n const result: (string | string[])[] = [];\n for (const scope of this.scopes) {\n if (Array.isArray(scope)) {\n result.push(scope.map((s) => s.replace(`${from}#`, `${to}#`)));\n } else {\n result.push(scope.replace(`${from}#`, `${to}#`));\n }\n }\n this.scopes = result;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { a as group, c as scope, i as form, l as scopes, n as and, o as id, r as anyScope, s as org, t as ScopeItem, u as user } from "./functional-scopes-DygK315q.js";
2
+ export { ScopeItem, and, anyScope, form, group, id, org, scope, scopes, user };
package/dist/scopes.js ADDED
@@ -0,0 +1,3 @@
1
+ import { a as id, c as scopes, i as group, l as user, n as anyScope, o as org, r as form, s as scope, t as and } from "./functional-scopes-CsZNJMXW.js";
2
+
3
+ export { and, anyScope, form, group, id, org, scope, scopes, user };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pcg/auth",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.2",
4
4
  "description": "Authorization library",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -14,6 +14,16 @@
14
14
  "type": "module",
15
15
  "main": "dist/index.js",
16
16
  "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./scopes": {
23
+ "types": "./dist/scopes.d.ts",
24
+ "import": "./dist/scopes.js"
25
+ }
26
+ },
17
27
  "files": [
18
28
  "dist"
19
29
  ],