@autofleet/zehut 2.0.0 → 2.0.1-alpha

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
@@ -70,8 +70,4 @@ and run
70
70
  ```
71
71
  npm publish
72
72
  ```
73
-
74
- # Environment Variables
75
-
76
- when using this package locally or outside autofleet-prod project you must set INTEGRATION_MS_SERVICE_HOST in .env file
77
73
  # zehut
package/lib/index.d.ts CHANGED
@@ -31,6 +31,7 @@ declare const _default: {
31
31
  appMiddleware: (options: {
32
32
  appId: string;
33
33
  clientSecret: string;
34
+ appSecret: string;
34
35
  }) => (req: any, res: any, next: any) => Promise<void>;
35
36
  };
36
37
  export default _default;
@@ -21,13 +21,15 @@ 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
- privateElevatedPermissionsHash: Map<string, PartialUserPayload | undefined>;
24
+ privateElevatedPermissions: 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;
31
33
  constructor(id?: string, accountType?: AccountType, elevatedPermissions?: PartialUserPayload);
32
34
  getUserPermissions(): Promise<UserPayload>;
33
35
  useCustomPermissionLoader(customPermissionLoader: any): Promise<UserPayload>;
@@ -35,7 +37,6 @@ export default class ApiUser {
35
37
  get fleets(): string[] | undefined;
36
38
  get demandSources(): string[] | undefined;
37
39
  getUserProperty(key: any): string[] | undefined;
38
- get elevatedPermissions(): UserPayload;
39
40
  get permissions(): UserPayload | undefined;
40
41
  elevatePermissions(addedPermissions: PartialUserPayload): () => void;
41
42
  getUserPermissionsLegacy(): Promise<any>;
@@ -15,8 +15,6 @@ 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");
20
18
  const outbreak_1 = require("@autofleet/outbreak");
21
19
  const services_1 = require("../services");
22
20
  exports.ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';
@@ -26,10 +24,9 @@ class ApiUser {
26
24
  this.id = id;
27
25
  this.emptyUser = !!id;
28
26
  this.appPermission = {};
29
- this.privateElevatedPermissionsHash = new Map();
30
- if (elevatedPermissions) {
31
- this.privateElevatedPermissionsHash.set('initial', elevatedPermissions);
32
- }
27
+ this.elevationCallsCounter = 0;
28
+ this.privateElevatedPermissions = elevatedPermissions;
29
+ this.elevationInitialState = elevatedPermissions;
33
30
  if (accountType) {
34
31
  this.accountType = accountType;
35
32
  }
@@ -82,37 +79,29 @@ class ApiUser {
82
79
  }
83
80
  return Object.keys(this.privatePermissions[key] || {});
84
81
  }
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
- }
96
82
  get permissions() {
97
83
  if (!this.privatePermissions) {
98
84
  throw new Error('Cannot get permissions without calling (async) getUserPermissions before');
99
85
  }
100
- const permissions = merge_deep_1.default(this.privatePermissions, this.elevatedPermissions);
101
- return permissions;
86
+ return Object.assign(Object.assign({}, this.privatePermissions), this.privateElevatedPermissions);
102
87
  }
103
88
  elevatePermissions(addedPermissions) {
104
- const elevationId = uuid_1.v4();
89
+ if (this.elevationCallsCounter > 1) {
90
+ throw new Error('Maximum one elvation call in parallel is allowed');
91
+ }
105
92
  const currentUserTrace = outbreak_1.getCurrentContext();
106
93
  if (!currentUserTrace) {
107
94
  throw new Error('Cannot find current user cross services trace');
108
95
  }
109
96
  const currentElevation = JSON.parse(currentUserTrace.context[exports.ELEVATED_PERMISSIONS_HEADER] || '{}');
97
+ this.elevationInitialState = currentElevation;
110
98
  const newElevation = Object.assign(currentElevation, addedPermissions);
111
- this.privateElevatedPermissionsHash.set(elevationId, newElevation);
112
- currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));
99
+ this.privateElevatedPermissions = newElevation;
100
+ currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(newElevation));
101
+ this.elevationCallsCounter += 1;
113
102
  return () => {
114
- this.privateElevatedPermissionsHash.delete(elevationId);
115
- currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));
103
+ this.elevationCallsCounter -= 1;
104
+ this.privateElevatedPermissions = this.elevationInitialState;
116
105
  };
117
106
  }
