@uniorganization/uni-lib 4.2.0 → 4.3.1
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/dist/entities/usr.entity.d.ts +0 -1
- package/dist/entities/usr.entity.js +0 -4
- package/dist/modules/common/jwt.strategy.js +2 -7
- package/dist/modules/common/jwt.strategy.spec.d.ts +1 -0
- package/dist/modules/common/jwt.strategy.spec.js +89 -0
- package/dist/modules/tenant/redis-tenant-state.store.js +0 -3
- package/dist/modules/tenant/tenant-authorization.service.d.ts +2 -2
- package/dist/modules/tenant/tenant-authorization.service.js +34 -25
- package/dist/modules/tenant/tenant-authorization.service.spec.d.ts +1 -0
- package/dist/modules/tenant/tenant-authorization.service.spec.js +101 -0
- package/dist/modules/tenant/tenant-authorization.types.d.ts +2 -2
- package/dist/modules/tenant/tenant.types.d.ts +0 -1
- package/package.json +1 -1
|
@@ -20,10 +20,6 @@ let Users = class Users {
|
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
exports.Users = Users;
|
|
23
|
-
__decorate([
|
|
24
|
-
(0, typeorm_1.Column)({ name: 'client_id', type: 'integer' }),
|
|
25
|
-
__metadata("design:type", Number)
|
|
26
|
-
], Users.prototype, "clientId", void 0);
|
|
27
23
|
__decorate([
|
|
28
24
|
(0, typeorm_1.PrimaryGeneratedColumn)({ name: 'ID' }),
|
|
29
25
|
__metadata("design:type", Number)
|
|
@@ -38,7 +38,6 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
|
|
|
38
38
|
var _a, _b, _c;
|
|
39
39
|
const signedUser = payload === null || payload === void 0 ? void 0 : payload.user;
|
|
40
40
|
const effectiveClientId = Number(signedUser === null || signedUser === void 0 ? void 0 : signedUser.clientId);
|
|
41
|
-
const homeClientId = Number(signedUser === null || signedUser === void 0 ? void 0 : signedUser.homeClientId);
|
|
42
41
|
const originalClientId = (_a = signedUser === null || signedUser === void 0 ? void 0 : signedUser.originalClientId) !== null && _a !== void 0 ? _a : null;
|
|
43
42
|
const now = Math.floor(Date.now() / 1000);
|
|
44
43
|
if ((payload === null || payload === void 0 ? void 0 : payload.type) !== 'access' ||
|
|
@@ -47,8 +46,6 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
|
|
|
47
46
|
String(signedUser.id) !== payload.sub ||
|
|
48
47
|
!Number.isInteger(effectiveClientId) ||
|
|
49
48
|
effectiveClientId <= 0 ||
|
|
50
|
-
!Number.isInteger(homeClientId) ||
|
|
51
|
-
homeClientId <= 0 ||
|
|
52
49
|
!Number.isInteger(payload.exp) ||
|
|
53
50
|
payload.exp <= now ||
|
|
54
51
|
!this.validTokenIdentifier(payload.jti) ||
|
|
@@ -62,7 +59,6 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
|
|
|
62
59
|
const impersonating = originalClientId !== null;
|
|
63
60
|
if ((impersonating &&
|
|
64
61
|
(originalClientId !== tenant_1.NEWTECH_CLIENT_ID ||
|
|
65
|
-
homeClientId !== tenant_1.NEWTECH_CLIENT_ID ||
|
|
66
62
|
effectiveClientId === tenant_1.NEWTECH_CLIENT_ID ||
|
|
67
63
|
!((_b = signedUser.roles) === null || _b === void 0 ? void 0 : _b.includes(tenant_1.TENANT_IMPERSONATOR_ROLE)) ||
|
|
68
64
|
!signedUser.impersonatedAt ||
|
|
@@ -71,8 +67,8 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
|
|
|
71
67
|
throw new common_1.UnauthorizedException();
|
|
72
68
|
}
|
|
73
69
|
const resolved = impersonating
|
|
74
|
-
? yield this.authorization.resolveImpersonation(signedUser.id,
|
|
75
|
-
: yield this.authorization.resolve(signedUser.id, effectiveClientId
|
|
70
|
+
? yield this.authorization.resolveImpersonation(signedUser.id, originalClientId, effectiveClientId)
|
|
71
|
+
: yield this.authorization.resolve(signedUser.id, effectiveClientId);
|
|
76
72
|
const { identity, snapshot } = resolved;
|
|
77
73
|
const isMasterTenantAdmin = !impersonating &&
|
|
78
74
|
snapshot.clientId === tenant_1.NEWTECH_CLIENT_ID &&
|
|
@@ -92,7 +88,6 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
|
|
|
92
88
|
password_expired: identity.passwordExpired,
|
|
93
89
|
viewEvaluations: identity.viewEvaluations,
|
|
94
90
|
ern: identity.ern,
|
|
95
|
-
homeClientId: identity.homeClientId,
|
|
96
91
|
clientId: snapshot.clientId,
|
|
97
92
|
clientAlias: snapshot.clientAlias,
|
|
98
93
|
clientName: snapshot.clientName,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
const common_1 = require("@nestjs/common");
|
|
13
|
+
const tenant_1 = require("../tenant");
|
|
14
|
+
const jwt_strategy_1 = require("./jwt.strategy");
|
|
15
|
+
describe('JwtStrategy impersonated sessions', () => {
|
|
16
|
+
process.env.JWT_SECRET = 'test-secret';
|
|
17
|
+
const authorization = { resolveImpersonation: jest.fn() };
|
|
18
|
+
const securityState = { isAccessTokenRevoked: jest.fn() };
|
|
19
|
+
const strategy = new jwt_strategy_1.JwtStrategy(authorization, securityState);
|
|
20
|
+
const payload = (overrides = {}) => {
|
|
21
|
+
const now = Math.floor(Date.now() / 1000);
|
|
22
|
+
const expiresAt = now + 900;
|
|
23
|
+
const user = Object.assign({ id: 1, homeClientId: 2, clientId: 2, clientName: 'Altera', clientAlias: 'altera', jti: 'impersonated-jti', tokenExpiresAt: expiresAt, originalClientId: tenant_1.NEWTECH_CLIENT_ID, impersonatedAt: new Date().toISOString(), impersonationReason: 'Investigate issue', roles: [tenant_1.TENANT_IMPERSONATOR_ROLE], privileges: [], role: {
|
|
24
|
+
id: 0,
|
|
25
|
+
code: tenant_1.TENANT_IMPERSONATOR_ROLE,
|
|
26
|
+
name: 'Tenant impersonator',
|
|
27
|
+
scope: 'GLOBAL',
|
|
28
|
+
clientId: null,
|
|
29
|
+
}, permissions: [], authzVersion: 1, isMasterTenantAdmin: false }, overrides);
|
|
30
|
+
return {
|
|
31
|
+
type: 'access',
|
|
32
|
+
user,
|
|
33
|
+
sub: '1',
|
|
34
|
+
iat: now - 10,
|
|
35
|
+
exp: expiresAt,
|
|
36
|
+
jti: 'impersonated-jti',
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
jest.clearAllMocks();
|
|
41
|
+
securityState.isAccessTokenRevoked.mockResolvedValue(false);
|
|
42
|
+
authorization.resolveImpersonation.mockResolvedValue({
|
|
43
|
+
identity: { id: 1, homeClientId: 2 },
|
|
44
|
+
snapshot: {
|
|
45
|
+
userId: 1,
|
|
46
|
+
homeClientId: 2,
|
|
47
|
+
clientId: 2,
|
|
48
|
+
clientName: 'Altera',
|
|
49
|
+
clientAlias: 'altera',
|
|
50
|
+
roles: [
|
|
51
|
+
{
|
|
52
|
+
roleId: 0,
|
|
53
|
+
roleName: tenant_1.TENANT_IMPERSONATOR_ROLE,
|
|
54
|
+
moduleId: 0,
|
|
55
|
+
moduleName: tenant_1.TENANT_IMPERSONATOR_ROLE,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
privileges: [],
|
|
59
|
+
accessRole: {
|
|
60
|
+
id: 0,
|
|
61
|
+
code: tenant_1.TENANT_IMPERSONATOR_ROLE,
|
|
62
|
+
name: 'Tenant impersonator',
|
|
63
|
+
scope: 'GLOBAL',
|
|
64
|
+
clientId: null,
|
|
65
|
+
},
|
|
66
|
+
permissions: [],
|
|
67
|
+
authzVersion: 1,
|
|
68
|
+
identity: { id: 1, homeClientId: 2 },
|
|
69
|
+
cachedAt: new Date().toISOString(),
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it('revalidates an impersonated token with an Altera home client through Newtech', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
|
+
const validated = yield strategy.validate(payload());
|
|
75
|
+
expect(authorization.resolveImpersonation).toHaveBeenCalledWith(1, 1, 2, 2);
|
|
76
|
+
expect(validated.homeClientId).toBe(2);
|
|
77
|
+
expect(validated.clientId).toBe(2);
|
|
78
|
+
expect(validated.originalClientId).toBe(tenant_1.NEWTECH_CLIENT_ID);
|
|
79
|
+
}));
|
|
80
|
+
const invalidTokenCases = [
|
|
81
|
+
[{ originalClientId: 2 }, 'a non-Newtech origin'],
|
|
82
|
+
[{ roles: [] }, 'a token without the impersonator role'],
|
|
83
|
+
[{ impersonationReason: null }, 'a token without a support reason'],
|
|
84
|
+
];
|
|
85
|
+
it.each(invalidTokenCases)('rejects %s', (overrides) => __awaiter(void 0, void 0, void 0, function* () {
|
|
86
|
+
yield expect(strategy.validate(payload(overrides))).rejects.toBeInstanceOf(common_1.UnauthorizedException);
|
|
87
|
+
expect(authorization.resolveImpersonation).not.toHaveBeenCalled();
|
|
88
|
+
}));
|
|
89
|
+
});
|
|
@@ -138,8 +138,6 @@ let RedisTenantStateStore = RedisTenantStateStore_1 = class RedisTenantStateStor
|
|
|
138
138
|
return Boolean(value &&
|
|
139
139
|
Number.isInteger(value.userId) &&
|
|
140
140
|
value.userId > 0 &&
|
|
141
|
-
Number.isInteger(value.homeClientId) &&
|
|
142
|
-
value.homeClientId > 0 &&
|
|
143
141
|
Number.isInteger(value.clientId) &&
|
|
144
142
|
value.clientId > 0 &&
|
|
145
143
|
typeof value.clientName === 'string' &&
|
|
@@ -165,7 +163,6 @@ let RedisTenantStateStore = RedisTenantStateStore_1 = class RedisTenantStateStor
|
|
|
165
163
|
Number.isInteger(value.authzVersion) &&
|
|
166
164
|
value.authzVersion > 0 &&
|
|
167
165
|
((_a = value.identity) === null || _a === void 0 ? void 0 : _a.id) === value.userId &&
|
|
168
|
-
value.identity.homeClientId === value.homeClientId &&
|
|
169
166
|
typeof value.cachedAt === 'string');
|
|
170
167
|
}
|
|
171
168
|
};
|
|
@@ -12,8 +12,8 @@ export declare class TenantAuthorizationService {
|
|
|
12
12
|
private readonly userRoleRepository;
|
|
13
13
|
private readonly logger;
|
|
14
14
|
constructor(store: TenantAuthorizationStore, userRepository: Repository<Users>, clientRepository: Repository<Client>, userRoleRepository: Repository<UserRoles>);
|
|
15
|
-
resolve(userId: number, clientId: number,
|
|
16
|
-
resolveImpersonation(userId: number,
|
|
15
|
+
resolve(userId: number, clientId: number, _legacyOriginClientId?: number): Promise<ResolvedTenantAuthorization>;
|
|
16
|
+
resolveImpersonation(userId: number, originClientId: number, targetClientIdOrLegacyOriginClientId: number, legacyTargetClientId?: number): Promise<ResolvedTenantAuthorization>;
|
|
17
17
|
invalidate(userId: number, clientId: number): Promise<void>;
|
|
18
18
|
invalidateMany(clientId: number, userIds: readonly number[]): Promise<void>;
|
|
19
19
|
private loadFromDatabase;
|
|
@@ -41,36 +41,36 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
41
41
|
this.userRoleRepository = userRoleRepository;
|
|
42
42
|
this.logger = new common_1.Logger(TenantAuthorizationService_1.name);
|
|
43
43
|
}
|
|
44
|
-
resolve(userId, clientId,
|
|
44
|
+
resolve(userId, clientId, _legacyOriginClientId) {
|
|
45
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
-
var _a;
|
|
46
|
+
var _a, _b;
|
|
47
47
|
this.assertPositiveInteger(userId);
|
|
48
48
|
this.assertPositiveInteger(clientId);
|
|
49
|
-
this.assertPositiveInteger(expectedHomeClientId);
|
|
50
49
|
const key = (0, tenant_authorization_keys_1.tenantAuthorizationKey)(clientId, userId);
|
|
51
50
|
const cached = yield this.readCache(key);
|
|
52
51
|
if (cached &&
|
|
53
52
|
cached.userId === userId &&
|
|
54
53
|
cached.clientId === clientId &&
|
|
55
|
-
cached.homeClientId === expectedHomeClientId &&
|
|
56
54
|
((_a = cached.identity) === null || _a === void 0 ? void 0 : _a.id) === userId &&
|
|
57
|
-
cached.identity.homeClientId === expectedHomeClientId &&
|
|
58
55
|
this.isFresh(cached.cachedAt) &&
|
|
59
|
-
cached.roles.length > 0
|
|
56
|
+
cached.roles.length > 0 &&
|
|
57
|
+
Array.isArray(cached.permissions) &&
|
|
58
|
+
((_b = cached.accessRole) === null || _b === void 0 ? void 0 : _b.id)) {
|
|
60
59
|
return { identity: cached.identity, snapshot: cached };
|
|
61
60
|
}
|
|
62
|
-
const resolved = yield this.loadFromDatabase(userId, clientId
|
|
61
|
+
const resolved = yield this.loadFromDatabase(userId, clientId);
|
|
63
62
|
yield this.writeCache(key, resolved.snapshot);
|
|
64
63
|
return resolved;
|
|
65
64
|
});
|
|
66
65
|
}
|
|
67
|
-
resolveImpersonation(userId,
|
|
66
|
+
resolveImpersonation(userId, originClientId, targetClientIdOrLegacyOriginClientId, legacyTargetClientId) {
|
|
68
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
-
|
|
68
|
+
const targetClientId = legacyTargetClientId !== null && legacyTargetClientId !== void 0 ? legacyTargetClientId : targetClientIdOrLegacyOriginClientId;
|
|
69
|
+
if (originClientId !== tenant_constants_1.NEWTECH_CLIENT_ID ||
|
|
70
70
|
targetClientId === tenant_constants_1.NEWTECH_CLIENT_ID) {
|
|
71
71
|
throw new common_1.UnauthorizedException();
|
|
72
72
|
}
|
|
73
|
-
const origin = yield this.resolve(userId,
|
|
73
|
+
const origin = yield this.resolve(userId, originClientId);
|
|
74
74
|
const isNewtechMaster = origin.snapshot.permissions.includes('master.users.read') ||
|
|
75
75
|
origin.snapshot.roles.some((role) => role.moduleName.toLowerCase() === 'root' &&
|
|
76
76
|
role.roleName.toLowerCase() === 'sysadmin');
|
|
@@ -82,7 +82,6 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
82
82
|
}
|
|
83
83
|
const snapshot = {
|
|
84
84
|
userId,
|
|
85
|
-
homeClientId,
|
|
86
85
|
clientId: targetClientId,
|
|
87
86
|
clientName: targetClient.name,
|
|
88
87
|
clientAlias: targetClient.alias,
|
|
@@ -123,8 +122,9 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
123
122
|
}
|
|
124
123
|
});
|
|
125
124
|
}
|
|
126
|
-
loadFromDatabase(userId, clientId
|
|
125
|
+
loadFromDatabase(userId, clientId) {
|
|
127
126
|
return __awaiter(this, void 0, void 0, function* () {
|
|
127
|
+
var _a, _b;
|
|
128
128
|
const [user, client, assignments, accessRows] = yield Promise.all([
|
|
129
129
|
this.userRepository.findOne({ where: { id: userId } }),
|
|
130
130
|
this.clientRepository.findOne({
|
|
@@ -165,20 +165,31 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
165
165
|
]);
|
|
166
166
|
if (!user ||
|
|
167
167
|
!Number(user.is_active) ||
|
|
168
|
-
user.clientId !== expectedHomeClientId ||
|
|
169
168
|
!client ||
|
|
170
|
-
accessRows.length
|
|
169
|
+
accessRows.length > 1 ||
|
|
170
|
+
(accessRows.length === 0 && assignments.length === 0)) {
|
|
171
171
|
throw new common_1.UnauthorizedException();
|
|
172
172
|
}
|
|
173
173
|
const accessRow = accessRows[0];
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
const usingLegacyCompatibility = accessRows.length === 0 || accessRow.code.startsWith('migrated-');
|
|
175
|
+
const legacyRole = (_a = assignments[0]) === null || _a === void 0 ? void 0 : _a.role;
|
|
176
|
+
const accessRole = accessRow
|
|
177
|
+
? {
|
|
178
|
+
id: Number(accessRow.id),
|
|
179
|
+
code: accessRow.code,
|
|
180
|
+
name: accessRow.name,
|
|
181
|
+
scope: accessRow.scope,
|
|
182
|
+
clientId: accessRow.roleClientId == null
|
|
183
|
+
? null
|
|
184
|
+
: Number(accessRow.roleClientId),
|
|
185
|
+
}
|
|
186
|
+
: {
|
|
187
|
+
id: Number(legacyRole === null || legacyRole === void 0 ? void 0 : legacyRole.id),
|
|
188
|
+
code: `legacy-${Number(legacyRole === null || legacyRole === void 0 ? void 0 : legacyRole.id)}`,
|
|
189
|
+
name: (_b = legacyRole === null || legacyRole === void 0 ? void 0 : legacyRole.roleName) !== null && _b !== void 0 ? _b : 'Legacy access',
|
|
190
|
+
scope: 'CLIENT',
|
|
191
|
+
clientId,
|
|
192
|
+
};
|
|
182
193
|
const roles = usingLegacyCompatibility && assignments.length
|
|
183
194
|
? assignments.map((assignment) => {
|
|
184
195
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -215,7 +226,6 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
215
226
|
: [];
|
|
216
227
|
const identity = {
|
|
217
228
|
id: user.id,
|
|
218
|
-
homeClientId: user.clientId,
|
|
219
229
|
firstName: user.first_name,
|
|
220
230
|
username: user.username,
|
|
221
231
|
lastName: user.last_name,
|
|
@@ -230,7 +240,6 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
230
240
|
};
|
|
231
241
|
const snapshot = {
|
|
232
242
|
userId,
|
|
233
|
-
homeClientId: user.clientId,
|
|
234
243
|
clientId,
|
|
235
244
|
clientName: client.name,
|
|
236
245
|
clientAlias: client.alias,
|
|
@@ -240,7 +249,7 @@ let TenantAuthorizationService = TenantAuthorizationService_1 = class TenantAuth
|
|
|
240
249
|
permissions: Array.isArray(accessRow.permissions)
|
|
241
250
|
? accessRow.permissions
|
|
242
251
|
: [],
|
|
243
|
-
authzVersion: Number(accessRow.authzVersion),
|
|
252
|
+
authzVersion: accessRow ? Number(accessRow.authzVersion) : 1,
|
|
244
253
|
identity,
|
|
245
254
|
cachedAt: new Date().toISOString(),
|
|
246
255
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
const common_1 = require("@nestjs/common");
|
|
13
|
+
const tenant_constants_1 = require("./tenant.constants");
|
|
14
|
+
const tenant_authorization_service_1 = require("./tenant-authorization.service");
|
|
15
|
+
describe('TenantAuthorizationService impersonation', () => {
|
|
16
|
+
const store = {
|
|
17
|
+
get: jest.fn(),
|
|
18
|
+
set: jest.fn(),
|
|
19
|
+
delete: jest.fn(),
|
|
20
|
+
deleteMany: jest.fn(),
|
|
21
|
+
};
|
|
22
|
+
const userRepository = {};
|
|
23
|
+
const clientRepository = { findOne: jest.fn() };
|
|
24
|
+
const userRoleRepository = {};
|
|
25
|
+
const service = new tenant_authorization_service_1.TenantAuthorizationService(store, userRepository, clientRepository, userRoleRepository);
|
|
26
|
+
const origin = (permissions) => ({
|
|
27
|
+
identity: {
|
|
28
|
+
id: 1,
|
|
29
|
+
homeClientId: 2,
|
|
30
|
+
firstName: 'Root',
|
|
31
|
+
lastName: 'User',
|
|
32
|
+
},
|
|
33
|
+
snapshot: {
|
|
34
|
+
userId: 1,
|
|
35
|
+
homeClientId: 2,
|
|
36
|
+
clientId: tenant_constants_1.NEWTECH_CLIENT_ID,
|
|
37
|
+
clientName: 'Newtech',
|
|
38
|
+
clientAlias: 'newtech',
|
|
39
|
+
roles: [
|
|
40
|
+
{
|
|
41
|
+
roleId: 8,
|
|
42
|
+
roleName: 'Master Root',
|
|
43
|
+
moduleId: 8,
|
|
44
|
+
moduleName: 'access-control',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
privileges: [],
|
|
48
|
+
accessRole: {
|
|
49
|
+
id: 8,
|
|
50
|
+
code: 'master-root',
|
|
51
|
+
name: 'Master Root',
|
|
52
|
+
scope: 'CLIENT',
|
|
53
|
+
clientId: tenant_constants_1.NEWTECH_CLIENT_ID,
|
|
54
|
+
},
|
|
55
|
+
permissions,
|
|
56
|
+
authzVersion: 3,
|
|
57
|
+
identity: {
|
|
58
|
+
id: 1,
|
|
59
|
+
homeClientId: 2,
|
|
60
|
+
firstName: 'Root',
|
|
61
|
+
lastName: 'User',
|
|
62
|
+
},
|
|
63
|
+
cachedAt: new Date().toISOString(),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
jest.clearAllMocks();
|
|
68
|
+
clientRepository.findOne.mockResolvedValue({
|
|
69
|
+
id: 2,
|
|
70
|
+
name: 'Altera',
|
|
71
|
+
alias: 'altera',
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
it('resolves support from active Newtech when the permanent home is Altera', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
75
|
+
jest
|
|
76
|
+
.spyOn(service, 'resolve')
|
|
77
|
+
.mockResolvedValue(origin(['master.users.read']));
|
|
78
|
+
const resolved = yield service.resolveImpersonation(1, tenant_constants_1.NEWTECH_CLIENT_ID, 2, 2);
|
|
79
|
+
expect(service.resolve).toHaveBeenCalledWith(1, tenant_constants_1.NEWTECH_CLIENT_ID, 2);
|
|
80
|
+
expect(resolved.snapshot.clientId).toBe(2);
|
|
81
|
+
expect(resolved.snapshot.homeClientId).toBe(2);
|
|
82
|
+
expect(resolved.snapshot.clientName).toBe('Altera');
|
|
83
|
+
}));
|
|
84
|
+
it('rejects an origin outside Newtech', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
85
|
+
yield expect(service.resolveImpersonation(1, 2, 2, 2)).rejects.toBeInstanceOf(common_1.UnauthorizedException);
|
|
86
|
+
}));
|
|
87
|
+
it('rejects Newtech as the target', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
yield expect(service.resolveImpersonation(1, tenant_constants_1.NEWTECH_CLIENT_ID, 2, tenant_constants_1.NEWTECH_CLIENT_ID)).rejects.toBeInstanceOf(common_1.UnauthorizedException);
|
|
89
|
+
}));
|
|
90
|
+
it('rejects an origin without master access', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
|
+
jest.spyOn(service, 'resolve').mockResolvedValue(origin([]));
|
|
92
|
+
yield expect(service.resolveImpersonation(1, tenant_constants_1.NEWTECH_CLIENT_ID, 2, 2)).rejects.toBeInstanceOf(common_1.UnauthorizedException);
|
|
93
|
+
}));
|
|
94
|
+
it('rejects an inactive or missing target client', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
|
+
jest
|
|
96
|
+
.spyOn(service, 'resolve')
|
|
97
|
+
.mockResolvedValue(origin(['master.users.read']));
|
|
98
|
+
clientRepository.findOne.mockResolvedValue(null);
|
|
99
|
+
yield expect(service.resolveImpersonation(1, tenant_constants_1.NEWTECH_CLIENT_ID, 2, 2)).rejects.toBeInstanceOf(common_1.UnauthorizedException);
|
|
100
|
+
}));
|
|
101
|
+
});
|
|
@@ -6,7 +6,6 @@ export interface TenantRoleAuthorization {
|
|
|
6
6
|
}
|
|
7
7
|
export interface TenantAuthorizationSnapshot {
|
|
8
8
|
userId: number;
|
|
9
|
-
homeClientId: number;
|
|
10
9
|
clientId: number;
|
|
11
10
|
clientName: string;
|
|
12
11
|
clientAlias: string;
|
|
@@ -17,6 +16,7 @@ export interface TenantAuthorizationSnapshot {
|
|
|
17
16
|
authzVersion: number;
|
|
18
17
|
identity: TenantAuthorizationIdentity;
|
|
19
18
|
cachedAt: string;
|
|
19
|
+
[key: string]: unknown;
|
|
20
20
|
}
|
|
21
21
|
export interface TenantAccessRoleAuthorization {
|
|
22
22
|
id: number;
|
|
@@ -27,7 +27,6 @@ export interface TenantAccessRoleAuthorization {
|
|
|
27
27
|
}
|
|
28
28
|
export interface TenantAuthorizationIdentity {
|
|
29
29
|
id: number;
|
|
30
|
-
homeClientId: number;
|
|
31
30
|
firstName?: string;
|
|
32
31
|
username?: string;
|
|
33
32
|
lastName?: string;
|
|
@@ -39,6 +38,7 @@ export interface TenantAuthorizationIdentity {
|
|
|
39
38
|
passwordExpired?: number;
|
|
40
39
|
viewEvaluations?: boolean;
|
|
41
40
|
ern?: string;
|
|
41
|
+
[key: string]: unknown;
|
|
42
42
|
}
|
|
43
43
|
export interface ResolvedTenantAuthorization {
|
|
44
44
|
identity: TenantAuthorizationIdentity;
|