@autofleet/zehut 1.8.3 → 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/lib/user/ApiUser.d.ts +3 -2
- package/lib/user/ApiUser.js +13 -24
- package/lib/user/api-user-flows.test.js +10 -21
- package/lib/user/index.js +22 -30
- package/package.json +1 -1
package/lib/user/ApiUser.d.ts
CHANGED
|
@@ -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
|
-
|
|
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>;
|
package/lib/user/ApiUser.js
CHANGED
|
@@ -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.
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
101
|
-
return permissions;
|
|
86
|
+
return Object.assign(Object.assign({}, this.privatePermissions), this.privateElevatedPermissions);
|
|
102
87
|
}
|
|
103
88
|
elevatePermissions(addedPermissions) {
|
|
104
|
-
|
|
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.
|
|
112
|
-
currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(
|
|
99
|
+
this.privateElevatedPermissions = newElevation;
|
|
100
|
+
currentUserTrace.context.set(exports.ELEVATED_PERMISSIONS_HEADER, JSON.stringify(newElevation));
|
|
101
|
+
this.elevationCallsCounter += 1;
|
|
113
102
|
return () => {
|
|
114
|
-
this.
|
|
115
|
-
|
|
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
|
|
43
|
+
const closeElevation = user.elevatePermissions({
|
|
45
44
|
businessModels: {
|
|
46
|
-
[
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
closeElevation2();
|
|
58
|
-
res.json([res1, res2]);
|
|
49
|
+
closeElevation();
|
|
50
|
+
res.json(res1);
|
|
59
51
|
}));
|
|
60
|
-
},
|
|
61
|
-
|
|
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
|
|
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:
|
|
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(
|
|
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
|
|
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
|
});
|
package/lib/user/index.js
CHANGED
|
@@ -34,40 +34,32 @@ const ApiUser_1 = __importStar(require("./ApiUser"));
|
|
|
34
34
|
const utils_1 = require("../utils");
|
|
35
35
|
const tracer_1 = require("../tracer");
|
|
36
36
|
exports.middleware = (options = {}) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
yield userObject.useCustomPermissionLoader(customPermissionLoader);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
yield userObject.getUserPermissions();
|
|
54
|
-
}
|
|
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);
|
|
55
50
|
}
|
|
56
|
-
|
|
57
|
-
yield userObject.
|
|
51
|
+
else {
|
|
52
|
+
yield userObject.getUserPermissions();
|
|
58
53
|
}
|
|
59
|
-
req.user = userObject;
|
|
60
|
-
trace.context.set('userObject', userObject);
|
|
61
|
-
// Added in order to support outbreak.
|
|
62
|
-
req.headers['x-af-user-permissions'] = userObject;
|
|
63
|
-
return next();
|
|
64
54
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return res.json({
|
|
68
|
-
error: 'cannot authenticate user',
|
|
69
|
-
});
|
|
55
|
+
if (eagerLoadUserPermissionsLegacy) {
|
|
56
|
+
yield userObject.getUserPermissionsLegacy();
|
|
70
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();
|
|
71
63
|
});
|
|
72
64
|
exports.middlewareWithDecode = (options = {}) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
65
|
var _a, _b;
|