@universis/janitor 2.4.4 → 2.5.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@universis/janitor",
3
- "version": "2.4.4",
3
+ "version": "2.5.0",
4
4
  "description": "Universis api plugin for handling user authorization and rate limiting",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,6 @@
1
+ import {ApplicationService} from '@themost/common';
2
+
3
+ export declare class EnableReadAccessScope extends ApplicationService {
4
+ readonly clientScopes: string[];
5
+ protected enable(clientScope: string): void;
6
+ }
@@ -0,0 +1,133 @@
1
+ import {ApplicationService, TraceUtils} from '@themost/common';
2
+ import {SchemaLoaderStrategy} from '@themost/data';
3
+ import {ScopeAccessConfiguration} from './ScopeAccessConfiguration';
4
+
5
+ class EnableReadAccessScope extends ApplicationService {
6
+ /**
7
+ *
8
+ * @param {import('@themost/express').ExpressDataApplication|import('@themost/common').ApplicationBase} app
9
+ */
10
+ constructor(app) {
11
+ super(app);
12
+ /**
13
+ * @type {import('rxjs').Observable<import('express').Application>}
14
+ */
15
+ const container = app && app.container;
16
+ if (container) {
17
+ container.subscribe((container) => {
18
+ if (container) {
19
+ for (const scope of this.clientScopes) {
20
+ this.enable(scope);
21
+ }
22
+ }
23
+ });
24
+ }
25
+ }
26
+
27
+ get clientScopes() {
28
+ return [
29
+ 'students',
30
+ 'teachers',
31
+ 'registrar'
32
+ ];
33
+ }
34
+
35
+ /**
36
+ * @protected
37
+ * @param {string} scope
38
+ */
39
+ enable(scope) {
40
+ const searchScope = scope;
41
+ const targetScope = scope + ':read';
42
+ // get schema loader strategy for enumerating models
43
+ const schemaLoader = this.getApplication().getConfiguration().getStrategy(SchemaLoaderStrategy)
44
+ if (schemaLoader) {
45
+ const models = schemaLoader.getModels();
46
+ let updated = 0;
47
+ // enumerate models that includes scope based privileges
48
+ // where mask is 1 (read)
49
+ for (const model of models) {
50
+ /**
51
+ * @type {import('@themost/common').DataModelProperties}
52
+ */
53
+ const definition = schemaLoader.getModelDefinition(model);
54
+ if (Array.isArray(definition.privileges)) {
55
+ const privileges = definition.privileges.filter((item) => {
56
+ return item.mask === 1 && Array.isArray(item.scope) && item.scope.includes(searchScope);
57
+ });
58
+ let shouldUpdate = false;
59
+ if (privileges.length > 0) {
60
+ shouldUpdate = true;
61
+ // add target scope to privileges
62
+ for (const privilege of privileges) {
63
+ if (!privilege.scope.includes((targetScope))) {
64
+ privilege.scope.push(targetScope);
65
+ }
66
+ }
67
+ }
68
+ // get fields that includes scope based privileges
69
+ const fields = definition.fields.filter((item) => {
70
+ return Array.isArray(item.privileges);
71
+ }).filter((item => {
72
+ const privileges = item.privileges.filter((privilege) => {
73
+ return privilege.mask === 1 && Array.isArray(privilege.scope) && privilege.scope.includes(searchScope);
74
+ });
75
+ return privileges.length > 0;
76
+ }));
77
+ if (fields.length > 0) {
78
+ shouldUpdate = true;
79
+ for (const field of fields) {
80
+ const privileges = field.privileges.filter((privilege) => {
81
+ return privilege.mask === 1 && Array.isArray(privilege.scope) && privilege.scope.includes(searchScope);
82
+ });
83
+ for (const privilege of privileges) {
84
+ if (!privilege.scope.includes((targetScope))) {
85
+ privilege.scope.push(targetScope);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ if (shouldUpdate) {
91
+ updated++;
92
+ // update model definition
93
+ schemaLoader.setModelDefinition(definition);
94
+ }
95
+ }
96
+ }
97
+ if (updated > 0) {
98
+ TraceUtils.log(`${this.constructor.name}: Updated ${updated} model definition(s) to include scope "${targetScope}"`);
99
+ }
100
+ }
101
+
102
+ const scopeConfiguration = this.getApplication().getConfiguration().getStrategy(ScopeAccessConfiguration);
103
+ if (scopeConfiguration) {
104
+ const elements= scopeConfiguration.elements
105
+ .filter((item) => item.scope.includes(searchScope))
106
+ .filter((item) => item.access.includes('read'));
107
+ if (elements.length > 0) {
108
+ const insertElements = [];
109
+ for (const element of elements) {
110
+ insertElements.push({
111
+ insertAfter: element,
112
+ resource: element.resource,
113
+ scope: [targetScope],
114
+ access: ['read']
115
+ });
116
+ }
117
+ for (const { insertAfter, resource, scope, access } of insertElements) {
118
+ const index = scopeConfiguration.elements.findIndex((element) => element === insertAfter);
119
+ scopeConfiguration.elements.splice(index + 1, 0, {
120
+ resource,
121
+ scope,
122
+ access
123
+ });
124
+ }
125
+ TraceUtils.log(`${this.constructor.name}: Updated ${elements.length} scope access configuration(s) to include scope "${targetScope}"`);
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ export {
132
+ EnableReadAccessScope
133
+ }
package/src/index.d.ts CHANGED
@@ -13,4 +13,5 @@ export * from './outputCacheRouter';
13
13
  export * from './OutputCachingService';
14
14
  export * from './VerifyUserClientScope';
15
15
  export * from './UserWithNetworkAddress';
16
- export * from './NetworkRestrictAccess';
16
+ export * from './NetworkRestrictAccess';
17
+ export * from './EnableReadAccessScope';
package/src/index.js CHANGED
@@ -15,3 +15,4 @@ export * from './OutputCachingService';
15
15
  export * from './VerifyUserClientScope';
16
16
  export * from './UserWithNetworkAddress';
17
17
  export * from './NetworkRestrictAccess';
18
+ export * from './EnableReadAccessScope';