118
107
  getUserPermissionsLegacy() {
@@ -31,7 +31,6 @@ 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()];
35
34
  index_1.enableTracing({
36
35
  outbreakOptions: {
37
36
  headersPrefix: 'x-af-',
@@ -41,24 +40,17 @@ describe('E2E', () => {
41
40
  app.use(index_2.middleware());
42
41
  app.get('/', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
43
42
  const user = index_1.getUser();
44
- const closeElevation1 = user.elevatePermissions({
43
+ const closeElevation = user.elevatePermissions({
45
44
  businessModels: {
46
- [uuid1]: ['vehicle:write'],
47
- },
48
- });
49
- const closeElevation2 = user.elevatePermissions({
50
- businessModels: {
51
- [uuid2]: ['vehicle:write'],
45
+ [uuid_1.v4()]: ['vehicle:write'],
52
46
  },
53
47
  });
54
48
  const { data: res1 } = yield axios_1.default.post('http://localhost:8082');
55
- closeElevation1();
56
- const { data: res2 } = yield axios_1.default.post('http://localhost:8082');
57
- closeElevation2();
58
- res.json([res1, res2]);
49
+ closeElevation();
50
+ res.json(res1);
59
51
  }));
60
- }, 8089);
61
- const server2NumberOfPermissions = [];
52
+ }, 8081);
53
+ let server2NumberOfPermissions = 0;
62
54
  const closeServer2 = yield generateApp((app) => {
63
55
  app.use(index_2.middleware());
64
56
  app.post('/', (req, res) => {
@@ -68,7 +60,7 @@ describe('E2E', () => {
68
60
  fleets: {},
69
61
  demandSources: {},
70
62
  };
71
- server2NumberOfPermissions.push(Object.keys(user.permissions.businessModels).length);
63
+ server2NumberOfPermissions = Object.keys(user.permissions.businessModels).length;
72
64
  server2TraceId = req.headers['x-trace-id'];
73
65
  res.json({
74
66
  value: req.headers['x-af-header'],
@@ -77,7 +69,7 @@ describe('E2E', () => {
77
69
  });
78
70
  });
79
71
  }, 8082);
80
- const { data: [res1, res2], headers } = yield axios_1.default.get('http://localhost:8089', {
72
+ const { data: res1, headers } = yield axios_1.default.get('http://localhost:8081', {
81
73
  headers: {
82
74
  'x-af-header': 'testHeader',
83
75
  'x-af-id': 'my-wakanda-id',
@@ -86,13 +78,10 @@ describe('E2E', () => {
86
78
  });
87
79
  closeServer1();
88
80
  closeServer2();
89
- expect(server2NumberOfPermissions).toEqual([2, 1]);
81
+ expect(server2NumberOfPermissions).toEqual(1);
90
82
  expect(headers['x-trace-id']).toEqual(server2TraceId);
91
83
  expect(res1.value).toEqual('testHeader');
92
84
  expect(res1.wkanda).toEqual('my-wakanda-id');
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();
85
+ expect(JSON.parse(res1.addedPermissions).businessModels).toBeDefined();
97
86
  }));
98
87
  });
@@ -12,6 +12,7 @@ export declare const middlewareWithDecode: (options?: {
12
12
  export declare const appMiddleware: (options: {
13
13
  appId: string;
14
14
  clientSecret: string;
15
+ appSecret: string;
15
16
  }) => (req: any, res: any, next: any) => Promise<void>;
16
17
  export declare const eagerLoadPermissionsMiddleware: (req: any, res: any, next: any) => Promise<any>;
17
18
  export declare const getDecodedBearer: (req: any) => any;
package/lib/user/index.js CHANGED
@@ -27,52 +27,39 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
27
27
  step((generator = generator.apply(thisArg, _arguments || [])).next());
28
28
  });
29
29
  };
30
- var __importDefault = (this && this.__importDefault) || function (mod) {
31
- return (mod && mod.__esModule) ? mod : { "default": mod };
32
- };
33
30
  Object.defineProperty(exports, "__esModule", { value: true });
34
31
  exports.getDecodedBearer = exports.eagerLoadPermissionsMiddleware = exports.appMiddleware = exports.middlewareWithDecode = exports.middleware = void 0;
35
32
  const jsonwebtoken_1 = require("jsonwebtoken");
36
33
  const ApiUser_1 = __importStar(require("./ApiUser"));
37
- const tracer_1 = require("../tracer");
38
- const app_auth_1 = require("../app-auth");
39
- const appDoesNotExist_1 = __importDefault(require("../exceptions/appDoesNotExist"));
40
34
  const utils_1 = require("../utils");
35
+ const tracer_1 = require("../tracer");
41
36
  exports.middleware = (options = {}) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
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();
48
- }
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
- }
37
+ const { eagerLoadUserPermissions, eagerLoadUserPermissionsLegacy, customPermissionLoader, } = options;
38
+ const userId = req.headers['x-af-user-id'];
39
+ const trace = tracer_1.newTrace('userPayload');
40
+ if (!userId) {
41
+ return next();
42
+ }
43
+ const elevatedPermissionsFromHeader = req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER] && req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER].length > 0
44
+ ? JSON.parse(req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER])
45
+ : {};
46
+ const userObject = new ApiUser_1.default(userId, 'user', elevatedPermissionsFromHeader);
47
+ if (eagerLoadUserPermissions) {
48
+ if (customPermissionLoader) {
49
+ yield userObject.useCustomPermissionLoader(customPermissionLoader);
60
50
  }
61
- if (eagerLoadUserPermissionsLegacy) {
62
- yield userObject.getUserPermissionsLegacy();
51
+ else {
52
+ yield userObject.getUserPermissions();
63
53
  }
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();
69
54
  }
70
- catch (e) {
71
- res.status(401);
72
- return res.json({
73
- error: 'cannot authenticate user',
74
- });
55
+ if (eagerLoadUserPermissionsLegacy) {
56
+ yield userObject.getUserPermissionsLegacy();
75
57
  }
58
+ req.user = userObject;
59
+ trace.context.set('userObject', userObject);
60
+ // Added in order to support outbreak.
61
+ req.headers['x-af-user-permissions'] = userObject;
62
+ return next();
76
63
  });
