@autofleet/zehut 1.8.1-beta5 → 1.8.1-beta6

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.
@@ -21,15 +21,13 @@ export declare type CustomPermissionLoader = (string: any) => Promise<UserPayloa
21
21
  export default class ApiUser {
22
22
  id: string | undefined;
23
23
  privatePermissions: UserPayload | undefined;
24
- privateElevatedPermissions: PartialUserPayload | undefined;
24
+ privateElevatedPermissionsHash: Map<string, PartialUserPayload | undefined>;
25
25
  privatePermissionsLegacy: any;
26
26
  appPermission: {
27
27
  [key: string]: any;
28
28
  };
29
29
  emptyUser: boolean;
30
30
  accountType: AccountType | undefined;
31
- elevationCallsCounter: number;
32
- elevationInitialState: PartialUserPayload | undefined;
33
31
  constructor(id?: string, accountType?: AccountType, elevatedPermissions?: PartialUserPayload);
34
32
  getUserPermissions(): Promise<UserPayload>;
35
33
  useCustomPermissionLoader(customPermissionLoader: any): Promise<UserPayload>;
@@ -37,6 +35,7 @@ export default class ApiUser {
37
35
  get fleets(): string[] | undefined;
38
36
  get demandSources(): string[] | undefined;
39
37
  getUserProperty(key: any): string[] | undefined;
38
+ get elevatedPermissions(): UserPayload;
40
39
  get permissions(): UserPayload | undefined;
41
40
  elevatePermissions(addedPermissions: PartialUserPayload): () => void;
42
41
  getUserPermissionsLegacy(): Promise<any>;
@@ -15,6 +15,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.ELEVATED_PERMISSIONS_HEADER = void 0;
16
16
  /* eslint-disable consistent-return */
17
17
  const node_cache_1 = __importDefault(require("node-cache"));
18
+ const merge_deep_1 = __importDefault(require("merge-deep"));
19
+ const uuid_1 = require("uuid");
18
20
  const outbreak_1 = require("@autofleet/outbreak");
19
21
  const services_1 = require("../services");
20
22
  exports.ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';
@@ -24,9 +26,10 @@ class ApiUser {
24
26
  this.id = id;
25
27
  this.emptyUser = !!id;
26
28
  this.appPermission = {};
27
- this.elevationCallsCounter = 0;
28
- this.privateElevatedPermissions = elevatedPermissions;
29
- this.elevationInitialState = elevatedPermissions;
29
+ this.privateElevatedPermissionsHash = new Map();
30
+ if (elevatedPermissions) {
31
+ this.privateElevatedPermissionsHash.set('initial', elevatedPermissions);
32
+ }
30
33
  if (accountType) {
31
34
  this.accountType = accountType;
32
35
  }
@@ -79,29 +82,37 @@ class ApiUser {
79
82
  }
80
83
  return Object.keys(this.privatePermissions[key] || {});
81
84
  }
85
+ get elevatedPermissions() {
86
+ let permissions = {
87
+ fleets: {},
88
+ businessModels: {},
89
+ demandSources: {},
90
+ };
91
+ [...this.privateElevatedPermissionsHash.values()].forEach((p) => {
92
+ permissions = merge_deep_1.default(permissions, p);
93
+ });
94
+ return permissions;
95
+ }
82
96
  get permissions() {
83
97
  if (!this.privatePermissions) {
84
98
  throw new Error('Cannot get permissions without calling (async) getUserPermissions before');
85
99
  }
86
- return Object.assign(Object.assign({}, this.privatePermissions), this.privateElevatedPermissions);
100
+ const permissions = merge_deep_1.default(this.privatePermissions, this.elevatedPermissions);
101
+ return permissions;
87
102
  }
88
103
  elevatePermissions(addedPermissions) {
89
- if (this.elevationCallsCounter > 1) {
90
- throw new Error('Maximum one elvation call in parallel is allowed');
91
- }
104
+ const elevationId = uuid_1.v4();
92
105
  const currentUserTrace = outbreak_1.getCurrentContext();
93
106
  if (!currentUserTrace) {
94
107
  throw new Error('Cannot find current user cross services trace');
95
108
  }
96
109
  const currentElevation = JSON.parse(currentUserTrace.context[exports.ELEVATED_PERMISSIONS_HEADER] || '{}');
97
- this.elevationInitialState = currentElevation;
98
110
  const newElevation = Object.assign(currentElevation, addedPermissions);
99
- this.privateElevatedPermissions = newElevation;
100
- currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(newElevation));
101
- this.elevationCallsCounter += 1;
111
+ this.privateElevatedPermissionsHash.set(elevationId, newElevation);
112
+ currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));
102
113
  return () => {
103
- this.elevationCallsCounter -= 1;
104
- this.privateElevatedPermissions = this.elevationInitialState;
114
+ this.privateElevatedPermissionsHash.delete(elevationId);
115
+ currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));
105
116
  };
