@restorecommerce/acs-client 3.0.22 → 3.1.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/CHANGELOG.md +19 -0
- package/lib/acs/authz.d.ts +4 -4
- package/lib/acs/authz.d.ts.map +1 -1
- package/lib/acs/authz.js +77 -108
- package/lib/acs/authz.js.map +1 -1
- package/lib/acs/cache.d.ts.map +1 -1
- package/lib/acs/cache.js +31 -65
- package/lib/acs/cache.js.map +1 -1
- package/lib/acs/decorators.d.ts +3 -3
- package/lib/acs/decorators.d.ts.map +1 -1
- package/lib/acs/decorators.js +39 -51
- package/lib/acs/decorators.js.map +1 -1
- package/lib/acs/errors.js +3 -9
- package/lib/acs/errors.js.map +1 -1
- package/lib/acs/interfaces.d.ts +10 -10
- package/lib/acs/interfaces.d.ts.map +1 -1
- package/lib/acs/interfaces.js +9 -18
- package/lib/acs/interfaces.js.map +1 -1
- package/lib/acs/middleware.js +3 -7
- package/lib/acs/middleware.js.map +1 -1
- package/lib/acs/resolver.d.ts +5 -5
- package/lib/acs/resolver.d.ts.map +1 -1
- package/lib/acs/resolver.js +75 -85
- package/lib/acs/resolver.js.map +1 -1
- package/lib/config.js +5 -9
- package/lib/config.js.map +1 -1
- package/lib/index.d.ts +9 -9
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +9 -25
- package/lib/index.js.map +1 -1
- package/lib/logger.js +7 -11
- package/lib/logger.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils.d.ts +4 -4
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +82 -94
- package/lib/utils.js.map +1 -1
- package/package.json +10 -9
package/lib/utils.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import lodash from 'lodash';
|
|
2
2
|
export declare const _: lodash.LoDashStatic;
|
|
3
|
-
import { PolicySetRQ, PolicySetRQResponse, ResourceFilterMap, CustomQueryArgs, DecisionResponse, ACSResource, AuthZAction, ResolvedSubject, Obligation } from './acs/interfaces';
|
|
4
|
-
import { QueryArguments, UserQueryArguments } from './acs/resolver';
|
|
5
|
-
import { Subject, DeepPartial } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/auth';
|
|
6
|
-
import { Attribute } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/attribute';
|
|
3
|
+
import { PolicySetRQ, PolicySetRQResponse, ResourceFilterMap, CustomQueryArgs, DecisionResponse, ACSResource, AuthZAction, ResolvedSubject, Obligation } from './acs/interfaces.js';
|
|
4
|
+
import { QueryArguments, UserQueryArguments } from './acs/resolver.js';
|
|
5
|
+
import { Subject, DeepPartial } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/auth.js';
|
|
6
|
+
import { Attribute } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/attribute.js';
|
|
7
7
|
export declare const handleError: (err: string | Error | any) => any;
|
|
8
8
|
export declare const buildFilterPermissions: (policySet: PolicySetRQ, subject: ResolvedSubject, reqResources: any, database: string) => Promise<QueryArguments | UserQueryArguments>;
|
|
9
9
|
export declare const generateOperationStatus: (code?: number, message?: string) => {
|
package/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,CAAC,qBAAS,CAAC;AACxB,OAAO,EACL,WAAW,EAAE,mBAAmB,EAChC,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAC9E,eAAe,EAAE,UAAU,EAC5B,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,eAAO,MAAM,CAAC,qBAAS,CAAC;AACxB,OAAO,EACL,WAAW,EAAE,mBAAmB,EAChC,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAC9E,eAAe,EAAE,UAAU,EAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAOvE,OAAO,EACL,OAAO,EACP,WAAW,EACZ,MAAM,mFAAmF,CAAC;AAC3F,OAAO,EAAE,SAAS,EAAE,MAAM,wFAAwF,CAAC;AASnH,eAAO,MAAM,WAAW,QAAS,MAAM,GAAG,KAAK,GAAG,GAAG,KAAG,GAQvD,CAAC;AAmTF,eAAO,MAAM,sBAAsB,cACtB,WAAW,WACb,eAAe,gBACV,GAAG,YACP,MAAM,KACf,OAAO,CAAC,cAAc,GAAG,kBAAkB,CAgM7C,CAAC;AAWF,eAAO,MAAM,uBAAuB,UAAW,MAAM,YAAY,MAAM;;;CAQtE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,mBAAoB,WAAW,CAAC,SAAS,CAAC,EAAE,qBAAqB,WAAW,CAAC,SAAS,CAAC,EAAE,KAAG,OAyDvH,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;IACvC,eAAe,EAAE,eAAe,EAAE,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,aACxB,WAAW,EAAE,qBACJ,mBAAmB,aAC3B,GAAG,UAAU,WAAW,WAC1B,WAAW,CAAC,OAAO,CAAC,aAClB,MAAM,iBACF,OAAO,eACT,MAAM,YACT,UAAU,GAAG,UAAU,KAChC,OAAO,CAAC,iBAAiB,GAAG,gBAAgB,CAgG9C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,gBAAiB,SAAS,EAAE,KAAG,UAAU,EA0BvF,CAAC"}
|
package/lib/utils.js
CHANGED
|
@@ -1,33 +1,26 @@
|
|
|
1
|
-
|
|
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.mapResourceURNObligationProperties = exports.createResourceFilterMap = exports.attributesMatch = exports.generateOperationStatus = exports.buildFilterPermissions = exports.handleError = exports._ = void 0;
|
|
7
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
1
|
+
import lodash from 'lodash';
|
|
8
2
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
9
|
-
require('deepdash')(
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
require('deepdash')(lodash);
|
|
4
|
+
export const _ = lodash;
|
|
5
|
+
import { errors, cfg } from './config.js';
|
|
12
6
|
// @ts-expect-error TS7016
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const handleError = (err) => {
|
|
7
|
+
import nodeEval from 'node-eval';
|
|
8
|
+
import logger from './logger.js';
|
|
9
|
+
import { get } from './acs/cache.js';
|
|
10
|
+
import { formatResourceType } from './acs/authz.js';
|
|
11
|
+
import { Filter_Operation, FilterOp_Operator } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/resource_base.js';
|
|
12
|
+
import { Response_Decision } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/access_control.js';
|
|
13
|
+
import { Effect } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/rule.js';
|
|
14
|
+
export const handleError = (err) => {
|
|
21
15
|
let error;
|
|
22
16
|
if (typeof err == 'string') {
|
|
23
|
-
error =
|
|
17
|
+
error = errors[err] || errors.SYSTEM_ERROR;
|
|
24
18
|
}
|
|
25
19
|
else {
|
|
26
|
-
error =
|
|
20
|
+
error = errors.SYSTEM_ERROR;
|
|
27
21
|
}
|
|
28
22
|
return error;
|
|
29
23
|
};
|
|
30
|
-
exports.handleError = handleError;
|
|
31
24
|
const reduceUserScope = (hrScope, reducedUserScope, hierarchicalRoleScoping) => {
|
|
32
25
|
reducedUserScope.push(hrScope.id);
|
|
33
26
|
if (hrScope?.children?.length > 0 && hierarchicalRoleScoping === 'true') {
|
|
@@ -40,7 +33,7 @@ const checkTargetScopeExists = (hrScopes, targetScope, reducedUserScope, hierarc
|
|
|
40
33
|
return hrScopes.some((hrScope) => {
|
|
41
34
|
if (hrScope?.id === targetScope) {
|
|
42
35
|
// found the target scope object, iterate and put the orgs in reducedUserScope array
|
|
43
|
-
|
|
36
|
+
logger?.debug(`Target entity match found in the user's hierarchical scope`);
|
|
44
37
|
reduceUserScope(hrScope, reducedUserScope, hierarchicalRoleScopingCheck);
|
|
45
38
|
return true;
|
|
46
39
|
}
|
|
@@ -62,7 +55,7 @@ const checkSubjectMatch = (user, ruleSubjectAttributes, reducedUserScope) => {
|
|
|
62
55
|
let hierarchicalRoleScopingCheck = 'true'; // by default HR scoping check is considered
|
|
63
56
|
let ruleRoleValue;
|
|
64
57
|
let ruleRoleScopeEntityName;
|
|
65
|
-
const urns =
|
|
58
|
+
const urns = cfg.get('authorization:urns');
|
|
66
59
|
if (ruleSubjectAttributes?.length === 0) {
|
|
67
60
|
return true;
|
|
68
61
|
}
|
|
@@ -75,21 +68,21 @@ const checkSubjectMatch = (user, ruleSubjectAttributes, reducedUserScope) => {
|
|
|
75
68
|
}
|
|
76
69
|
else if (attribute.id === urns.role) { // urns.role -> urn:restorecommerce:acs:names:role
|
|
77
70
|
ruleRoleValue = attribute.value;
|
|
78
|
-
|
|
71
|
+
logger?.debug(`Found Rule's Subject role ${ruleRoleValue}`);
|
|
79
72
|
}
|
|
80
73
|
else if (attribute?.id === urns.hierarchicalRoleScoping) {
|
|
81
74
|
hierarchicalRoleScopingCheck = attribute.value;
|
|
82
|
-
|
|
75
|
+
logger?.debug('HR Scoping URN set on rule', { hierarchicalRoleScopingCheck });
|
|
83
76
|
}
|
|
84
77
|
}
|
|
85
78
|
if (ruleRoleValue && ruleRoleScopeEntityName) {
|
|
86
79
|
const matchingRoleScopedInstance = user?.role_associations?.flatMap(ra => ra.attributes).filter(a => a?.id === urns?.roleScopingEntity
|
|
87
80
|
&& a?.value === ruleRoleScopeEntityName).flatMap(a => a?.attributes).filter(aa => aa?.id === urns?.roleScopingInstance).map(aa => aa.value);
|
|
88
|
-
|
|
81
|
+
logger?.debug('Role scoped instances for matching entity', { id: user?.id, ruleRoleScopeEntityName, matchingRoleScopedInstance });
|
|
89
82
|
// validate HR scope root ID contains the role scope instances
|
|
90
83
|
const hrScopeExist = user?.hierarchical_scopes?.some((hrScope) => matchingRoleScopedInstance.includes(hrScope.id));
|
|
91
84
|
if (!hrScopeExist) {
|
|
92
|
-
|
|
85
|
+
logger?.info('Hierarchial scopes for matching role does not exist', {
|
|
93
86
|
role: ruleRoleValue,
|
|
94
87
|
hrScopes: user?.hierarchical_scopes,
|
|
95
88
|
instances: matchingRoleScopedInstance
|
|
@@ -97,12 +90,12 @@ const checkSubjectMatch = (user, ruleSubjectAttributes, reducedUserScope) => {
|
|
|
97
90
|
return false;
|
|
98
91
|
}
|
|
99
92
|
else if (hrScopeExist && user?.scope) {
|
|
100
|
-
|
|
93
|
+
logger?.debug('Target scope set and HR scopes exist, validate target scope from HR scopes', { targetScope: user?.scope });
|
|
101
94
|
return checkTargetScopeExists(user?.hierarchical_scopes?.filter((hrScope) => matchingRoleScopedInstance?.includes(hrScope?.id) && hrScope?.role === ruleRoleValue), user?.scope, reducedUserScope, hierarchicalRoleScopingCheck);
|
|
102
95
|
}
|
|
103
96
|
else if (hrScopeExist && !user.scope) {
|
|
104
97
|
// HR scope match exist but user has not provided scope so still a match is considered
|
|
105
|
-
|
|
98
|
+
logger?.debug('Target scope not provided using full HR tree for matched role', { role: ruleRoleValue });
|
|
106
99
|
// if no scope is provided then use the complete HR tree for user scopes
|
|
107
100
|
user?.hierarchical_scopes?.filter((hrScope) => matchingRoleScopedInstance?.includes(hrScope?.id)
|
|
108
101
|
&& hrScope?.role === ruleRoleValue).forEach((eachHRScope) => {
|
|
@@ -117,7 +110,7 @@ const checkSubjectMatch = (user, ruleSubjectAttributes, reducedUserScope) => {
|
|
|
117
110
|
return false;
|
|
118
111
|
};
|
|
119
112
|
const validateCondition = (condition, request) => {
|
|
120
|
-
const evalResponse = (
|
|
113
|
+
const evalResponse = nodeEval(condition, 'condition.js', request);
|
|
121
114
|
if (typeof evalResponse === 'function') {
|
|
122
115
|
return evalResponse(request);
|
|
123
116
|
}
|
|
@@ -136,12 +129,12 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
136
129
|
ruleCondition = true;
|
|
137
130
|
}
|
|
138
131
|
// if there is a condition add this to filter
|
|
139
|
-
if (condition && !
|
|
132
|
+
if (condition && !_.isEmpty(condition)) {
|
|
140
133
|
condition = condition.replace(/\\n/g, '\n');
|
|
141
134
|
if (!reqResources) {
|
|
142
135
|
reqResources = [];
|
|
143
136
|
}
|
|
144
|
-
if (!
|
|
137
|
+
if (!_.isArray(reqResources)) {
|
|
145
138
|
reqResources = [reqResources];
|
|
146
139
|
}
|
|
147
140
|
const request = {
|
|
@@ -168,7 +161,7 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
168
161
|
ruleCondition = true;
|
|
169
162
|
filter.push({
|
|
170
163
|
field: 'id',
|
|
171
|
-
operation:
|
|
164
|
+
operation: Filter_Operation.eq,
|
|
172
165
|
value: filterId
|
|
173
166
|
});
|
|
174
167
|
}
|
|
@@ -177,7 +170,7 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
177
170
|
ruleCondition = true;
|
|
178
171
|
filter.push({
|
|
179
172
|
field: 'id',
|
|
180
|
-
operation:
|
|
173
|
+
operation: Filter_Operation.eq,
|
|
181
174
|
value: filterId
|
|
182
175
|
});
|
|
183
176
|
}
|
|
@@ -185,7 +178,7 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
185
178
|
}
|
|
186
179
|
else if (typeof filterId === 'object') { // prebuilt filter
|
|
187
180
|
// handle array
|
|
188
|
-
if (filterId.filters &&
|
|
181
|
+
if (filterId.filters && _.isArray(filterId.filters)) {
|
|
189
182
|
filter.push(...filterId.filters);
|
|
190
183
|
// map filter operator if its returned from condition
|
|
191
184
|
if (filterId?.operator) {
|
|
@@ -202,16 +195,16 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
202
195
|
}
|
|
203
196
|
}
|
|
204
197
|
catch (err) {
|
|
205
|
-
|
|
206
|
-
|
|
198
|
+
logger?.error('Error caught evaluating condition:', { condition });
|
|
199
|
+
logger?.error('Error', { code: err.code, message: err.message, stack: err.stack });
|
|
207
200
|
return;
|
|
208
201
|
}
|
|
209
202
|
}
|
|
210
203
|
const scopingAttribute = subjects?.find((attribute) => attribute.id == urns.roleScopingEntity);
|
|
211
|
-
if (!!scopingAttribute && effect ==
|
|
204
|
+
if (!!scopingAttribute && effect == Effect.PERMIT && database === 'arangoDB' && !ruleCondition) { // note: there is currently no query to exclude scopes
|
|
212
205
|
// userTotalScope is an array accumulated scopes for each rule
|
|
213
206
|
query['scope'] = {
|
|
214
|
-
custom_query:
|
|
207
|
+
custom_query: cfg.get('authorization:custom_query_name') ?? 'filterByOwnership',
|
|
215
208
|
custom_arguments: {
|
|
216
209
|
// value: Buffer.from(JSON.stringify({
|
|
217
210
|
entity: scopingAttribute.value,
|
|
@@ -220,9 +213,9 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
220
213
|
};
|
|
221
214
|
scopingUpdated = true;
|
|
222
215
|
}
|
|
223
|
-
else if (database && database === 'postgres' && effect ==
|
|
216
|
+
else if (database && database === 'postgres' && effect == Effect.PERMIT) {
|
|
224
217
|
query['filters'] = [];
|
|
225
|
-
const filterKeyMapArray =
|
|
218
|
+
const filterKeyMapArray = cfg?.get('authorization:filterParamKey');
|
|
226
219
|
let filterParamKey;
|
|
227
220
|
if (Array.isArray(filterKeyMapArray)) {
|
|
228
221
|
filterParamKey = filterKeyMapArray?.find((obj) => obj?.scopingEntity === scopingAttribute?.value)?.value;
|
|
@@ -231,7 +224,7 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
231
224
|
// default filter Paramkey for PostgresDB
|
|
232
225
|
filterParamKey = 'orgKey';
|
|
233
226
|
}
|
|
234
|
-
|
|
227
|
+
logger?.debug('Filter paramter key for Postgres DB', { filterParamKey });
|
|
235
228
|
for (const eachScope of userTotalScope) {
|
|
236
229
|
query['filters'].push({ field: filterParamKey, operation: 'eq', value: eachScope });
|
|
237
230
|
}
|
|
@@ -246,17 +239,17 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
246
239
|
if (resources?.length > 0) {
|
|
247
240
|
for (const attribute of resources) {
|
|
248
241
|
if (attribute.id == urns.resourceID) {
|
|
249
|
-
if (effect ==
|
|
242
|
+
if (effect == Effect.PERMIT) {
|
|
250
243
|
filter.push({
|
|
251
244
|
field: 'id',
|
|
252
|
-
operation:
|
|
245
|
+
operation: Filter_Operation.eq,
|
|
253
246
|
value: attribute.value
|
|
254
247
|
});
|
|
255
248
|
}
|
|
256
249
|
else {
|
|
257
250
|
filter.push({
|
|
258
251
|
field: 'id',
|
|
259
|
-
operation:
|
|
252
|
+
operation: Filter_Operation.neq,
|
|
260
253
|
value: attribute.value
|
|
261
254
|
});
|
|
262
255
|
}
|
|
@@ -269,12 +262,12 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
269
262
|
}
|
|
270
263
|
query['fields'].push({
|
|
271
264
|
name: attribute.value.split('#')[1],
|
|
272
|
-
include: effect ==
|
|
265
|
+
include: effect == Effect.PERMIT
|
|
273
266
|
});
|
|
274
267
|
}
|
|
275
268
|
}
|
|
276
269
|
}
|
|
277
|
-
const key = effect ==
|
|
270
|
+
const key = effect == Effect.PERMIT ? FilterOp_Operator.or : FilterOp_Operator.and;
|
|
278
271
|
if (query.filters) {
|
|
279
272
|
// query.filters = { filter: query['filter'] };
|
|
280
273
|
// and or operator comparision
|
|
@@ -285,7 +278,7 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
285
278
|
}
|
|
286
279
|
delete query['filter'];
|
|
287
280
|
}
|
|
288
|
-
else if (!
|
|
281
|
+
else if (!_.isEmpty(filter) || key == FilterOp_Operator.or) {
|
|
289
282
|
query['filters'] = filter;
|
|
290
283
|
// override the operator if its returned from rule condition
|
|
291
284
|
if (filterOperator) {
|
|
@@ -295,23 +288,23 @@ const buildQueryFromTarget = (target, effect, userTotalScope, urns, scopingUpdat
|
|
|
295
288
|
query.scopingUpdated = scopingUpdated;
|
|
296
289
|
return query;
|
|
297
290
|
};
|
|
298
|
-
const buildFilterPermissions = async (policySet, subject, reqResources, database) => {
|
|
291
|
+
export const buildFilterPermissions = async (policySet, subject, reqResources, database) => {
|
|
299
292
|
if (subject?.id) {
|
|
300
293
|
if (!subject.hierarchical_scopes?.length) {
|
|
301
|
-
subject.hierarchical_scopes = await
|
|
294
|
+
subject.hierarchical_scopes = await get(`cache:${subject.id}:${subject.token}:hrScopes`);
|
|
302
295
|
}
|
|
303
296
|
if (!subject.hierarchical_scopes?.length) {
|
|
304
|
-
subject.hierarchical_scopes = await
|
|
297
|
+
subject.hierarchical_scopes = await get(`cache:${subject.id}:hrScopes`);
|
|
305
298
|
}
|
|
306
299
|
if (!subject.role_associations?.length) {
|
|
307
|
-
subject.role_associations = await
|
|
300
|
+
subject.role_associations = await get(`cache:${subject.id}:subject`).then(subject => subject?.role_associations ?? []);
|
|
308
301
|
}
|
|
309
302
|
}
|
|
310
303
|
else {
|
|
311
304
|
subject.hierarchical_scopes ??= [];
|
|
312
305
|
subject.role_associations ??= [];
|
|
313
306
|
}
|
|
314
|
-
const urns =
|
|
307
|
+
const urns = cfg.get('authorization:urns');
|
|
315
308
|
const query = {
|
|
316
309
|
filters: []
|
|
317
310
|
};
|
|
@@ -326,22 +319,22 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
326
319
|
if (policy?.target?.subjects) {
|
|
327
320
|
const userSubjectMatched = checkSubjectMatch(subject, policy.target.subjects);
|
|
328
321
|
if (!userSubjectMatched) {
|
|
329
|
-
|
|
322
|
+
logger?.debug(`Skipping policy as policy subject and user subject don't match`);
|
|
330
323
|
continue;
|
|
331
324
|
}
|
|
332
325
|
}
|
|
333
326
|
let effect;
|
|
334
327
|
for (const rule of policy.rules || []) {
|
|
335
|
-
if (algorithm == urns.permitOverrides && rule.effect ==
|
|
336
|
-
effect =
|
|
328
|
+
if (algorithm == urns.permitOverrides && rule.effect == Effect.PERMIT) {
|
|
329
|
+
effect = Effect.PERMIT;
|
|
337
330
|
break;
|
|
338
331
|
}
|
|
339
|
-
else if (algorithm == urns.denyOverrides && rule.effect ==
|
|
340
|
-
effect =
|
|
332
|
+
else if (algorithm == urns.denyOverrides && rule.effect == Effect.DENY) {
|
|
333
|
+
effect = Effect.DENY;
|
|
341
334
|
}
|
|
342
335
|
}
|
|
343
336
|
if (effect === undefined) {
|
|
344
|
-
effect = algorithm == urns.permitOverrides ?
|
|
337
|
+
effect = algorithm == urns.permitOverrides ? Effect.DENY : Effect.PERMIT;
|
|
345
338
|
}
|
|
346
339
|
let scopingUpdated = false;
|
|
347
340
|
for (const rule of policy?.rules || []) {
|
|
@@ -349,21 +342,21 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
349
342
|
if (rule?.target?.subjects) {
|
|
350
343
|
const userSubjectMatched = checkSubjectMatch(subject, rule.target.subjects, reducedUserScope);
|
|
351
344
|
if (!userSubjectMatched) {
|
|
352
|
-
|
|
345
|
+
logger?.debug(`Skipping rule as user subject and rule subject don't match`);
|
|
353
346
|
continue;
|
|
354
347
|
}
|
|
355
348
|
}
|
|
356
349
|
const filterPermissions = buildQueryFromTarget(rule.target, rule.effect, reducedUserScope, urns, scopingUpdated, reqResources, rule.condition, subject, database);
|
|
357
|
-
if (!
|
|
350
|
+
if (!_.isEmpty(filterPermissions)) {
|
|
358
351
|
scopingUpdated = filterPermissions.scopingUpdated;
|
|
359
352
|
delete filterPermissions.scopingUpdated;
|
|
360
353
|
}
|
|
361
|
-
if (!
|
|
354
|
+
if (!_.isEmpty(filterPermissions)) {
|
|
362
355
|
policyFiltersArr.push(filterPermissions);
|
|
363
356
|
// if reducedUserScope is empty - no filters are applied further
|
|
364
357
|
// as this is a rule without scoping and should override the filters
|
|
365
358
|
// from other Rules which have scoping entity
|
|
366
|
-
if (
|
|
359
|
+
if (_.isEmpty(reducedUserScope) && rule.effect === effect) {
|
|
367
360
|
return { filters: [] };
|
|
368
361
|
}
|
|
369
362
|
}
|
|
@@ -375,17 +368,17 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
375
368
|
}
|
|
376
369
|
}
|
|
377
370
|
}
|
|
378
|
-
if (
|
|
371
|
+
if (_.isEmpty(policyEffects)) {
|
|
379
372
|
return null;
|
|
380
373
|
}
|
|
381
374
|
let applicable;
|
|
382
375
|
if (pSetAlgorithm == urns.permitOverrides) {
|
|
383
|
-
applicable =
|
|
376
|
+
applicable = _.includes(policyEffects, Effect.PERMIT) ? Effect.PERMIT : Effect.DENY;
|
|
384
377
|
}
|
|
385
378
|
else {
|
|
386
|
-
applicable =
|
|
379
|
+
applicable = _.includes(policyEffects, Effect.DENY) ? Effect.DENY : Effect.PERMIT;
|
|
387
380
|
}
|
|
388
|
-
const key = applicable ==
|
|
381
|
+
const key = applicable == Effect.PERMIT ? 'or' : 'and';
|
|
389
382
|
if (policyFiltersArr.length === 0) {
|
|
390
383
|
return undefined;
|
|
391
384
|
}
|
|
@@ -394,7 +387,7 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
394
387
|
let filterList = [];
|
|
395
388
|
// fix to override the AQL query filters with ACS policy filters if they exist for ArangoDB
|
|
396
389
|
// TODO remove this once the AQL filterByOwnership is removed and ACS policy filters are returned
|
|
397
|
-
if (policy?.scope && applicable ==
|
|
390
|
+
if (policy?.scope && applicable == Effect.PERMIT && !query['custom_query']) {
|
|
398
391
|
if (!query['custom_queries']) {
|
|
399
392
|
query['custom_queries'] = [];
|
|
400
393
|
}
|
|
@@ -435,7 +428,7 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
435
428
|
for (const filter of filterList) {
|
|
436
429
|
query.filters.push(filter);
|
|
437
430
|
}
|
|
438
|
-
if (
|
|
431
|
+
if (_.isArray(filterList) && filterList.length > 0) {
|
|
439
432
|
query.filters.operator = key;
|
|
440
433
|
// override the operator if its returned from rule condition
|
|
441
434
|
if (policy?.filters?.operator) {
|
|
@@ -454,7 +447,7 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
454
447
|
if (aqlQueryFilters && query?.filters?.length > 0) {
|
|
455
448
|
query.filters = [];
|
|
456
449
|
}
|
|
457
|
-
if (!
|
|
450
|
+
if (!_.isEmpty(query) && (!_.isNil(query.filters) || !_.isEmpty(query['fields']) || !_.isEmpty(query['custom_query']))) {
|
|
458
451
|
if (query['custom_arguments']) {
|
|
459
452
|
query['custom_arguments'] = { value: Buffer.from(JSON.stringify(query['custom_arguments'])) };
|
|
460
453
|
}
|
|
@@ -463,8 +456,7 @@ const buildFilterPermissions = async (policySet, subject, reqResources, database
|
|
|
463
456
|
}
|
|
464
457
|
return undefined;
|
|
465
458
|
};
|
|
466
|
-
|
|
467
|
-
const generateOperationStatus = (code, message) => {
|
|
459
|
+
export const generateOperationStatus = (code, message) => {
|
|
468
460
|
if (!code) {
|
|
469
461
|
code = 500; // Internal server error
|
|
470
462
|
}
|
|
@@ -473,7 +465,6 @@ const generateOperationStatus = (code, message) => {
|
|
|
473
465
|
message
|
|
474
466
|
};
|
|
475
467
|
};
|
|
476
|
-
exports.generateOperationStatus = generateOperationStatus;
|
|
477
468
|
/**
|
|
478
469
|
* Check if the attributes of a resources from a rule, policy
|
|
479
470
|
* or policy set match the attributes from a request.
|
|
@@ -481,7 +472,7 @@ exports.generateOperationStatus = generateOperationStatus;
|
|
|
481
472
|
* @param ruleAttributes
|
|
482
473
|
* @param requestAttributes
|
|
483
474
|
*/
|
|
484
|
-
const attributesMatch = (ruleAttributes, requestAttributes) => {
|
|
475
|
+
export const attributesMatch = (ruleAttributes, requestAttributes) => {
|
|
485
476
|
if (ruleAttributes?.length > 0) {
|
|
486
477
|
for (const attribute of ruleAttributes) {
|
|
487
478
|
const id = attribute.id;
|
|
@@ -538,7 +529,6 @@ const attributesMatch = (ruleAttributes, requestAttributes) => {
|
|
|
538
529
|
}
|
|
539
530
|
return true;
|
|
540
531
|
};
|
|
541
|
-
exports.attributesMatch = attributesMatch;
|
|
542
532
|
/**
|
|
543
533
|
* creates resource filters and custom query / arguments for the resource list provided
|
|
544
534
|
* It iterates through each resource and filter the applicable policies and
|
|
@@ -556,7 +546,7 @@ exports.attributesMatch = attributesMatch;
|
|
|
556
546
|
* if this param is missing defaults to `arangoDB`
|
|
557
547
|
*
|
|
558
548
|
*/
|
|
559
|
-
const createResourceFilterMap = async (resource, policySetResponse, resources, action, subject, subjectID, authzEnforced, targetScope, database) => {
|
|
549
|
+
export const createResourceFilterMap = async (resource, policySetResponse, resources, action, subject, subjectID, authzEnforced, targetScope, database) => {
|
|
560
550
|
const resourceFilterMap = [];
|
|
561
551
|
const customQueryArgs = [];
|
|
562
552
|
for (const resourceObj of resource) {
|
|
@@ -571,8 +561,8 @@ const createResourceFilterMap = async (resource, policySetResponse, resources, a
|
|
|
571
561
|
else {
|
|
572
562
|
resourceName = resourcenameNameSpace;
|
|
573
563
|
}
|
|
574
|
-
const resourceType =
|
|
575
|
-
const urns =
|
|
564
|
+
const resourceType = formatResourceType(resourceName, resourceNameSpace);
|
|
565
|
+
const urns = cfg.get('authorization:urns');
|
|
576
566
|
const resourceValueURN = urns?.model + `:${resourceType}`;
|
|
577
567
|
const resourcePolicies = { policy_sets: [{ policies: [] }] };
|
|
578
568
|
const resourceAttributes = [{ id: urns?.entity, value: resourceValueURN }];
|
|
@@ -584,10 +574,10 @@ const createResourceFilterMap = async (resource, policySetResponse, resources, a
|
|
|
584
574
|
for (const policy of policies) {
|
|
585
575
|
const policyTargetResources = policy?.target?.resources;
|
|
586
576
|
if (policyTargetResources) {
|
|
587
|
-
const policyMatch =
|
|
577
|
+
const policyMatch = attributesMatch(policyTargetResources, resourceAttributes);
|
|
588
578
|
if (policyMatch && policy?.rules?.length > 0) {
|
|
589
579
|
for (const rule of policy.rules) {
|
|
590
|
-
const ruleMatch =
|
|
580
|
+
const ruleMatch = attributesMatch(rule?.target?.resources, resourceAttributes);
|
|
591
581
|
if (ruleMatch) {
|
|
592
582
|
resourcePolicies.policy_sets[0].policies.push(policy);
|
|
593
583
|
break;
|
|
@@ -598,7 +588,7 @@ const createResourceFilterMap = async (resource, policySetResponse, resources, a
|
|
|
598
588
|
else if (policy?.rules) {
|
|
599
589
|
// check for rule
|
|
600
590
|
for (const rule of policy.rules) {
|
|
601
|
-
const ruleMatch =
|
|
591
|
+
const ruleMatch = attributesMatch(rule?.target?.resources, resourceAttributes);
|
|
602
592
|
if (ruleMatch) {
|
|
603
593
|
resourcePolicies.policy_sets[0].policies.push(policy);
|
|
604
594
|
break;
|
|
@@ -609,9 +599,9 @@ const createResourceFilterMap = async (resource, policySetResponse, resources, a
|
|
|
609
599
|
}
|
|
610
600
|
});
|
|
611
601
|
}
|
|
612
|
-
const permissionArguments = await
|
|
602
|
+
const permissionArguments = await buildFilterPermissions(resourcePolicies.policy_sets[0], subject, resources, database);
|
|
613
603
|
if (permissionArguments) {
|
|
614
|
-
if (!
|
|
604
|
+
if (!_.isArray(permissionArguments.filters)) {
|
|
615
605
|
permissionArguments.filters = [permissionArguments.filters];
|
|
616
606
|
}
|
|
617
607
|
resourceFilterMap.push({ resource: resourceName, filters: permissionArguments.filters });
|
|
@@ -627,27 +617,26 @@ const createResourceFilterMap = async (resource, policySetResponse, resources, a
|
|
|
627
617
|
const msg = [
|
|
628
618
|
`Access not allowed for request with subject:${subjectID},`,
|
|
629
619
|
`resource:${resourceName}, action:${action}, target_scope:${targetScope};`,
|
|
630
|
-
`the response was ${
|
|
620
|
+
`the response was ${Response_Decision.DENY}`
|
|
631
621
|
].join(' ');
|
|
632
622
|
const details = `Subject:${subjectID} does not have access to target scope ${targetScope}}`;
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return { decision:
|
|
623
|
+
logger?.verbose(msg);
|
|
624
|
+
logger?.verbose('Details:', { details });
|
|
625
|
+
return { decision: Response_Decision.DENY, operation_status: generateOperationStatus(Number(errors.ACTION_NOT_ALLOWED.code), msg) };
|
|
636
626
|
}
|
|
637
627
|
else {
|
|
638
|
-
|
|
639
|
-
`The Access response was ${
|
|
628
|
+
logger?.verbose([
|
|
629
|
+
`The Access response was ${Response_Decision.DENY} for a request from subject:${subjectID}`,
|
|
640
630
|
`resource:${resourceName}, action:${action}, target_scope:${targetScope}`,
|
|
641
631
|
`but since ACS enforcement config is disabled overriding the ACS result`,
|
|
642
632
|
].join(' '));
|
|
643
|
-
return { decision:
|
|
633
|
+
return { decision: Response_Decision.PERMIT, operation_status: { code: 200, message: 'success' } };
|
|
644
634
|
}
|
|
645
635
|
}
|
|
646
636
|
return {
|
|
647
637
|
resourceFilterMap, customQueryArgs
|
|
648
638
|
};
|
|
649
639
|
};
|
|
650
|
-
exports.createResourceFilterMap = createResourceFilterMap;
|
|
651
640
|
/**
|
|
652
641
|
* converts the Obligation Attribute[] to Obligation[] object
|
|
653
642
|
*
|
|
@@ -656,9 +645,9 @@ exports.createResourceFilterMap = createResourceFilterMap;
|
|
|
656
645
|
* to property[].
|
|
657
646
|
*
|
|
658
647
|
*/
|
|
659
|
-
const mapResourceURNObligationProperties = (obligations) => {
|
|
648
|
+
export const mapResourceURNObligationProperties = (obligations) => {
|
|
660
649
|
const mappedResourceObligation = [];
|
|
661
|
-
const urns =
|
|
650
|
+
const urns = cfg.get('authorization:urns');
|
|
662
651
|
if (obligations?.length > 0) {
|
|
663
652
|
for (const obligationObj of obligations) {
|
|
664
653
|
if (obligationObj?.id === urns.entity && obligationObj?.value) {
|
|
@@ -683,5 +672,4 @@ const mapResourceURNObligationProperties = (obligations) => {
|
|
|
683
672
|
}
|
|
684
673
|
return mappedResourceObligation;
|
|
685
674
|
};
|
|
686
|
-
exports.mapResourceURNObligationProperties = mapResourceURNObligationProperties;
|
|
687
675
|
//# sourceMappingURL=utils.js.map
|