77
64
  exports.middlewareWithDecode = (options = {}) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
78
65
  var _a, _b;
@@ -81,7 +68,7 @@ exports.middlewareWithDecode = (options = {}) => (req, res, next) => __awaiter(v
81
68
  let decoded;
82
69
  if (req.headers.authorization) {
83
70
  try {
84
- decoded = yield utils_1.decodeBearer(req.headers.authorization);
71
+ decoded = utils_1.decodeBearer(req.headers.authorization);
85
72
  }
86
73
  catch (e) {
87
74
  if (e instanceof jsonwebtoken_1.TokenExpiredError) {
@@ -131,7 +118,7 @@ exports.middlewareWithDecode = (options = {}) => (req, res, next) => __awaiter(v
131
118
  return next();
132
119
  });
133
120
  exports.appMiddleware = (options) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
134
- const { appId, clientSecret, } = options;
121
+ const { appId, clientSecret, appSecret, } = options;
135
122
  const trace = tracer_1.newTrace('userPayload');
136
123
  let decoded;
137
124
  if (!req.headers.authorization) {
@@ -141,10 +128,7 @@ exports.appMiddleware = (options) => (req, res, next) => __awaiter(void 0, void
141
128
  });
142
129
  }
143
130
  try {
144
- decoded = yield app_auth_1.decodeAppBearer(req.headers.authorization, appId);
145
- if (!decoded) {
146
- throw new appDoesNotExist_1.default();
147
- }
131
+ decoded = utils_1.decodeBearer(req.headers.authorization, appSecret);
148
132
  }
149
133
  catch (e) {
150
134
  if (e instanceof jsonwebtoken_1.TokenExpiredError) {
@@ -159,12 +143,6 @@ exports.appMiddleware = (options) => (req, res, next) => __awaiter(void 0, void
159
143
  errors: [e.message],
160
144
  });
161
145
  }
162
- if (e instanceof appDoesNotExist_1.default) {
163
- res.status(400);
164
- return res.json({
165
- errors: [e.message],
166
- });
167
- }
168
146
  res.status(500);
169
147
  return res.json({
170
148
  errors: ['Server error while parsing token'],
@@ -175,14 +153,13 @@ exports.appMiddleware = (options) => (req, res, next) => __awaiter(void 0, void
175
153
  req.headers['X-AF-USER-ID'] = userId;
176
154
  }
177
155
  const userObject = new ApiUser_1.default(userId);
178
- if (appId) {
156
+ if (appId && clientSecret) {
179
157
  req.headers['x-autofleet-apps-secret'] = clientSecret;
180
158
  // Won't work until we find a better solution for identity ms
181
159
  yield userObject.getUserAppPermissions(appId, clientSecret);
182
160
  }
183
161
  req.user = userObject;
184
162
  trace.context.set('userObject', userObject);
185
- trace.context.set('accessToken', utils_1.getAuthFromBearer(req.headers.authorization));
186
163
  // Added in order to support outbreak.
187
164
  req.headers['x-af-user-permissions'] = userObject;
188
165
  return next();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/zehut",
3
- "version": "2.0.0",
3
+ "version": "2.0.1-alpha",
4
4
  "description": "manage user's identity",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
package/lib/app-auth.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export declare const decodeAppBearer: (bearer: string, appId: string) => Promise<any>;
2
- export declare const getClientSecret: (appId: string) => Promise<any>;
package/lib/app-auth.js DELETED
@@ -1,21 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.getClientSecret = exports.decodeAppBearer = void 0;
13
- const services_1 = require("./services");
14
- exports.decodeAppBearer = (bearer, appId) => __awaiter(void 0, void 0, void 0, function* () {
15
- const { data: decoded } = yield services_1.AutofleetApiNetwork.post('/api/v1/auth', { bearer, appId });
16
- return decoded;
17
- });
18
- exports.getClientSecret = (appId) => __awaiter(void 0, void 0, void 0, function* () {
19
- const { data: secret } = yield services_1.AutofleetApiNetwork.get(`/api/v1/auth/client-secret/${appId}`);
20
- return secret;
21
- });
@@ -1,3 +0,0 @@
1
- export default class AppDoesNotExist extends Error {
2
- mesaage: 'app does not exist';
3
- }
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- class AppDoesNotExist extends Error {
4
- }
5
- exports.default = AppDoesNotExist;