106
117
  }
107
118
  getUserPermissionsLegacy() {
@@ -31,6 +31,7 @@ const generateApp = (addEndpoints, port) => __awaiter(void 0, void 0, void 0, fu
31
31
  describe('E2E', () => {
32
32
  it('Basic functionality', () => __awaiter(void 0, void 0, void 0, function* () {
33
33
  let server2TraceId = null;
34
+ const [uuid1, uuid2] = [uuid_1.v4(), uuid_1.v4()];
34
35
  index_1.enableTracing({
35
36
  outbreakOptions: {
36
37
  headersPrefix: 'x-af-',
@@ -40,17 +41,24 @@ describe('E2E', () => {
40
41
  app.use(index_2.middleware());
41
42
  app.get('/', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
42
43
  const user = index_1.getUser();
43
- const closeElevation = user.elevatePermissions({
44
+ const closeElevation1 = user.elevatePermissions({
44
45
  businessModels: {
45
- [uuid_1.v4()]: ['vehicle:write'],
46
+ [uuid1]: ['vehicle:write'],
47
+ },
48
+ });
49
+ const closeElevation2 = user.elevatePermissions({
50
+ businessModels: {
51
+ [uuid2]: ['vehicle:write'],
46
52
  },
47
53
  });
48
54
  const { data: res1 } = yield axios_1.default.post('http://localhost:8082');
49
- closeElevation();
50
- res.json(res1);
55
+ closeElevation1();
56
+ const { data: res2 } = yield axios_1.default.post('http://localhost:8082');
57
+ closeElevation2();
58
+ res.json([res1, res2]);
51
59
  }));
52
- }, 8081);
53
- let server2NumberOfPermissions = 0;
60
+ }, 8089);
61
+ const server2NumberOfPermissions = [];
54
62
  const closeServer2 = yield generateApp((app) => {
55
63
  app.use(index_2.middleware());
56
64
  app.post('/', (req, res) => {
@@ -60,7 +68,7 @@ describe('E2E', () => {
60
68
  fleets: {},
61
69
  demandSources: {},
62
70
  };
63
- server2NumberOfPermissions = Object.keys(user.permissions.businessModels).length;
71
+ server2NumberOfPermissions.push(Object.keys(user.permissions.businessModels).length);
64
72
  server2TraceId = req.headers['x-trace-id'];
65
73
  res.json({
66
74
  value: req.headers['x-af-header'],
@@ -69,7 +77,7 @@ describe('E2E', () => {
69
77
  });
70
78
  });
71
79
  }, 8082);
72
- const { data: res1, headers } = yield axios_1.default.get('http://localhost:8081', {
80
+ const { data: [res1, res2], headers } = yield axios_1.default.get('http://localhost:8089', {
73
81
  headers: {
74
82
  'x-af-header': 'testHeader',
75
83
  'x-af-id': 'my-wakanda-id',
@@ -78,10 +86,13 @@ describe('E2E', () => {
78
86
  });
79
87
  closeServer1();
80
88
  closeServer2();
81
- expect(server2NumberOfPermissions).toEqual(1);
89
+ expect(server2NumberOfPermissions).toEqual([2, 1]);
82
90
  expect(headers['x-trace-id']).toEqual(server2TraceId);
83
91
  expect(res1.value).toEqual('testHeader');
84
92
  expect(res1.wkanda).toEqual('my-wakanda-id');
85
- expect(JSON.parse(res1.addedPermissions).businessModels).toBeDefined();
93
+ expect(JSON.parse(res1.addedPermissions).businessModels[uuid1]).toBeDefined();
94
+ expect(JSON.parse(res1.addedPermissions).businessModels[uuid2]).toBeDefined();
95
+ expect(JSON.parse(res2.addedPermissions).businessModels[uuid1]).not.toBeDefined();
96
+ expect(JSON.parse(res2.addedPermissions).businessModels[uuid2]).toBeDefined();
86
97
  }));
87
98
  });
package/lib/user/index.js CHANGED
@@ -39,32 +39,40 @@ const app_auth_1 = require("../app-auth");
39
39
  const appDoesNotExist_1 = __importDefault(require("../exceptions/appDoesNotExist"));
40
40
  const utils_1 = require("../utils");
41
41
  exports.middleware = (options = {}) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
42
- const { eagerLoadUserPermissions, eagerLoadUserPermissionsLegacy, customPermissionLoader, } = options;
43
- const userId = req.headers['x-af-user-id'];
44
- const trace = tracer_1.newTrace('userPayload');
45
- if (!userId) {
46
- return next();
47
- }
48
- const elevatedPermissionsFromHeader = req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER] && req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER].length > 0
49
- ? JSON.parse(req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER])
50
- : {};
51
- const userObject = new ApiUser_1.default(userId, 'user', elevatedPermissionsFromHeader);
52
- if (eagerLoadUserPermissions) {
53
- if (customPermissionLoader) {
54
- yield userObject.useCustomPermissionLoader(customPermissionLoader);
42
+ try {
43
+ const { eagerLoadUserPermissions, eagerLoadUserPermissionsLegacy, customPermissionLoader, } = options;
44
+ const userId = req.headers['x-af-user-id'];
45
+ const trace = tracer_1.newTrace('userPayload');
46
+ if (!userId) {
47
+ return next();
55
48
  }
56
- else {
57
- yield userObject.getUserPermissions();
49
+ const elevatedPermissionsFromHeader = req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER] && req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER].length > 0
50
+ ? JSON.parse(req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER])
51
+ : {};
52
+ const userObject = new ApiUser_1.default(userId, 'user', elevatedPermissionsFromHeader);
53
+ if (eagerLoadUserPermissions) {
54
+ if (customPermissionLoader) {
55
+ yield userObject.useCustomPermissionLoader(customPermissionLoader);
56
+ }
57
+ else {
58
+ yield userObject.getUserPermissions();
59
+ }
60
+ }
61
+ if (eagerLoadUserPermissionsLegacy) {
62
+ yield userObject.getUserPermissionsLegacy();
58
63
  }
64
+ req.user = userObject;
65
+ trace.context.set('userObject', userObject);
66
+ // Added in order to support outbreak.
67
+ req.headers['x-af-user-permissions'] = userObject;
68
+ return next();
59
69
  }
60
- if (eagerLoadUserPermissionsLegacy) {
61
- yield userObject.getUserPermissionsLegacy();
70
+ catch (e) {
71
+ res.status(401);
72
+ return res.json({
73
+ error: 'cannot authenticate user',
74
+ });
62
75
  }
63
- req.user = userObject;
64
- trace.context.set('userObject', userObject);
65
- // Added in order to support outbreak.
66
- req.headers['x-af-user-permissions'] = userObject;
67
- return next();
68
76
  });
69
77
  exports.middlewareWithDecode = (options = {}) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
70
78
  var _a, _b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/zehut",
3
- "version": "1.8.1-beta5",
3
+ "version": "1.8.1-beta6",
4
4
  "description": "manage user's identity",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -33,6 +33,7 @@
33
33
  "axios": "^0.27.2",
34
34
  "express": "^4.18.1",
35
35
  "jsonwebtoken": "^8.5.1",
36
+ "merge-deep": "^3.0.3",
36
37
  "moment": "^2.29.1",
37
38
  "node-cache": "^5.1.2",
38
39
  "uuid": "^8.3.2"