@autofleet/zehut 3.1.2-beta.17 → 3.1.2-beta.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.
@@ -7,33 +7,13 @@ exports.CONTEXTS_IDS_HEADER = exports.ELEVATED_PERMISSIONS_HEADER = void 0;
7
7
  /* eslint-disable consistent-return */
8
8
  const node_cache_1 = __importDefault(require("node-cache"));
9
9
  const object_hash_1 = __importDefault(require("object-hash"));
10
+ const merge_deep_1 = __importDefault(require("merge-deep"));
10
11
  const uuid_1 = require("uuid");
11
12
  const outbreak_1 = require("@autofleet/outbreak");
12
13
  const services_1 = require("../services");
13
14
  exports.ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';
14
15
  exports.CONTEXTS_IDS_HEADER = 'x-af-context-ids';
15
16
  const userCache = new node_cache_1.default({ stdTTL: 10 });
16
- const mergePermissions = (target, sources) => {
17
- const permissions = {
18
- ...(target || {}),
19
- fleets: { ...target?.fleets },
20
- businessModels: { ...target?.businessModels },
21
- demandSources: { ...target?.demandSources },
22
- // Clone other nested objects as needed
23
- };
24
- // eslint-disable-next-line no-restricted-syntax
25
- for (const source of sources) {
26
- Object.keys(source).forEach((entityType) => {
27
- // eslint-disable-next-line no-param-reassign
28
- permissions[entityType] ?? (permissions[entityType] = {});
29
- Object.entries(source[entityType]).forEach(([entityId, perms]) => {
30
- // eslint-disable-next-line no-param-reassign
31
- permissions[entityType][entityId] = (permissions[entityType][entityId] || []).concat(perms);
32
- });
33
- });
34
- }
35
- return permissions;
36
- };
37
17
  class ApiUser {
38
18
  constructor(id, accountType, elevatedPermissions, contextIds) {
39
19
  this.id = id;
@@ -102,13 +82,22 @@ class ApiUser {
102
82
  return Object.keys(this.privatePermissions[key] || {});
103
83
  }
104
84
  get elevatedPermissions() {
105
- return mergePermissions(undefined, this.privateElevatedPermissionsHash.values());
85
+ let permissions = {
86
+ fleets: {},
87
+ businessModels: {},
88
+ demandSources: {},
89
+ };
90
+ [...this.privateElevatedPermissionsHash.values()].forEach((p) => {
91
+ permissions = (0, merge_deep_1.default)(permissions, p);
92
+ });
93
+ return permissions;
106
94
  }
107
95
  get permissions() {
108
96
  if (!this.privatePermissions) {
109
97
  throw new Error('Cannot get permissions without calling (async) getUserPermissions before');
110
98
  }
111
- return mergePermissions(this.privatePermissions, this.privateElevatedPermissionsHash.values());
99
+ const permissions = (0, merge_deep_1.default)(this.privatePermissions, this.elevatedPermissions);
100
+ return permissions;
112
101
  }
113
102
  elevatePermissions(addedPermissions) {
114
103
  const elevationId = (0, uuid_1.v4)();
@@ -8,20 +8,6 @@ const express_1 = __importDefault(require("express"));
8
8
  const axios_1 = __importDefault(require("axios"));
9
9
  const index_1 = require("../index");
10
10
  const index_2 = require("./index");
11
- const services_1 = require("../services");
12
- jest.spyOn(services_1.IdentityNetwork, 'get').mockImplementation(async (url) => {
13
- if (url.includes('/api/v1/users/')) {
14
- return {
15
- data: {
16
- accountType: 'user',
17
- fleets: {},
18
- businessModels: {},
19
- demandSources: {},
20
- },
21
- };
22
- }
23
- return { data: {} };
24
- });
25
11
  const generateApp = async (addEndpoints, port) => {
26
12
  const app = (0, express_1.default)();
27
13
  addEndpoints(app);
@@ -136,167 +122,4 @@ describe('E2E', () => {
136
122
  closeServer1();
137
123
  expect(error.message).toEqual('Entity id on elevatePermissions is not a valid UUID, provided: nnn');
138
124
  });
139
- it('should correctly handle elevation of permissions and their reversion', async () => {
140
- let capturedError = null;
141
- // Snapshots to capture state after each step
142
- let afterFirstElevationElevated = {
143
- fleets: {},
144
- businessModels: {},
145
- demandSources: {},
146
- };
147
- let afterFirstElevationCombined = {
148
- fleets: {},
149
- businessModels: {},
150
- demandSources: {},
151
- };
152
- let afterSecondElevationElevated = {
153
- fleets: {},
154
- businessModels: {},
155
- demandSources: {},
156
- };
157
- let afterSecondElevationCombined = {
158
- fleets: {},
159
- businessModels: {},
160
- demandSources: {},
161
- };
162
- let afterCloseSecondElevated = {
163
- fleets: {},
164
- businessModels: {},
165
- demandSources: {},
166
- };
167
- let afterCloseSecondCombined = {
168
- fleets: {},
169
- businessModels: {},
170
- demandSources: {},
171
- };
172
- let afterCloseFirstElevated = {
173
- fleets: {},
174
- businessModels: {},
175
- demandSources: {},
176
- };
177
- let afterCloseFirstCombined = {
178
- fleets: {},
179
- businessModels: {},
180
- demandSources: {},
181
- };
182
- (0, index_1.enableTracing)({
183
- outbreakOptions: {
184
- headersPrefix: 'x-af-',
185
- },
186
- });
187
- const userId = (0, uuid_1.v4)();
188
- // Generate UUIDs for test entities
189
- const fleetUUID1 = (0, uuid_1.v4)();
190
- const fleetUUID2 = (0, uuid_1.v4)();
191
- const bmUUID1 = (0, uuid_1.v4)();
192
- const dsUUID1 = (0, uuid_1.v4)();
193
- const dsUUID2 = (0, uuid_1.v4)();
194
- // Spin up a test server
195
- const closeServer = await generateApp((app) => {
196
- app.use((0, index_2.middleware)());
197
- app.get('/', async (req, res) => {
198
- try {
199
- const user = (0, index_1.getUser)();
200
- // Load base permissions (mocked)
201
- await user.getUserPermissions();
202
- // Now user.privatePermissions is set
203
- // 1. First Elevation
204
- const closeElevation1 = user.elevatePermissions({
205
- fleets: {
206
- [fleetUUID1]: ['readF1'],
207
- },
208
- businessModels: {
209
- [bmUUID1]: ['writeBM1'],
210
- },
211
- demandSources: {
212
- [dsUUID1]: ['readDS1'],
213
- },
214
- });
215
- // Capture elevated and combined permissions after first elevation
216
- afterFirstElevationElevated = user.elevatedPermissions;
217
- afterFirstElevationCombined = user.permissions; // Non-null assertion since privatePermissions is set
218
- // 2. Second Elevation
219
- const closeElevation2 = user.elevatePermissions({
220
- fleets: {
221
- [fleetUUID1]: ['manageF1'],
222
- [fleetUUID2]: ['createF2'], // New fleet
223
- },
224
- businessModels: {
225
- [bmUUID1]: ['writeBM2'], // Additional permission to existing business model
226
- },
227
- demandSources: {
228
- [dsUUID2]: ['readDS2', 'readDS2'], // New demand source with duplicate permissions
229
- },
230
- });
231
- // Capture elevated and combined permissions after second elevation
232
- afterSecondElevationElevated = user.elevatedPermissions;
233
- afterSecondElevationCombined = user.permissions;
234
- // 3. Close Second Elevation
235
- closeElevation2();
236
- afterCloseSecondElevated = user.elevatedPermissions;
237
- afterCloseSecondCombined = user.permissions;
238
- // 4. Close First Elevation
239
- closeElevation1();
240
- afterCloseFirstElevated = user.elevatedPermissions;
241
- afterCloseFirstCombined = user.permissions;
242
- }
243
- catch (e) {
244
- capturedError = e;
245
- }
246
- res.json({ status: 'ok' });
247
- });
248
- }, 8089);
249
- // Trigger the test route
250
- const response = await axios_1.default.get('http://localhost:8089', {
251
- headers: {
252
- 'x-af-user-id': userId,
253
- },
254
- });
255
- closeServer();
256
- // Basic assertions
257
- expect(response.status).toEqual(200);
258
- expect(capturedError).toBeNull();
259
- // ---------------------
260
- // Assertions After First Elevation
261
- // ---------------------
262
- expect(afterFirstElevationElevated.fleets?.[fleetUUID1]).toEqual(expect.arrayContaining(['readF1']));
263
- expect(afterFirstElevationElevated.businessModels?.[bmUUID1]).toEqual(expect.arrayContaining(['writeBM1']));
264
- expect(afterFirstElevationElevated.demandSources?.[dsUUID1]).toEqual(expect.arrayContaining(['readDS1']));
265
- expect(afterFirstElevationCombined.fleets?.[fleetUUID1]).toEqual(expect.arrayContaining(['readF1']));
266
- expect(afterFirstElevationCombined.businessModels?.[bmUUID1]).toEqual(expect.arrayContaining(['writeBM1']));
267
- expect(afterFirstElevationCombined.demandSources?.[dsUUID1]).toEqual(expect.arrayContaining(['readDS1']));
268
- // ---------------------
269
- // Assertions After Second Elevation
270
- // ---------------------
271
- expect(afterSecondElevationElevated.fleets?.[fleetUUID1]).toEqual(expect.arrayContaining(['readF1', 'manageF1']));
272
- expect(afterSecondElevationElevated.fleets?.[fleetUUID2]).toEqual(expect.arrayContaining(['createF2']));
273
- expect(afterSecondElevationElevated.businessModels?.[bmUUID1]).toEqual(expect.arrayContaining(['writeBM1', 'writeBM2']));
274
- expect(afterSecondElevationElevated.demandSources?.[dsUUID2]).toEqual(expect.arrayContaining(['readDS2', 'readDS2']));
275
- expect(afterSecondElevationCombined.fleets?.[fleetUUID1]).toEqual(expect.arrayContaining(['readF1', 'manageF1']));
276
- expect(afterSecondElevationCombined.fleets?.[fleetUUID2]).toEqual(expect.arrayContaining(['createF2']));
277
- expect(afterSecondElevationCombined.businessModels?.[bmUUID1]).toEqual(expect.arrayContaining(['writeBM1', 'writeBM2']));
278
- expect(afterSecondElevationCombined.demandSources?.[dsUUID2]).toEqual(expect.arrayContaining(['readDS2', 'readDS2']));
279
- // ---------------------
280
- // Assertions After Closing Second Elevation
281
- // ---------------------
282
- expect(afterCloseSecondElevated.fleets?.[fleetUUID1]).toEqual(expect.arrayContaining(['readF1']));
283
- expect(afterCloseSecondElevated.fleets?.[fleetUUID2]).toBeUndefined();
284
- expect(afterCloseSecondElevated.businessModels?.[bmUUID1]).toEqual(expect.arrayContaining(['writeBM1']));
285
- expect(afterCloseSecondElevated.demandSources?.[dsUUID1]).toEqual(expect.arrayContaining(['readDS1']));
286
- expect(afterCloseSecondElevated.demandSources?.[dsUUID2]).toBeUndefined();
287
- expect(afterCloseSecondCombined.fleets?.[fleetUUID1]).toEqual(expect.arrayContaining(['readF1']));
288
- expect(afterCloseSecondCombined.fleets?.[fleetUUID2]).toBeUndefined();
289
- expect(afterCloseSecondCombined.businessModels?.[bmUUID1]).toEqual(expect.arrayContaining(['writeBM1']));
290
- expect(afterCloseSecondCombined.demandSources?.[dsUUID1]).toEqual(expect.arrayContaining(['readDS1']));
291
- expect(afterCloseSecondCombined.demandSources?.[dsUUID2]).toBeUndefined();
292
- // ---------------------
293
- // Assertions After Closing First Elevation
294
- // ---------------------
295
- expect(afterCloseFirstElevated.fleets).toEqual({});
296
- expect(afterCloseFirstElevated.businessModels).toEqual({});
297
- expect(afterCloseFirstElevated.demandSources).toEqual({});
298
- expect(afterCloseFirstCombined.fleets).toEqual({});
299
- expect(afterCloseFirstCombined.businessModels).toEqual({});
300
- expect(afterCloseFirstCombined.demandSources).toEqual({});
301
- });
302
125
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/zehut",
3
- "version": "3.1.2-beta.17",
3
+ "version": "3.1.2-beta.2",
4
4
  "description": "manage user's identity",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -28,6 +28,7 @@
28
28
  "axios": "^0.27.2",
29
29
  "express": "^4.18.1",
30
30
  "jsonwebtoken": "^8.5.1",
31
+ "merge-deep": "^3.0.3",
31
32
  "methods": "^1.1.2",
32
33
  "moment": "^2.29.1",
33
34
  "nock": "^13.2.9",
@@ -49,7 +50,12 @@
49
50
  "typescript": "^4.9.5"
50
51
  },
51
52
  "peerDependencies": {
52
- "@autofleet/shtinker": "*"
53
+ "@autofleet/shtinker": "1.2.3-beta.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "@autofleet/shtinker": {
57
+ "optional": true
58
+ }
53
59
  },
54
60
  "files": [
55
61
  "lib/**/*"