@autofleet/zehut 3.1.2-beta.9 → 3.2.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/README.md +2 -76
- package/lib/check-permission.d.ts +3 -3
- package/lib/check-permission.js +7 -26
- package/lib/errors.d.ts +3 -3
- package/lib/errors.js +1 -1
- package/lib/exceptions/appDoesNotExist.d.ts +2 -1
- package/lib/exceptions/appDoesNotExist.js +5 -0
- package/lib/index.d.ts +16 -11
- package/lib/index.js +5 -3
- package/lib/secret-getter.d.ts +2 -2
- package/lib/services.d.ts +3 -2
- package/lib/tracer.js +2 -2
- package/lib/user/ApiUser.d.ts +9 -11
- package/lib/user/ApiUser.js +39 -29
- package/lib/user/index.d.ts +14 -6
- package/lib/user/index.js +58 -78
- package/lib/utils.d.ts +3 -8
- package/lib/utils.js +17 -44
- package/package.json +29 -21
- package/lib/secret-getter.test.d.ts +0 -1
- package/lib/secret-getter.test.js +0 -90
- package/lib/test-helpers/index.d.ts +0 -9
- package/lib/test-helpers/index.js +0 -35
- package/lib/user/api-user-flows.test.d.ts +0 -1
- package/lib/user/api-user-flows.test.js +0 -125
package/README.md
CHANGED
|
@@ -1,77 +1,3 @@
|
|
|
1
|
-
# AutoFleet
|
|
2
|
-
This respostory is made in order have as much of common code as we can.
|
|
1
|
+
# AutoFleet Zehut
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Make sure you have:
|
|
7
|
-
* Tests
|
|
8
|
-
* Docs
|
|
9
|
-
|
|
10
|
-
## Consts
|
|
11
|
-
|
|
12
|
-
Currently we suppurt:
|
|
13
|
-
```
|
|
14
|
-
{
|
|
15
|
-
'OK'
|
|
16
|
-
'ERROR'
|
|
17
|
-
'FAIL'
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Network
|
|
22
|
-
Server 2 Servers communication.
|
|
23
|
-
|
|
24
|
-
Implemented:
|
|
25
|
-
* Retriving service urls from environment
|
|
26
|
-
* Retry - Using https://github.com/softonic/axios-retry
|
|
27
|
-
* Caching - TBD
|
|
28
|
-
* Syntatic response for fail - TBD
|
|
29
|
-
* Circuit Breaking - TBD
|
|
30
|
-
|
|
31
|
-
The API is just like [axios](https://github.com/axios/axios) api but the creation of new instance **must** have either `serviceName` or `serviceUrl` in options.
|
|
32
|
-
|
|
33
|
-
In case `serviceName` used the constractor will look for an environment varible with the the name `<SERVICE_NAME>_SERVICE_HOST`.
|
|
34
|
-
|
|
35
|
-
For Example:
|
|
36
|
-
```
|
|
37
|
-
const { Network } = require('@autofleet/node-common');
|
|
38
|
-
|
|
39
|
-
n = new Network({ serviceName: 'TEST' });
|
|
40
|
-
|
|
41
|
-
n.get('/posts/1');
|
|
42
|
-
```
|
|
43
|
-
.env file:
|
|
44
|
-
```
|
|
45
|
-
RIDE_SERVICE_HOST=jsonplaceholder.typicode.com
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
To learn more [click here](https://blog.risingstack.com/designing-microservices-architecture-for-failure/).
|
|
49
|
-
|
|
50
|
-
## Settings
|
|
51
|
-
|
|
52
|
-
### Adding settings
|
|
53
|
-
For adding new setting you need to add it to the map.js file, please specify
|
|
54
|
-
* name - descriptive name
|
|
55
|
-
* description - few words about what it does + unit
|
|
56
|
-
* type - supportable types: 'number', 'string', 'json'
|
|
57
|
-
* defaultValue - default value
|
|
58
|
-
* context - 'security' and 'operation' will not show in the simulator configuration
|
|
59
|
-
|
|
60
|
-
See example.
|
|
61
|
-
|
|
62
|
-
## DeLorean
|
|
63
|
-
|
|
64
|
-
Use this model to mock time on server - more info TBD.
|
|
65
|
-
|
|
66
|
-
## Publish package
|
|
67
|
-
|
|
68
|
-
bump the version number in package.json
|
|
69
|
-
and run
|
|
70
|
-
```
|
|
71
|
-
npm publish
|
|
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
|
-
# zehut
|
|
3
|
+
This package handles authorization and authentication for AutoFleet services.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ApiUser from './user/ApiUser';
|
|
2
2
|
export declare const getUser: () => ApiUser | undefined;
|
|
3
3
|
export declare const isUserExist: () => string;
|
|
4
|
-
export declare const checkFleetPermission: (fleetId:
|
|
5
|
-
export declare const checkBusinessModelPermission: (businessModelId:
|
|
6
|
-
export declare const checkDemandSourcePermission: (demandSourceId:
|
|
4
|
+
export declare const checkFleetPermission: (fleetId: string) => boolean;
|
|
5
|
+
export declare const checkBusinessModelPermission: (businessModelId: string) => boolean;
|
|
6
|
+
export declare const checkDemandSourcePermission: (demandSourceId: string) => boolean;
|
package/lib/check-permission.js
CHANGED
|
@@ -2,34 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkDemandSourcePermission = exports.checkBusinessModelPermission = exports.checkFleetPermission = exports.isUserExist = exports.getUser = void 0;
|
|
4
4
|
const tracer_1 = require("./tracer");
|
|
5
|
-
const
|
|
5
|
+
const user_1 = require("./user");
|
|
6
|
+
const getUser = () => (0, tracer_1.getCurrentTrace)().context?.get(user_1.USER_OBJECT);
|
|
6
7
|
exports.getUser = getUser;
|
|
7
|
-
const isUserExist = () =>
|
|
8
|
-
const u = (0, exports.getUser)();
|
|
9
|
-
return u && u.id;
|
|
10
|
-
};
|
|
8
|
+
const isUserExist = () => (0, exports.getUser)()?.id;
|
|
11
9
|
exports.isUserExist = isUserExist;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const user = (0, exports.getUser)();
|
|
15
|
-
return !user || Object.keys(user.permissions.fleets).includes(fleetId);
|
|
16
|
-
}
|
|
17
|
-
return true;
|
|
18
|
-
};
|
|
10
|
+
const checkUserPermissions = (entityId, entityType) => !(0, exports.isUserExist)() || Object.hasOwn((0, exports.getUser)().permissions[entityType], entityId);
|
|
11
|
+
const checkFleetPermission = (fleetId) => checkUserPermissions(fleetId, 'fleets');
|
|
19
12
|
exports.checkFleetPermission = checkFleetPermission;
|
|
20
|
-
const checkBusinessModelPermission = (businessModelId) =>
|
|
21
|
-
if ((0, exports.isUserExist)()) {
|
|
22
|
-
const user = (0, exports.getUser)();
|
|
23
|
-
return !user || Object.keys(user.permissions.businessModels).includes(businessModelId);
|
|
24
|
-
}
|
|
25
|
-
return true;
|
|
26
|
-
};
|
|
13
|
+
const checkBusinessModelPermission = (businessModelId) => checkUserPermissions(businessModelId, 'businessModels');
|
|
27
14
|
exports.checkBusinessModelPermission = checkBusinessModelPermission;
|
|
28
|
-
const checkDemandSourcePermission = (demandSourceId) =>
|
|
29
|
-
if ((0, exports.isUserExist)()) {
|
|
30
|
-
const user = (0, exports.getUser)();
|
|
31
|
-
return !user || Object.keys(user.permissions.demandSources).includes(demandSourceId);
|
|
32
|
-
}
|
|
33
|
-
return true;
|
|
34
|
-
};
|
|
15
|
+
const checkDemandSourcePermission = (demandSourceId) => checkUserPermissions(demandSourceId, 'demandSources');
|
|
35
16
|
exports.checkDemandSourcePermission = checkDemandSourcePermission;
|
package/lib/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import ApiUser from './user';
|
|
1
|
+
import type ApiUser from './user';
|
|
2
2
|
export declare class UnauthorizedAccessError extends Error {
|
|
3
|
-
user: ApiUser;
|
|
4
|
-
constructor(user?:
|
|
3
|
+
user: ApiUser | null;
|
|
4
|
+
constructor(user?: ApiUser | null, message?: string);
|
|
5
5
|
}
|
package/lib/errors.js
CHANGED
|
@@ -5,8 +5,8 @@ exports.UnauthorizedAccessError = void 0;
|
|
|
5
5
|
class UnauthorizedAccessError extends Error {
|
|
6
6
|
constructor(user = null, message = 'UnauthorizedAccessError') {
|
|
7
7
|
super(message);
|
|
8
|
-
this.name = 'UnauthorizedAccessError';
|
|
9
8
|
this.user = user;
|
|
9
|
+
this.name = 'UnauthorizedAccessError';
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
exports.UnauthorizedAccessError = UnauthorizedAccessError;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
class AppDoesNotExist extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.name = 'AppDoesNotExist';
|
|
7
|
+
this.message = 'app does not exist';
|
|
8
|
+
}
|
|
4
9
|
}
|
|
5
10
|
exports.default = AppDoesNotExist;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
/// <reference types="express" />
|
|
2
|
+
/// <reference types="qs" />
|
|
1
3
|
import * as outbreak from '@autofleet/outbreak';
|
|
2
4
|
import User, { middleware, eagerLoadPermissionsMiddleware, middlewareWithDecode, getDecodedBearer, appMiddleware, createOrSetRabbitTrace } from './user';
|
|
5
|
+
import { type UserPayload, CONTEXTS_IDS_HEADER } from './user/ApiUser';
|
|
3
6
|
import { newTrace, traceTypes } from './tracer';
|
|
4
7
|
import { checkFleetPermission, checkBusinessModelPermission, checkDemandSourcePermission, isUserExist, getUser } from './check-permission';
|
|
5
8
|
import { UnauthorizedAccessError } from './errors';
|
|
@@ -8,10 +11,11 @@ import { AUTHORIZATION_METHODS, getAuthorizationHeader } from './authorization';
|
|
|
8
11
|
declare const getCurrentPayload: () => import("./tracer").Trace | {
|
|
9
12
|
[x: string]: never;
|
|
10
13
|
};
|
|
14
|
+
type OutbreakOptions = Parameters<typeof outbreak.default>[0];
|
|
11
15
|
declare const enableTracing: ({ outbreakOptions }?: {
|
|
12
|
-
outbreakOptions?:
|
|
16
|
+
outbreakOptions?: OutbreakOptions;
|
|
13
17
|
}) => void;
|
|
14
|
-
export { traceTypes, newTrace, enableTracing, User, middleware, middlewareWithDecode, eagerLoadPermissionsMiddleware, getCurrentPayload, getDecodedBearer, checkFleetPermission, checkBusinessModelPermission, checkDemandSourcePermission, isUserExist, getUser, getRefreshTokenSecret, getTokenSecret, UnauthorizedAccessError, appMiddleware, createOrSetRabbitTrace, outbreak, AUTHORIZATION_METHODS, getAuthorizationHeader, };
|
|
18
|
+
export { traceTypes, newTrace, enableTracing, User, middleware, middlewareWithDecode, eagerLoadPermissionsMiddleware, getCurrentPayload, getDecodedBearer, checkFleetPermission, checkBusinessModelPermission, checkDemandSourcePermission, isUserExist, getUser, getRefreshTokenSecret, getTokenSecret, UnauthorizedAccessError, appMiddleware, createOrSetRabbitTrace, outbreak, AUTHORIZATION_METHODS, getAuthorizationHeader, type UserPayload, CONTEXTS_IDS_HEADER, };
|
|
15
19
|
declare const _default: {
|
|
16
20
|
traceTypes: {
|
|
17
21
|
HTTP_REQUEST: string;
|
|
@@ -24,28 +28,28 @@ declare const _default: {
|
|
|
24
28
|
eagerLoadUserPermissions?: boolean;
|
|
25
29
|
eagerLoadUserPermissionsLegacy?: boolean;
|
|
26
30
|
customPermissionLoader?: import("./user/ApiUser").CustomPermissionLoader;
|
|
27
|
-
}) => (
|
|
31
|
+
}) => import("express").Handler;
|
|
28
32
|
middlewareWithDecode: (options?: {
|
|
29
33
|
eagerLoadUserPermissions?: boolean;
|
|
30
34
|
eagerLoadUserPermissionsLegacy?: boolean;
|
|
31
35
|
returnErrorIfNoToken?: boolean;
|
|
32
|
-
}) => (
|
|
33
|
-
eagerLoadPermissionsMiddleware: (
|
|
36
|
+
}) => import("express").Handler;
|
|
37
|
+
eagerLoadPermissionsMiddleware: import("express").Handler;
|
|
34
38
|
getCurrentPayload: () => import("./tracer").Trace | {
|
|
35
39
|
[x: string]: never;
|
|
36
40
|
};
|
|
37
|
-
getDecodedBearer: (req: any) => any;
|
|
38
|
-
checkFleetPermission: (fleetId:
|
|
39
|
-
checkBusinessModelPermission: (businessModelId:
|
|
40
|
-
checkDemandSourcePermission: (demandSourceId:
|
|
41
|
+
getDecodedBearer: (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>) => any;
|
|
42
|
+
checkFleetPermission: (fleetId: string) => boolean;
|
|
43
|
+
checkBusinessModelPermission: (businessModelId: string) => boolean;
|
|
44
|
+
checkDemandSourcePermission: (demandSourceId: string) => boolean;
|
|
41
45
|
isUserExist: () => string;
|
|
42
46
|
getUser: () => User;
|
|
43
47
|
UnauthorizedAccessError: typeof UnauthorizedAccessError;
|
|
44
48
|
appMiddleware: (options: {
|
|
45
49
|
appId: string;
|
|
46
50
|
clientSecret: string;
|
|
47
|
-
}) => (
|
|
48
|
-
createOrSetRabbitTrace: (trace:
|
|
51
|
+
}) => import("express").Handler;
|
|
52
|
+
createOrSetRabbitTrace: (trace: import("./tracer").Trace, userId: string) => Promise<void>;
|
|
49
53
|
outbreak: typeof outbreak;
|
|
50
54
|
AUTHORIZATION_METHODS: {
|
|
51
55
|
NONE: string;
|
|
@@ -55,5 +59,6 @@ declare const _default: {
|
|
|
55
59
|
getAuthorizationHeader: (authorizationSettings: {
|
|
56
60
|
method: string;
|
|
57
61
|
}) => string;
|
|
62
|
+
CONTEXTS_IDS_HEADER: string;
|
|
58
63
|
};
|
|
59
64
|
export default _default;
|
package/lib/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.getAuthorizationHeader = exports.AUTHORIZATION_METHODS = exports.outbreak = exports.createOrSetRabbitTrace = exports.appMiddleware = exports.UnauthorizedAccessError = exports.getTokenSecret = exports.getRefreshTokenSecret = exports.getUser = exports.isUserExist = exports.checkDemandSourcePermission = exports.checkBusinessModelPermission = exports.checkFleetPermission = exports.getDecodedBearer = exports.getCurrentPayload = exports.eagerLoadPermissionsMiddleware = exports.middlewareWithDecode = exports.middleware = exports.User = exports.enableTracing = exports.newTrace = exports.traceTypes = void 0;
|
|
26
|
+
exports.CONTEXTS_IDS_HEADER = exports.getAuthorizationHeader = exports.AUTHORIZATION_METHODS = exports.outbreak = exports.createOrSetRabbitTrace = exports.appMiddleware = exports.UnauthorizedAccessError = exports.getTokenSecret = exports.getRefreshTokenSecret = exports.getUser = exports.isUserExist = exports.checkDemandSourcePermission = exports.checkBusinessModelPermission = exports.checkFleetPermission = exports.getDecodedBearer = exports.getCurrentPayload = exports.eagerLoadPermissionsMiddleware = exports.middlewareWithDecode = exports.middleware = exports.User = exports.enableTracing = exports.newTrace = exports.traceTypes = void 0;
|
|
27
27
|
const outbreak = __importStar(require("@autofleet/outbreak"));
|
|
28
28
|
exports.outbreak = outbreak;
|
|
29
29
|
const user_1 = __importStar(require("./user"));
|
|
@@ -34,6 +34,8 @@ Object.defineProperty(exports, "middlewareWithDecode", { enumerable: true, get:
|
|
|
34
34
|
Object.defineProperty(exports, "getDecodedBearer", { enumerable: true, get: function () { return user_1.getDecodedBearer; } });
|
|
35
35
|
Object.defineProperty(exports, "appMiddleware", { enumerable: true, get: function () { return user_1.appMiddleware; } });
|
|
36
36
|
Object.defineProperty(exports, "createOrSetRabbitTrace", { enumerable: true, get: function () { return user_1.createOrSetRabbitTrace; } });
|
|
37
|
+
const ApiUser_1 = require("./user/ApiUser");
|
|
38
|
+
Object.defineProperty(exports, "CONTEXTS_IDS_HEADER", { enumerable: true, get: function () { return ApiUser_1.CONTEXTS_IDS_HEADER; } });
|
|
37
39
|
const tracer_1 = require("./tracer");
|
|
38
40
|
Object.defineProperty(exports, "newTrace", { enumerable: true, get: function () { return tracer_1.newTrace; } });
|
|
39
41
|
Object.defineProperty(exports, "traceTypes", { enumerable: true, get: function () { return tracer_1.traceTypes; } });
|
|
@@ -51,11 +53,10 @@ Object.defineProperty(exports, "getTokenSecret", { enumerable: true, get: functi
|
|
|
51
53
|
const authorization_1 = require("./authorization");
|
|
52
54
|
Object.defineProperty(exports, "AUTHORIZATION_METHODS", { enumerable: true, get: function () { return authorization_1.AUTHORIZATION_METHODS; } });
|
|
53
55
|
Object.defineProperty(exports, "getAuthorizationHeader", { enumerable: true, get: function () { return authorization_1.getAuthorizationHeader; } });
|
|
54
|
-
const headersTracer = outbreak.default;
|
|
55
56
|
const getCurrentPayload = tracer_1.getCurrentTrace;
|
|
56
57
|
exports.getCurrentPayload = getCurrentPayload;
|
|
57
58
|
const enableTracing = ({ outbreakOptions = {} } = {}) => {
|
|
58
|
-
|
|
59
|
+
outbreak.default({
|
|
59
60
|
headersPrefix: 'x-af',
|
|
60
61
|
...outbreakOptions,
|
|
61
62
|
});
|
|
@@ -82,4 +83,5 @@ exports.default = {
|
|
|
82
83
|
outbreak,
|
|
83
84
|
AUTHORIZATION_METHODS: authorization_1.AUTHORIZATION_METHODS,
|
|
84
85
|
getAuthorizationHeader: authorization_1.getAuthorizationHeader,
|
|
86
|
+
CONTEXTS_IDS_HEADER: ApiUser_1.CONTEXTS_IDS_HEADER,
|
|
85
87
|
};
|
package/lib/secret-getter.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const getRefreshTokenSecret: (token?:
|
|
2
|
-
export declare const getTokenSecret: (token?:
|
|
1
|
+
export declare const getRefreshTokenSecret: (token?: string) => string;
|
|
2
|
+
export declare const getTokenSecret: (token?: string) => string;
|
package/lib/services.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
1
|
+
import Network from '@autofleet/network';
|
|
2
|
+
export declare const IdentityNetwork: Network;
|
|
3
|
+
export declare const AutofleetApiNetwork: Network;
|
package/lib/tracer.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getCurrentTrace = exports.traceTypes = exports.newTrace = exports.enable = exports.Trace = void 0;
|
|
7
7
|
const async_hooks_1 = __importDefault(require("async_hooks"));
|
|
8
|
-
const
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
9
|
const prevStates = {};
|
|
10
10
|
const tracer = {
|
|
11
11
|
currentTrace: null,
|
|
@@ -40,7 +40,7 @@ const hook = async_hooks_1.default.createHook({
|
|
|
40
40
|
});
|
|
41
41
|
class Trace {
|
|
42
42
|
constructor(type) {
|
|
43
|
-
this.id = (0,
|
|
43
|
+
this.id = (0, node_crypto_1.randomUUID)();
|
|
44
44
|
this.type = type;
|
|
45
45
|
this.context = new Map();
|
|
46
46
|
}
|
package/lib/user/ApiUser.d.ts
CHANGED
|
@@ -23,23 +23,21 @@ export interface PartialUserPayload {
|
|
|
23
23
|
}
|
|
24
24
|
export type CustomPermissionLoader = (string: any) => Promise<UserPayload>;
|
|
25
25
|
export default class ApiUser {
|
|
26
|
-
id
|
|
27
|
-
|
|
28
|
-
privateElevatedPermissionsHash: Map<string, PartialUserPayload | undefined>;
|
|
29
|
-
privatePermissionsLegacy: any;
|
|
30
|
-
appPermission: {
|
|
31
|
-
[key: string]: any;
|
|
32
|
-
};
|
|
33
|
-
emptyUser: boolean;
|
|
34
|
-
accountType: AccountType | undefined;
|
|
26
|
+
id?: string;
|
|
27
|
+
accountType?: AccountType;
|
|
35
28
|
contextIds?: string[];
|
|
29
|
+
private privatePermissions;
|
|
30
|
+
private readonly privateElevatedPermissionsHash;
|
|
31
|
+
private privatePermissionsLegacy;
|
|
32
|
+
private readonly appPermission;
|
|
33
|
+
readonly emptyUser: boolean;
|
|
36
34
|
constructor(id?: string, accountType?: AccountType, elevatedPermissions?: PartialUserPayload, contextIds?: string[]);
|
|
37
35
|
getUserPermissions(): Promise<UserPayload>;
|
|
38
|
-
useCustomPermissionLoader(customPermissionLoader:
|
|
36
|
+
useCustomPermissionLoader(customPermissionLoader: (userId: string) => UserPayload | PromiseLike<UserPayload>): Promise<UserPayload>;
|
|
39
37
|
get businessModels(): string[] | undefined;
|
|
40
38
|
get fleets(): string[] | undefined;
|
|
41
39
|
get demandSources(): string[] | undefined;
|
|
42
|
-
getUserProperty
|
|
40
|
+
private getUserProperty;
|
|
43
41
|
get elevatedPermissions(): UserPayload;
|
|
44
42
|
get permissions(): UserPayload | undefined;
|
|
45
43
|
elevatePermissions(addedPermissions: PartialUserPayload): () => void;
|
package/lib/user/ApiUser.js
CHANGED
|
@@ -4,33 +4,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CONTEXTS_IDS_HEADER = exports.ELEVATED_PERMISSIONS_HEADER = void 0;
|
|
7
|
-
/* eslint-disable consistent-return */
|
|
8
7
|
const node_cache_1 = __importDefault(require("node-cache"));
|
|
9
8
|
const object_hash_1 = __importDefault(require("object-hash"));
|
|
10
|
-
const uuid_1 = require("uuid");
|
|
11
9
|
const outbreak_1 = require("@autofleet/outbreak");
|
|
12
|
-
const services_1 = require("../services");
|
|
13
10
|
const utils_1 = require("../utils");
|
|
11
|
+
const services_1 = require("../services");
|
|
14
12
|
exports.ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';
|
|
15
13
|
exports.CONTEXTS_IDS_HEADER = 'x-af-context-ids';
|
|
16
14
|
const userCache = new node_cache_1.default({ stdTTL: 10 });
|
|
15
|
+
const mergePermissions = (target, sources) => {
|
|
16
|
+
const permissions = {
|
|
17
|
+
...target,
|
|
18
|
+
fleets: { ...target?.fleets },
|
|
19
|
+
businessModels: { ...target?.businessModels },
|
|
20
|
+
demandSources: { ...target?.demandSources },
|
|
21
|
+
// Clone other nested objects as needed
|
|
22
|
+
};
|
|
23
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
24
|
+
for (const source of sources) {
|
|
25
|
+
Object.keys(source).forEach((entityType) => {
|
|
26
|
+
// eslint-disable-next-line no-param-reassign
|
|
27
|
+
permissions[entityType] ?? (permissions[entityType] = {});
|
|
28
|
+
Object.entries(source[entityType]).forEach(([entityId, perms]) => {
|
|
29
|
+
// eslint-disable-next-line no-param-reassign
|
|
30
|
+
permissions[entityType][entityId] = (permissions[entityType][entityId] || []).concat(perms);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return permissions;
|
|
35
|
+
};
|
|
17
36
|
class ApiUser {
|
|
18
37
|
constructor(id, accountType, elevatedPermissions, contextIds) {
|
|
19
38
|
this.id = id;
|
|
20
|
-
this.
|
|
21
|
-
this.appPermission = {};
|
|
22
|
-
this.privateElevatedPermissionsHash = new Map();
|
|
39
|
+
this.accountType = accountType;
|
|
23
40
|
this.contextIds = contextIds;
|
|
41
|
+
this.privateElevatedPermissionsHash = new Map();
|
|
42
|
+
this.appPermission = {};
|
|
43
|
+
this.emptyUser = !!id;
|
|
24
44
|
if (elevatedPermissions) {
|
|
25
|
-
this.privateElevatedPermissionsHash.set('initial', elevatedPermissions);
|
|
26
|
-
}
|
|
27
|
-
if (accountType) {
|
|
28
|
-
this.accountType = accountType;
|
|
45
|
+
this.privateElevatedPermissionsHash.set(Symbol('initial'), elevatedPermissions);
|
|
29
46
|
}
|
|
30
47
|
}
|
|
31
48
|
async getUserPermissions() {
|
|
32
49
|
if (!this.id) {
|
|
33
|
-
return;
|
|
50
|
+
return undefined;
|
|
34
51
|
}
|
|
35
52
|
if (this.privatePermissions) {
|
|
36
53
|
return this.privatePermissions;
|
|
@@ -50,7 +67,7 @@ class ApiUser {
|
|
|
50
67
|
}
|
|
51
68
|
async useCustomPermissionLoader(customPermissionLoader) {
|
|
52
69
|
if (!this.id) {
|
|
53
|
-
return;
|
|
70
|
+
return undefined;
|
|
54
71
|
}
|
|
55
72
|
if (this.privatePermissions) {
|
|
56
73
|
return this.privatePermissions;
|
|
@@ -82,29 +99,22 @@ class ApiUser {
|
|
|
82
99
|
return Object.keys(this.privatePermissions[key] || {});
|
|
83
100
|
}
|
|
84
101
|
get elevatedPermissions() {
|
|
85
|
-
|
|
86
|
-
fleets: {},
|
|
87
|
-
businessModels: {},
|
|
88
|
-
demandSources: {},
|
|
89
|
-
};
|
|
90
|
-
[...this.privateElevatedPermissionsHash.values()].forEach((p) => {
|
|
91
|
-
permissions = (0, utils_1.mergeDeep)(permissions, p);
|
|
92
|
-
});
|
|
93
|
-
return permissions;
|
|
102
|
+
return mergePermissions(undefined, this.privateElevatedPermissionsHash.values());
|
|
94
103
|
}
|
|
95
104
|
get permissions() {
|
|
96
105
|
if (!this.privatePermissions) {
|
|
97
106
|
throw new Error('Cannot get permissions without calling (async) getUserPermissions before');
|
|
98
107
|
}
|
|
99
|
-
|
|
100
|
-
return permissions;
|
|
108
|
+
return mergePermissions(this.privatePermissions, this.privateElevatedPermissionsHash.values());
|
|
101
109
|
}
|
|
102
110
|
elevatePermissions(addedPermissions) {
|
|
103
|
-
|
|
111
|
+
// @itayankri is concerned about memory consumption, so create a symbol with no description, to avoid assigning memory for the description string
|
|
112
|
+
// eslint-disable-next-line symbol-description
|
|
113
|
+
const elevationId = Symbol();
|
|
104
114
|
// Validate that the added permissions are valid UUIDs
|
|
105
|
-
Object.
|
|
106
|
-
Object.keys(
|
|
107
|
-
if (!(0,
|
|
115
|
+
Object.values(addedPermissions).forEach((entityIds) => {
|
|
116
|
+
Object.keys(entityIds).forEach((entityId) => {
|
|
117
|
+
if (!(0, utils_1.validateUUID)(entityId)) {
|
|
108
118
|
throw new Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${entityId}`);
|
|
109
119
|
}
|
|
110
120
|
});
|
|
@@ -124,7 +134,7 @@ class ApiUser {
|
|
|
124
134
|
}
|
|
125
135
|
async getUserPermissionsLegacy() {
|
|
126
136
|
if (!this.id) {
|
|
127
|
-
return;
|
|
137
|
+
return undefined;
|
|
128
138
|
}
|
|
129
139
|
if (this.privatePermissionsLegacy) {
|
|
130
140
|
return this.privatePermissionsLegacy;
|
|
@@ -141,7 +151,7 @@ class ApiUser {
|
|
|
141
151
|
}
|
|
142
152
|
async getUserAppPermissions(appId, clientSecret) {
|
|
143
153
|
if (!this.id || !appId || !clientSecret) {
|
|
144
|
-
return;
|
|
154
|
+
return undefined;
|
|
145
155
|
}
|
|
146
156
|
const currentAppPermission = this.appPermission[appId];
|
|
147
157
|
if (currentAppPermission) {
|
package/lib/user/index.d.ts
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
|
+
import type { Handler, Request } from 'express';
|
|
1
2
|
import ApiUser, { CustomPermissionLoader } from './ApiUser';
|
|
3
|
+
import { newTrace } from '../tracer';
|
|
4
|
+
export declare const USER_OBJECT = "userObject";
|
|
5
|
+
declare module 'express-serve-static-core' {
|
|
6
|
+
interface Request {
|
|
7
|
+
user: ApiUser;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
2
10
|
export declare const middleware: (options?: {
|
|
3
11
|
eagerLoadUserPermissions?: boolean;
|
|
4
12
|
eagerLoadUserPermissionsLegacy?: boolean;
|
|
5
13
|
customPermissionLoader?: CustomPermissionLoader;
|
|
6
|
-
}) =>
|
|
14
|
+
}) => Handler;
|
|
7
15
|
export declare const middlewareWithDecode: (options?: {
|
|
8
16
|
eagerLoadUserPermissions?: boolean;
|
|
9
17
|
eagerLoadUserPermissionsLegacy?: boolean;
|
|
10
18
|
returnErrorIfNoToken?: boolean;
|
|
11
|
-
}) =>
|
|
19
|
+
}) => Handler;
|
|
12
20
|
export declare const appMiddleware: (options: {
|
|
13
21
|
appId: string;
|
|
14
22
|
clientSecret: string;
|
|
15
|
-
}) =>
|
|
16
|
-
export declare const eagerLoadPermissionsMiddleware:
|
|
17
|
-
export declare const getDecodedBearer: (req:
|
|
18
|
-
export declare const createOrSetRabbitTrace: (trace:
|
|
23
|
+
}) => Handler;
|
|
24
|
+
export declare const eagerLoadPermissionsMiddleware: Handler;
|
|
25
|
+
export declare const getDecodedBearer: (req: Request) => any;
|
|
26
|
+
export declare const createOrSetRabbitTrace: (trace: ReturnType<typeof newTrace> | undefined, userId: string | undefined) => Promise<void>;
|
|
19
27
|
export default ApiUser;
|
package/lib/user/index.js
CHANGED
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.createOrSetRabbitTrace = exports.getDecodedBearer = exports.eagerLoadPermissionsMiddleware = exports.appMiddleware = exports.middlewareWithDecode = exports.middleware = void 0;
|
|
29
|
+
exports.createOrSetRabbitTrace = exports.getDecodedBearer = exports.eagerLoadPermissionsMiddleware = exports.appMiddleware = exports.middlewareWithDecode = exports.middleware = exports.USER_OBJECT = void 0;
|
|
30
30
|
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
31
31
|
const ApiUser_1 = __importStar(require("./ApiUser"));
|
|
32
32
|
const tracer_1 = require("../tracer");
|
|
@@ -34,22 +34,31 @@ const app_auth_1 = require("../app-auth");
|
|
|
34
34
|
const appDoesNotExist_1 = __importDefault(require("../exceptions/appDoesNotExist"));
|
|
35
35
|
const utils_1 = require("../utils");
|
|
36
36
|
const IDENTITY_MS = 'identity-ms';
|
|
37
|
+
const ACCESS_TOKEN = 'accessToken';
|
|
38
|
+
exports.USER_OBJECT = 'userObject';
|
|
39
|
+
const USER_TRACING_HEADER = 'x-af-user-id';
|
|
40
|
+
const ORIGIN_HEADER = 'X-IAF-ORIGIN-SERVICE';
|
|
41
|
+
const USER_PERMISSIONS_HEADER = 'x-af-user-permissions';
|
|
42
|
+
const LOWER_CASE_ORIGIN_HEADER = ORIGIN_HEADER.toLowerCase();
|
|
43
|
+
const AUTOFLEET_APPS_SECRET_HEADER = 'x-autofleet-apps-secret';
|
|
37
44
|
const middleware = (options = {}) => async (req, res, next) => {
|
|
38
45
|
try {
|
|
39
|
-
const originHeader = req.headers[
|
|
46
|
+
const originHeader = (req.headers[ORIGIN_HEADER] || req.headers[LOWER_CASE_ORIGIN_HEADER] || '');
|
|
40
47
|
if (originHeader.toLowerCase() === IDENTITY_MS) {
|
|
41
|
-
|
|
48
|
+
next();
|
|
49
|
+
return;
|
|
42
50
|
}
|
|
43
51
|
const { eagerLoadUserPermissions, eagerLoadUserPermissionsLegacy, customPermissionLoader, } = options;
|
|
44
|
-
const userId = req.headers[
|
|
45
|
-
const contextIds = req.headers?.[ApiUser_1.CONTEXTS_IDS_HEADER]?.split(',');
|
|
52
|
+
const userId = req.headers[USER_TRACING_HEADER];
|
|
46
53
|
const trace = (0, tracer_1.newTrace)('userPayload');
|
|
47
54
|
if (!userId) {
|
|
48
|
-
|
|
55
|
+
next();
|
|
56
|
+
return;
|
|
49
57
|
}
|
|
50
58
|
const elevatedPermissionsFromHeader = req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER] && req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER].length > 0
|
|
51
59
|
? JSON.parse(req.headers[ApiUser_1.ELEVATED_PERMISSIONS_HEADER])
|
|
52
60
|
: {};
|
|
61
|
+
const contextIds = req.headers?.[ApiUser_1.CONTEXTS_IDS_HEADER]?.split(',');
|
|
53
62
|
const userObject = new ApiUser_1.default(userId, 'user', elevatedPermissionsFromHeader, contextIds);
|
|
54
63
|
if (eagerLoadUserPermissions) {
|
|
55
64
|
if (customPermissionLoader) {
|
|
@@ -63,16 +72,14 @@ const middleware = (options = {}) => async (req, res, next) => {
|
|
|
63
72
|
await userObject.getUserPermissionsLegacy();
|
|
64
73
|
}
|
|
65
74
|
req.user = userObject;
|
|
66
|
-
trace.context.set(
|
|
75
|
+
trace.context.set(exports.USER_OBJECT, userObject);
|
|
67
76
|
// Added in order to support outbreak.
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
// @ts-expect-error we are setting an object onto the request headers.
|
|
78
|
+
req.headers[USER_PERMISSIONS_HEADER] = userObject;
|
|
79
|
+
next();
|
|
70
80
|
}
|
|
71
81
|
catch (e) {
|
|
72
|
-
res.status(401);
|
|
73
|
-
return res.json({
|
|
74
|
-
error: 'cannot authenticate user',
|
|
75
|
-
});
|
|
82
|
+
res.status(401).json({ error: 'cannot authenticate user' });
|
|
76
83
|
}
|
|
77
84
|
};
|
|
78
85
|
exports.middleware = middleware;
|
|
@@ -86,51 +93,39 @@ const middlewareWithDecode = (options = {}) => async (req, res, next) => {
|
|
|
86
93
|
}
|
|
87
94
|
catch (e) {
|
|
88
95
|
if (e instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
89
|
-
res.status(401);
|
|
90
|
-
res.json({
|
|
91
|
-
errors: ['Access token expired'],
|
|
92
|
-
});
|
|
96
|
+
res.status(401).json({ errors: ['Access token expired'] });
|
|
93
97
|
}
|
|
94
98
|
else if (e instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
95
|
-
res.status(400);
|
|
96
|
-
res.json({
|
|
97
|
-
errors: [e.message],
|
|
98
|
-
});
|
|
99
|
+
res.status(400).json({ errors: [e.message] });
|
|
99
100
|
}
|
|
100
101
|
else {
|
|
101
|
-
res.status(500);
|
|
102
|
-
res.json({
|
|
103
|
-
errors: ['Server error while parsing token'],
|
|
104
|
-
});
|
|
102
|
+
res.status(500).json({ errors: ['Server error while parsing token'] });
|
|
105
103
|
}
|
|
106
104
|
return;
|
|
107
105
|
}
|
|
108
106
|
const userId = decoded?.user?.id;
|
|
109
107
|
if (userId) {
|
|
110
|
-
req.headers[
|
|
108
|
+
req.headers[USER_TRACING_HEADER] = userId;
|
|
111
109
|
}
|
|
112
110
|
const contextIds = req.headers?.[ApiUser_1.CONTEXTS_IDS_HEADER]?.split(',');
|
|
113
111
|
const userObject = new ApiUser_1.default(userId, decoded?.user?.accountType, undefined, contextIds);
|
|
114
|
-
if (eagerLoadUserPermissions) {
|
|
115
|
-
await
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
if (eagerLoadUserPermissions || eagerLoadUserPermissionsLegacy) {
|
|
113
|
+
await Promise.all([
|
|
114
|
+
eagerLoadUserPermissions && userObject.getUserPermissions(),
|
|
115
|
+
eagerLoadUserPermissionsLegacy && userObject.getUserPermissionsLegacy(),
|
|
116
|
+
]);
|
|
119
117
|
}
|
|
120
118
|
req.user = userObject;
|
|
121
|
-
trace.context.set(
|
|
119
|
+
trace.context.set(exports.USER_OBJECT, userObject);
|
|
122
120
|
// Added in order to support outbreak.
|
|
123
|
-
|
|
121
|
+
// @ts-expect-error we are setting an object onto the request headers.
|
|
122
|
+
req.headers[USER_PERMISSIONS_HEADER] = userObject;
|
|
124
123
|
}
|
|
125
124
|
else if (returnErrorIfNoToken) {
|
|
126
|
-
res.status(401);
|
|
127
|
-
|
|
128
|
-
return res.json({
|
|
129
|
-
errors: ['No token provided'],
|
|
130
|
-
});
|
|
125
|
+
res.status(401).json({ errors: ['No token provided'] });
|
|
126
|
+
return;
|
|
131
127
|
}
|
|
132
|
-
|
|
133
|
-
return next();
|
|
128
|
+
next();
|
|
134
129
|
};
|
|
135
130
|
exports.middlewareWithDecode = middlewareWithDecode;
|
|
136
131
|
const appMiddleware = (options) => async (req, res, next) => {
|
|
@@ -138,10 +133,8 @@ const appMiddleware = (options) => async (req, res, next) => {
|
|
|
138
133
|
const trace = (0, tracer_1.newTrace)('userPayload');
|
|
139
134
|
let decoded;
|
|
140
135
|
if (!req.headers.authorization) {
|
|
141
|
-
res.status(401);
|
|
142
|
-
return
|
|
143
|
-
errors: ['No token provided'],
|
|
144
|
-
});
|
|
136
|
+
res.status(401).json({ errors: ['No token provided'] });
|
|
137
|
+
return;
|
|
145
138
|
}
|
|
146
139
|
try {
|
|
147
140
|
decoded = await (0, app_auth_1.decodeAppBearer)(req.headers.authorization, appId);
|
|
@@ -151,66 +144,53 @@ const appMiddleware = (options) => async (req, res, next) => {
|
|
|
151
144
|
}
|
|
152
145
|
catch (e) {
|
|
153
146
|
if (e instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
154
|
-
res.status(401);
|
|
155
|
-
return
|
|
156
|
-
errors: ['Access token expired'],
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
if (e instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
160
|
-
res.status(400);
|
|
161
|
-
return res.json({
|
|
162
|
-
errors: [e.message],
|
|
163
|
-
});
|
|
147
|
+
res.status(401).json({ errors: ['Access token expired'] });
|
|
148
|
+
return;
|
|
164
149
|
}
|
|
165
|
-
if (
|
|
166
|
-
res.status(400);
|
|
167
|
-
return
|
|
168
|
-
errors: [e.message],
|
|
169
|
-
});
|
|
150
|
+
if ([jsonwebtoken_1.JsonWebTokenError, appDoesNotExist_1.default].some((Err) => e instanceof Err)) {
|
|
151
|
+
res.status(400).json({ errors: [e.message] });
|
|
152
|
+
return;
|
|
170
153
|
}
|
|
171
|
-
res.status(500);
|
|
172
|
-
return
|
|
173
|
-
errors: ['Server error while parsing token'],
|
|
174
|
-
});
|
|
154
|
+
res.status(500).json({ errors: ['Server error while parsing token'] });
|
|
155
|
+
return;
|
|
175
156
|
}
|
|
176
157
|
const userId = decoded?.userId;
|
|
177
158
|
if (userId) {
|
|
178
|
-
req.headers[
|
|
159
|
+
req.headers[USER_TRACING_HEADER] = userId;
|
|
179
160
|
}
|
|
180
161
|
const userObject = new ApiUser_1.default(userId);
|
|
181
162
|
if (appId) {
|
|
182
|
-
req.headers[
|
|
163
|
+
req.headers[AUTOFLEET_APPS_SECRET_HEADER] = clientSecret;
|
|
183
164
|
// Won't work until we find a better solution for identity ms
|
|
184
165
|
await userObject.getUserAppPermissions(appId, clientSecret);
|
|
185
166
|
}
|
|
186
167
|
req.user = userObject;
|
|
187
|
-
trace.context.set(
|
|
188
|
-
trace.context.set(
|
|
168
|
+
trace.context.set(exports.USER_OBJECT, userObject);
|
|
169
|
+
trace.context.set(ACCESS_TOKEN, (0, utils_1.getAuthFromBearer)(req.headers.authorization));
|
|
189
170
|
// Added in order to support outbreak.
|
|
190
|
-
|
|
191
|
-
|
|
171
|
+
// @ts-expect-error we are setting an object onto the request headers.
|
|
172
|
+
req.headers[USER_PERMISSIONS_HEADER] = userObject;
|
|
173
|
+
next();
|
|
192
174
|
};
|
|
193
175
|
exports.appMiddleware = appMiddleware;
|
|
194
176
|
const eagerLoadPermissionsMiddleware = async (req, res, next) => {
|
|
195
177
|
await req.user.getUserPermissions();
|
|
196
|
-
|
|
178
|
+
next();
|
|
197
179
|
};
|
|
198
180
|
exports.eagerLoadPermissionsMiddleware = eagerLoadPermissionsMiddleware;
|
|
199
181
|
const getDecodedBearer = (req) => {
|
|
200
|
-
if (req.headers.authorization) {
|
|
201
|
-
return
|
|
182
|
+
if (!req.headers.authorization) {
|
|
183
|
+
return null;
|
|
202
184
|
}
|
|
203
|
-
return
|
|
185
|
+
return (0, utils_1.decodeBearer)(req.headers.authorization);
|
|
204
186
|
};
|
|
205
187
|
exports.getDecodedBearer = getDecodedBearer;
|
|
206
188
|
const createOrSetRabbitTrace = async (trace, userId) => {
|
|
207
189
|
const userObject = new ApiUser_1.default(userId);
|
|
208
190
|
await userObject.getUserPermissions();
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
trace.context.set('userObject', userObject);
|
|
191
|
+
// eslint-disable-next-line no-param-reassign
|
|
192
|
+
trace ?? (trace = (0, tracer_1.newTrace)(tracer_1.traceTypes.RABBIT));
|
|
193
|
+
trace.context.set(exports.USER_OBJECT, userObject);
|
|
214
194
|
};
|
|
215
195
|
exports.createOrSetRabbitTrace = createOrSetRabbitTrace;
|
|
216
196
|
exports.default = ApiUser_1.default;
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { UUID } from 'node:crypto';
|
|
1
3
|
export declare const getAuthFromBearer: (bearer: string) => string;
|
|
2
4
|
export declare const decodeBearer: (bearer: string, appSecret?: string) => any;
|
|
3
5
|
export declare const parsePermissions: (contextId: any, decodedToken: any) => any;
|
|
4
6
|
export declare const getEntitiesFromContext: (contextId: string, decodedToken: any) => any;
|
|
5
7
|
export declare const getContextAttributes: (contextId: string, decodedToken: any) => any;
|
|
6
|
-
|
|
7
|
-
* Custom deep merge function that merges `source` into `target`.
|
|
8
|
-
*
|
|
9
|
-
* @param target - The object to receive merged properties.
|
|
10
|
-
* @param source - The object whose properties are merged into the target.
|
|
11
|
-
* @returns The modified `target` object.
|
|
12
|
-
*/
|
|
13
|
-
export declare const mergeDeep: <T extends object>(target: T, source: Partial<T>) => T;
|
|
8
|
+
export declare function validateUUID(uuid: unknown): uuid is UUID;
|
package/lib/utils.js
CHANGED
|
@@ -23,8 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.
|
|
27
|
-
/* eslint-disable prefer-destructuring */
|
|
26
|
+
exports.validateUUID = exports.getContextAttributes = exports.getEntitiesFromContext = exports.parsePermissions = exports.decodeBearer = exports.getAuthFromBearer = void 0;
|
|
28
27
|
const jwt = __importStar(require("jsonwebtoken"));
|
|
29
28
|
const secret_getter_1 = require("./secret-getter");
|
|
30
29
|
const CONTEXT_PROPS = ['fleetId', 'businessModelId', 'demandSourceId'];
|
|
@@ -45,7 +44,7 @@ const parsePermissions = (contextId, decodedToken) => {
|
|
|
45
44
|
if (!decodedToken) {
|
|
46
45
|
return [];
|
|
47
46
|
}
|
|
48
|
-
const contexts = decodedToken
|
|
47
|
+
const { contexts } = decodedToken;
|
|
49
48
|
const activeContext = contexts.find((context) => context.id === contextId);
|
|
50
49
|
const permissionsValue = `${activeContext.permissions?.map((cp) => `${cp},`)}`;
|
|
51
50
|
return {
|
|
@@ -58,7 +57,7 @@ const getEntitiesFromContext = (contextId, decodedToken) => {
|
|
|
58
57
|
if (!decodedToken) {
|
|
59
58
|
return [];
|
|
60
59
|
}
|
|
61
|
-
let contexts = decodedToken
|
|
60
|
+
let { contexts } = decodedToken;
|
|
62
61
|
if (contextId) {
|
|
63
62
|
contexts = contexts.filter((context) => context.id === contextId);
|
|
64
63
|
}
|
|
@@ -66,10 +65,8 @@ const getEntitiesFromContext = (contextId, decodedToken) => {
|
|
|
66
65
|
contexts.forEach((context) => {
|
|
67
66
|
const prop = CONTEXT_MAP_PROPS[context.subSystem || 'business'];
|
|
68
67
|
const permissions = (0, exports.parsePermissions)(context.id, decodedToken);
|
|
69
|
-
|
|
70
|
-
attributes[prop]
|
|
71
|
-
? attributes[prop][permissions.key] = permissions.value
|
|
72
|
-
: attributes[prop] = { [permissions.key]: permissions.value };
|
|
68
|
+
attributes[prop] || (attributes[prop] = {});
|
|
69
|
+
attributes[prop][permissions.key] = permissions.value;
|
|
73
70
|
});
|
|
74
71
|
return attributes;
|
|
75
72
|
};
|
|
@@ -78,7 +75,7 @@ const getContextAttributes = (contextId, decodedToken) => {
|
|
|
78
75
|
if (!decodedToken) {
|
|
79
76
|
return [];
|
|
80
77
|
}
|
|
81
|
-
let contexts = decodedToken
|
|
78
|
+
let { contexts } = decodedToken;
|
|
82
79
|
if (contextId) {
|
|
83
80
|
contexts = contexts.filter((context) => context.id === contextId);
|
|
84
81
|
}
|
|
@@ -87,44 +84,20 @@ const getContextAttributes = (contextId, decodedToken) => {
|
|
|
87
84
|
CONTEXT_PROPS.forEach((prop) => {
|
|
88
85
|
if (context[prop]) {
|
|
89
86
|
const contextPropWrapped = [context[prop]];
|
|
90
|
-
|
|
91
|
-
attributes[prop]
|
|
92
|
-
? attributes[prop] = attributes[prop].concat(contextPropWrapped)
|
|
93
|
-
: attributes[prop] = contextPropWrapped;
|
|
87
|
+
attributes[prop] || (attributes[prop] = []);
|
|
88
|
+
attributes[prop] = attributes[prop].concat(contextPropWrapped);
|
|
94
89
|
}
|
|
95
90
|
});
|
|
96
91
|
});
|
|
97
92
|
return attributes;
|
|
98
93
|
};
|
|
99
94
|
exports.getContextAttributes = getContextAttributes;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
* @param target - The object to receive merged properties.
|
|
110
|
-
* @param source - The object whose properties are merged into the target.
|
|
111
|
-
* @returns The modified `target` object.
|
|
112
|
-
*/
|
|
113
|
-
const mergeDeep = (target, source) => {
|
|
114
|
-
if (!isObject(target) || !isObject(source)) {
|
|
115
|
-
throw new Error('Both arguments must be objects to merge');
|
|
116
|
-
}
|
|
117
|
-
Object.keys(source).forEach((key) => {
|
|
118
|
-
const sourceValue = source[key];
|
|
119
|
-
const targetValue = target[key];
|
|
120
|
-
if (isObject(sourceValue) && isObject(targetValue)) {
|
|
121
|
-
(0, exports.mergeDeep)(targetValue, sourceValue);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
// eslint-disable-next-line no-param-reassign
|
|
125
|
-
target[key] = sourceValue;
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return target;
|
|
129
|
-
};
|
|
130
|
-
exports.mergeDeep = mergeDeep;
|
|
95
|
+
const EMPTY_UUID = '00000000-0000-0000-0000-000000000000';
|
|
96
|
+
const FULL_UUID = 'ffffffff-ffff-ffff-ffff-ffffffffffff';
|
|
97
|
+
const VALID_CHARS_REGEX = '[0-9a-f]';
|
|
98
|
+
const UUID_VERSION_REGEX = '[1-8]';
|
|
99
|
+
const UUID_REGEX = new RegExp(`^(?:${VALID_CHARS_REGEX}{8}-${VALID_CHARS_REGEX}{4}-${UUID_VERSION_REGEX}${VALID_CHARS_REGEX}{3}-[89ab]${VALID_CHARS_REGEX}{3}-${VALID_CHARS_REGEX}{12}|${EMPTY_UUID}|${FULL_UUID})$`, 'i');
|
|
100
|
+
function validateUUID(uuid) {
|
|
101
|
+
return typeof uuid === 'string' && UUID_REGEX.test(uuid);
|
|
102
|
+
}
|
|
103
|
+
exports.validateUUID = validateUUID;
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/zehut",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "manage user's identity",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "rm -rf lib && tsc",
|
|
8
|
+
"build": "rm -rf lib && tsc -p tsconfig.build.json",
|
|
9
9
|
"prepublish": "npm run build",
|
|
10
|
-
"coverage": "
|
|
11
|
-
"test": "
|
|
12
|
-
"test-auto": "
|
|
13
|
-
"linter": "
|
|
10
|
+
"coverage": "vitest --coverage",
|
|
11
|
+
"test": "vitest",
|
|
12
|
+
"test-auto": "vitest --watch",
|
|
13
|
+
"linter": "eslint ."
|
|
14
14
|
},
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "
|
|
17
|
+
"url": "https://github.com/Autofleet/zehut.git"
|
|
18
18
|
},
|
|
19
19
|
"author": "",
|
|
20
20
|
"license": "ISC",
|
|
@@ -23,30 +23,38 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/Autofleet/zehut",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@autofleet/network": "^1.
|
|
26
|
+
"@autofleet/network": "^1.7.4",
|
|
27
27
|
"@autofleet/outbreak": "^1.0.4",
|
|
28
|
-
"axios": "^0.27.2",
|
|
29
|
-
"express": "^4.18.1",
|
|
30
28
|
"jsonwebtoken": "^8.5.1",
|
|
31
|
-
"
|
|
32
|
-
"moment": "^2.29.1",
|
|
33
|
-
"nock": "^13.2.9",
|
|
29
|
+
"moment": "^2.30.1",
|
|
34
30
|
"node-cache": "^5.1.2",
|
|
35
|
-
"object-hash": "^3.0.0"
|
|
36
|
-
"supertest": "^6.2.4",
|
|
37
|
-
"uuid": "^8.3.2"
|
|
31
|
+
"object-hash": "^3.0.0"
|
|
38
32
|
},
|
|
39
33
|
"devDependencies": {
|
|
40
|
-
"@
|
|
41
|
-
"@types/
|
|
34
|
+
"@autofleet/logger": "^4.2.0",
|
|
35
|
+
"@types/express": "^4.17.21",
|
|
36
|
+
"@types/jsonwebtoken": "^8.5.9",
|
|
37
|
+
"@types/node": "^18.19.75",
|
|
42
38
|
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
|
43
39
|
"@typescript-eslint/parser": "^6.5.0",
|
|
40
|
+
"@vitest/coverage-v8": "^3.0.6",
|
|
41
|
+
"axios": "^0.29.0",
|
|
44
42
|
"eslint": "^8.48.0",
|
|
45
43
|
"eslint-config-airbnb-typescript": "^17.1.0",
|
|
46
44
|
"eslint-plugin-import": "^2.28.1",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
45
|
+
"express": "^4.21.2",
|
|
46
|
+
"nock": "^14.0.0",
|
|
47
|
+
"supertest": "^7.0.0",
|
|
48
|
+
"typescript": "^4.9.5",
|
|
49
|
+
"vitest": "^3.0.6"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@autofleet/shtinker": "^1.2.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@autofleet/shtinker": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
50
58
|
},
|
|
51
59
|
"files": [
|
|
52
60
|
"lib/**/*"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
30
|
-
const moment_1 = __importDefault(require("moment"));
|
|
31
|
-
const sleep = async (ms) => new Promise((resolve) => {
|
|
32
|
-
setTimeout(resolve, ms);
|
|
33
|
-
});
|
|
34
|
-
process.env.NODE_ENV = 'node-common-test';
|
|
35
|
-
describe('secret getter tests', () => {
|
|
36
|
-
const testSecrets = {
|
|
37
|
-
DEPRECATED_JWT_SECRET: 'test_deprecated_secret',
|
|
38
|
-
JWT_NEW_SECRET: 'test_secret',
|
|
39
|
-
DEPRECATED_REFRESH_JWT_SECRET: 'test_deprecated_refresh_secret',
|
|
40
|
-
REFRESH_JWT_SECRET: 'test_refresh_secret',
|
|
41
|
-
};
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
jest.resetModules();
|
|
44
|
-
process.env = {
|
|
45
|
-
...(process.env || {}),
|
|
46
|
-
...testSecrets,
|
|
47
|
-
}; // Make a copy
|
|
48
|
-
});
|
|
49
|
-
it('test get new refresh token secret when after deprecation timestamp', async () => {
|
|
50
|
-
process.env.DEPRECATION_UNIX_TIMESTAMP = (0, moment_1.default)().subtract(1, 'days').unix().toString();
|
|
51
|
-
const { getTokenSecret } = await Promise.resolve().then(() => __importStar(require('./secret-getter')));
|
|
52
|
-
const token = jsonwebtoken_1.default.sign({
|
|
53
|
-
exp: (0, moment_1.default)().unix() + (60 * 60 * 24 * 365),
|
|
54
|
-
}, testSecrets.JWT_NEW_SECRET);
|
|
55
|
-
const result = getTokenSecret(token);
|
|
56
|
-
expect(result).toBe(testSecrets.JWT_NEW_SECRET);
|
|
57
|
-
});
|
|
58
|
-
it('test get old refresh token secret when before deprecation timestamp', async () => {
|
|
59
|
-
process.env.DEPRECATION_UNIX_TIMESTAMP = (0, moment_1.default)().add(1, 'days').unix().toString();
|
|
60
|
-
const { getTokenSecret } = await Promise.resolve().then(() => __importStar(require('./secret-getter')));
|
|
61
|
-
const token = jsonwebtoken_1.default.sign({
|
|
62
|
-
exp: (0, moment_1.default)().unix() + (60 * 60 * 24 * 365),
|
|
63
|
-
}, testSecrets.DEPRECATED_JWT_SECRET);
|
|
64
|
-
const result = getTokenSecret(token);
|
|
65
|
-
expect(result).toBe(testSecrets.DEPRECATED_JWT_SECRET);
|
|
66
|
-
});
|
|
67
|
-
it('test get old refresh token secret when after deprecation timestamp, but token is from before', async () => {
|
|
68
|
-
const token = jsonwebtoken_1.default.sign({
|
|
69
|
-
exp: (0, moment_1.default)().unix() + (60 * 60 * 24 * 365),
|
|
70
|
-
}, testSecrets.DEPRECATED_JWT_SECRET);
|
|
71
|
-
process.env.DEPRECATION_UNIX_TIMESTAMP = (0, moment_1.default)().add(10, 'seconds').unix().toString();
|
|
72
|
-
const { getTokenSecret } = await Promise.resolve().then(() => __importStar(require('./secret-getter')));
|
|
73
|
-
await sleep(1000);
|
|
74
|
-
const result = getTokenSecret(token);
|
|
75
|
-
expect(result).toBe(testSecrets.DEPRECATED_JWT_SECRET);
|
|
76
|
-
});
|
|
77
|
-
it('test malformed token returns new secret', async () => {
|
|
78
|
-
const { getTokenSecret } = await Promise.resolve().then(() => __importStar(require('./secret-getter')));
|
|
79
|
-
const token = 'shit';
|
|
80
|
-
const result = getTokenSecret(token);
|
|
81
|
-
expect(result).toBe(testSecrets.JWT_NEW_SECRET);
|
|
82
|
-
});
|
|
83
|
-
it('test malformed token returns new secret even before deprecation time', async () => {
|
|
84
|
-
process.env.DEPRECATION_UNIX_TIMESTAMP = (0, moment_1.default)().add(10, 'day').unix().toString();
|
|
85
|
-
const { getTokenSecret } = await Promise.resolve().then(() => __importStar(require('./secret-getter')));
|
|
86
|
-
const token = 'shit';
|
|
87
|
-
const result = getTokenSecret(token);
|
|
88
|
-
expect(result).toBe(testSecrets.JWT_NEW_SECRET);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { AccountType, PartialUserPayload } from '../user/ApiUser';
|
|
2
|
-
export type UserCustomAttributes = {
|
|
3
|
-
id?: string;
|
|
4
|
-
accountType?: AccountType;
|
|
5
|
-
elevatedPermissions?: PartialUserPayload;
|
|
6
|
-
permissions?: PartialUserPayload;
|
|
7
|
-
};
|
|
8
|
-
export declare const setIdentityNock: (userAttributes: any) => void;
|
|
9
|
-
export declare const mockRequestAndUser: (app: any, userCustomAttributes: any) => {};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mockRequestAndUser = exports.setIdentityNock = void 0;
|
|
7
|
-
const supertest_1 = __importDefault(require("supertest"));
|
|
8
|
-
const nock_1 = __importDefault(require("nock"));
|
|
9
|
-
const http_1 = __importDefault(require("http"));
|
|
10
|
-
const uuid_1 = require("uuid");
|
|
11
|
-
const setIdentityNock = (userAttributes) => {
|
|
12
|
-
(0, nock_1.default)(`http://${process.env.IDENTITY_MS_SERVICE_HOST}`)
|
|
13
|
-
.get(`/api/v1/users/${userAttributes.id}/authorization-payload`)
|
|
14
|
-
.reply(200, userAttributes.permissions);
|
|
15
|
-
};
|
|
16
|
-
exports.setIdentityNock = setIdentityNock;
|
|
17
|
-
const mockRequestAndUser = (app, userCustomAttributes) => {
|
|
18
|
-
const agent = supertest_1.default.agent(app);
|
|
19
|
-
const userAttributes = {
|
|
20
|
-
id: userCustomAttributes.id || (0, uuid_1.v4)(),
|
|
21
|
-
permissions: {
|
|
22
|
-
businessModels: userCustomAttributes?.permissions?.businessModels || {},
|
|
23
|
-
fleets: userCustomAttributes?.permissions?.fleets || {},
|
|
24
|
-
demandSources: userCustomAttributes?.permissions?.demandSources || {},
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
agent.set('x-af-user-id', userAttributes.id);
|
|
28
|
-
(0, exports.setIdentityNock)(userAttributes);
|
|
29
|
-
const agentObject = {};
|
|
30
|
-
http_1.default.METHODS.forEach((method) => {
|
|
31
|
-
agentObject[method.toLowerCase()] = (path) => agent[method.toLowerCase()](path).set('x-af-user-id', userAttributes.id);
|
|
32
|
-
});
|
|
33
|
-
return agentObject;
|
|
34
|
-
};
|
|
35
|
-
exports.mockRequestAndUser = mockRequestAndUser;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const uuid_1 = require("uuid");
|
|
7
|
-
const express_1 = __importDefault(require("express"));
|
|
8
|
-
const axios_1 = __importDefault(require("axios"));
|
|
9
|
-
const index_1 = require("../index");
|
|
10
|
-
const index_2 = require("./index");
|
|
11
|
-
const generateApp = async (addEndpoints, port) => {
|
|
12
|
-
const app = (0, express_1.default)();
|
|
13
|
-
addEndpoints(app);
|
|
14
|
-
const server = await new Promise((resolve) => {
|
|
15
|
-
const s = app.listen(port, () => {
|
|
16
|
-
console.log('Listen on port', port);
|
|
17
|
-
resolve(s);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
return () => server.close();
|
|
21
|
-
};
|
|
22
|
-
describe('E2E', () => {
|
|
23
|
-
it('Basic functionality', async () => {
|
|
24
|
-
let server2TraceId = null;
|
|
25
|
-
const [uuid1, uuid2] = [(0, uuid_1.v4)(), (0, uuid_1.v4)()];
|
|
26
|
-
(0, index_1.enableTracing)({
|
|
27
|
-
outbreakOptions: {
|
|
28
|
-
headersPrefix: 'x-af-',
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
const closeServer1 = await generateApp((app) => {
|
|
32
|
-
app.use((0, index_2.middleware)());
|
|
33
|
-
app.get('/', async (req, res) => {
|
|
34
|
-
const user = (0, index_1.getUser)();
|
|
35
|
-
const closeElevation1 = user.elevatePermissions({
|
|
36
|
-
businessModels: {
|
|
37
|
-
[uuid1]: ['vehicle:write'],
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
const closeElevation2 = user.elevatePermissions({
|
|
41
|
-
businessModels: {
|
|
42
|
-
[uuid2]: ['vehicle:write'],
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
const { data: res1 } = await axios_1.default.post('http://localhost:8082');
|
|
46
|
-
closeElevation1();
|
|
47
|
-
const { data: res2 } = await axios_1.default.post('http://localhost:8082');
|
|
48
|
-
closeElevation2();
|
|
49
|
-
res.json([res1, res2]);
|
|
50
|
-
});
|
|
51
|
-
}, 8089);
|
|
52
|
-
const server2NumberOfPermissions = [];
|
|
53
|
-
const closeServer2 = await generateApp((app) => {
|
|
54
|
-
app.use((0, index_2.middleware)());
|
|
55
|
-
app.post('/', (req, res) => {
|
|
56
|
-
const user = (0, index_1.getUser)();
|
|
57
|
-
user.privatePermissions = {
|
|
58
|
-
businessModels: {},
|
|
59
|
-
fleets: {},
|
|
60
|
-
demandSources: {},
|
|
61
|
-
};
|
|
62
|
-
server2NumberOfPermissions.push(Object.keys(user.permissions.businessModels).length);
|
|
63
|
-
server2TraceId = req.headers['x-trace-id'];
|
|
64
|
-
res.json({
|
|
65
|
-
value: req.headers['x-af-header'],
|
|
66
|
-
wkanda: req.headers['x-af-id'],
|
|
67
|
-
addedPermissions: req.headers['x-af-elevated-permissions'],
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
}, 8082);
|
|
71
|
-
const { data: [res1, res2], headers } = await axios_1.default.get('http://localhost:8089', {
|
|
72
|
-
headers: {
|
|
73
|
-
'x-af-header': 'testHeader',
|
|
74
|
-
'x-af-id': 'my-wakanda-id',
|
|
75
|
-
'x-af-user-id': (0, uuid_1.v4)(),
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
closeServer1();
|
|
79
|
-
closeServer2();
|
|
80
|
-
expect(server2NumberOfPermissions).toEqual([2, 1]);
|
|
81
|
-
expect(headers['x-trace-id']).toEqual(server2TraceId);
|
|
82
|
-
expect(res1.value).toEqual('testHeader');
|
|
83
|
-
expect(res1.wkanda).toEqual('my-wakanda-id');
|
|
84
|
-
expect(JSON.parse(res1.addedPermissions).businessModels[uuid1]).toBeDefined();
|
|
85
|
-
expect(JSON.parse(res1.addedPermissions).businessModels[uuid2]).toBeDefined();
|
|
86
|
-
expect(JSON.parse(res2.addedPermissions).businessModels[uuid1]).not.toBeDefined();
|
|
87
|
-
expect(JSON.parse(res2.addedPermissions).businessModels[uuid2]).toBeDefined();
|
|
88
|
-
});
|
|
89
|
-
it('Should throw in case of invalid UUID', async () => {
|
|
90
|
-
let error = null;
|
|
91
|
-
(0, index_1.enableTracing)({
|
|
92
|
-
outbreakOptions: {
|
|
93
|
-
headersPrefix: 'x-af-',
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
const closeServer1 = await generateApp((app) => {
|
|
97
|
-
app.use((0, index_2.middleware)());
|
|
98
|
-
app.get('/', async (req, res) => {
|
|
99
|
-
const user = (0, index_1.getUser)();
|
|
100
|
-
try {
|
|
101
|
-
const closeElevation1 = user.elevatePermissions({
|
|
102
|
-
businessModels: {
|
|
103
|
-
nnn: ['vehicle:write'],
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
await axios_1.default.post('http://localhost:8082');
|
|
107
|
-
closeElevation1();
|
|
108
|
-
}
|
|
109
|
-
catch (e) {
|
|
110
|
-
error = e;
|
|
111
|
-
}
|
|
112
|
-
res.json({ status: 'ok' });
|
|
113
|
-
});
|
|
114
|
-
}, 8089);
|
|
115
|
-
await axios_1.default.get('http://localhost:8089', {
|
|
116
|
-
headers: {
|
|
117
|
-
'x-af-header': 'testHeader',
|
|
118
|
-
'x-af-id': 'my-wakanda-id',
|
|
119
|
-
'x-af-user-id': (0, uuid_1.v4)(),
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
closeServer1();
|
|
123
|
-
expect(error.message).toEqual('Entity id on elevatePermissions is not a valid UUID, provided: nnn');
|
|
124
|
-
});
|
|
125
|
-
});
|