@mondaydotcomorg/monday-authorization 3.3.0-feature-bashanye-navigate-can-action-in-scope-to-graph-9e68432 → 3.3.0-feature-bashanye-navigate-can-action-in-scope-to-graph-7ce3f8a
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/authorization-service.d.ts +0 -8
- package/dist/authorization-service.d.ts.map +1 -1
- package/dist/authorization-service.js +12 -160
- package/dist/clients/graph-api.client.d.ts +24 -0
- package/dist/clients/graph-api.client.d.ts.map +1 -0
- package/dist/clients/graph-api.client.js +102 -0
- package/dist/clients/platform-api.client.d.ts +31 -0
- package/dist/clients/platform-api.client.d.ts.map +1 -0
- package/dist/clients/platform-api.client.js +86 -0
- package/dist/esm/authorization-service.d.ts +0 -8
- package/dist/esm/authorization-service.d.ts.map +1 -1
- package/dist/esm/authorization-service.mjs +13 -155
- package/dist/esm/clients/graph-api.client.d.ts +24 -0
- package/dist/esm/clients/graph-api.client.d.ts.map +1 -0
- package/dist/esm/clients/graph-api.client.mjs +100 -0
- package/dist/esm/clients/platform-api.client.d.ts +31 -0
- package/dist/esm/clients/platform-api.client.d.ts.map +1 -0
- package/dist/esm/clients/platform-api.client.mjs +84 -0
- package/dist/esm/prometheus-service.d.ts +3 -3
- package/dist/esm/prometheus-service.d.ts.map +1 -1
- package/dist/esm/prometheus-service.mjs +65 -3
- package/dist/esm/types/graph-api.types.d.ts +11 -0
- package/dist/esm/types/graph-api.types.d.ts.map +1 -0
- package/dist/esm/types/graph-api.types.mjs +1 -0
- package/dist/esm/utils/authorization.utils.d.ts +22 -0
- package/dist/esm/utils/authorization.utils.d.ts.map +1 -0
- package/dist/esm/utils/authorization.utils.mjs +39 -0
- package/dist/prometheus-service.d.ts +3 -3
- package/dist/prometheus-service.d.ts.map +1 -1
- package/dist/prometheus-service.js +65 -3
- package/dist/types/graph-api.types.d.ts +11 -0
- package/dist/types/graph-api.types.d.ts.map +1 -0
- package/dist/types/graph-api.types.js +1 -0
- package/dist/utils/authorization.utils.d.ts +22 -0
- package/dist/utils/authorization.utils.d.ts.map +1 -0
- package/dist/utils/authorization.utils.js +49 -0
- package/package.json +1 -1
|
@@ -30,14 +30,6 @@ export declare class AuthorizationService {
|
|
|
30
30
|
static canActionInScope(accountId: number, userId: number, action: string, scope: ScopeOptions): Promise<ScopedActionPermit>;
|
|
31
31
|
private static getProfile;
|
|
32
32
|
static canActionInScopeMultiple(accountId: number, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
|
|
33
|
-
private static buildScopedActionsPayload;
|
|
34
|
-
private static scopeToResource;
|
|
35
|
-
private static buildGraphRequestBody;
|
|
36
|
-
private static fetchGraphIsAllowed;
|
|
37
|
-
private static mapGraphResponse;
|
|
38
|
-
private static fetchPlatformCanActions;
|
|
39
|
-
private static toCamelCase;
|
|
40
|
-
private static mapPlatformResponse;
|
|
41
33
|
private static isAuthorizedSingular;
|
|
42
34
|
private static isAuthorizedMultiple;
|
|
43
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7F,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAe1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAM9B,OAAO,CAAC,MAAM,CAAC,UAAU;WAsBZ,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBAuCnB,oBAAoB;mBAUpB,oBAAoB;CAoF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAepG"}
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const perf_hooks = require('perf_hooks');
|
|
4
|
-
const snakeCase = require('lodash/snakeCase.js');
|
|
5
|
-
const camelCase = require('lodash/camelCase.js');
|
|
6
|
-
const mapKeys = require('lodash/mapKeys.js');
|
|
7
4
|
const tridentBackendApi = require('@mondaydotcomorg/trident-backend-api');
|
|
8
5
|
const mondayFetchApi = require('@mondaydotcomorg/monday-fetch-api');
|
|
9
6
|
const igniteSdk = require('@mondaydotcomorg/ignite-sdk');
|
|
10
7
|
const prometheusService = require('./prometheus-service.js');
|
|
11
8
|
const authorizationInternalService = require('./authorization-internal-service.js');
|
|
12
9
|
const attributionsService = require('./attributions-service.js');
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const snakeCase__default = /*#__PURE__*/_interopDefault(snakeCase);
|
|
17
|
-
const camelCase__default = /*#__PURE__*/_interopDefault(camelCase);
|
|
18
|
-
const mapKeys__default = /*#__PURE__*/_interopDefault(mapKeys);
|
|
10
|
+
const clients_graphApi_client = require('./clients/graph-api.client.js');
|
|
11
|
+
const clients_platformApi_client = require('./clients/platform-api.client.js');
|
|
12
|
+
const utils_authorization_utils = require('./utils/authorization.utils.js');
|
|
19
13
|
|
|
20
14
|
const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
|
|
21
15
|
const PLATFORM_AUTHORIZE_PATH = '/internal_ms/authorization/authorize';
|
|
22
|
-
const PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH = '/internal_ms/authorization/can_actions_in_scopes';
|
|
23
|
-
const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
|
|
24
16
|
const ALLOWED_SDK_PLATFORM_PROFILES_KEY = 'allowed-sdk-platform-profiles';
|
|
25
17
|
const IN_RELEASE_SDK_PLATFORM_PROFILES_KEY = 'in-release-sdk-platform-profile';
|
|
26
18
|
const PLATFORM_PROFILE_RELEASE_FF = 'sdk-platform-profiles';
|
|
@@ -102,166 +94,26 @@ class AuthorizationService {
|
|
|
102
94
|
const startTime = perf_hooks.performance.now();
|
|
103
95
|
let scopedActionResponseObjects;
|
|
104
96
|
if (shouldNavigateToGraph) {
|
|
105
|
-
|
|
106
|
-
scopedActionResponseObjects = this.mapGraphResponse(scopedActions, userId, response);
|
|
97
|
+
scopedActionResponseObjects = await clients_graphApi_client.GraphApiClient.checkPermissions(internalAuthToken, scopedActions);
|
|
107
98
|
}
|
|
108
99
|
else {
|
|
109
100
|
const profile = this.getProfile(accountId, userId);
|
|
110
|
-
|
|
111
|
-
const platformResponse = await this.fetchPlatformCanActions(profile, internalAuthToken, userId, scopedActionsPayload);
|
|
112
|
-
scopedActionResponseObjects = this.mapPlatformResponse(platformResponse);
|
|
101
|
+
scopedActionResponseObjects = await clients_platformApi_client.PlatformApiClient.checkPermissions(profile, internalAuthToken, userId, scopedActions);
|
|
113
102
|
}
|
|
114
103
|
const endTime = perf_hooks.performance.now();
|
|
115
104
|
const time = endTime - startTime;
|
|
116
105
|
const apiType = shouldNavigateToGraph ? 'graph' : 'platform';
|
|
106
|
+
// Record metrics for each authorization check
|
|
117
107
|
for (const obj of scopedActionResponseObjects) {
|
|
118
|
-
const { action } = obj.scopedAction;
|
|
119
|
-
const
|
|
108
|
+
const { action, scope } = obj.scopedAction;
|
|
109
|
+
const { resourceType } = utils_authorization_utils.scopeToResource(scope);
|
|
120
110
|
const isAuthorized = obj.permit.can;
|
|
121
|
-
prometheusService.sendAuthorizationCheckResponseTimeMetric(
|
|
122
|
-
if (obj.permit.can)
|
|
123
|
-
|
|
124
|
-
return scopedActionResponseObjects;
|
|
125
|
-
}
|
|
126
|
-
static buildScopedActionsPayload(scopedActions) {
|
|
127
|
-
return scopedActions.map(scopedAction => {
|
|
128
|
-
return { ...scopedAction, scope: mapKeys__default.default(scopedAction.scope, (_, key) => snakeCase__default.default(key)) };
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
static scopeToResource(scope) {
|
|
132
|
-
if ('workspaceId' in scope) {
|
|
133
|
-
return { resourceType: 'workspace', resourceId: scope.workspaceId };
|
|
134
|
-
}
|
|
135
|
-
if ('boardId' in scope) {
|
|
136
|
-
return { resourceType: 'board', resourceId: scope.boardId };
|
|
137
|
-
}
|
|
138
|
-
if ('pulseId' in scope) {
|
|
139
|
-
return { resourceType: 'pulse', resourceId: scope.pulseId };
|
|
140
|
-
}
|
|
141
|
-
if ('accountProductId' in scope) {
|
|
142
|
-
return { resourceType: 'account_product', resourceId: scope.accountProductId };
|
|
143
|
-
}
|
|
144
|
-
if ('accountId' in scope) {
|
|
145
|
-
return { resourceType: 'account', resourceId: scope.accountId };
|
|
146
|
-
}
|
|
147
|
-
throw new Error('Unsupported scope provided');
|
|
148
|
-
}
|
|
149
|
-
static buildGraphRequestBody(scopedActions) {
|
|
150
|
-
const resourcesAccumulator = {};
|
|
151
|
-
for (const { action, scope } of scopedActions) {
|
|
152
|
-
const { resourceType, resourceId } = this.scopeToResource(scope);
|
|
153
|
-
if (!resourcesAccumulator[resourceType]) {
|
|
154
|
-
resourcesAccumulator[resourceType] = {};
|
|
155
|
-
}
|
|
156
|
-
if (!resourcesAccumulator[resourceType][resourceId]) {
|
|
157
|
-
resourcesAccumulator[resourceType][resourceId] = new Set();
|
|
158
|
-
}
|
|
159
|
-
resourcesAccumulator[resourceType][resourceId].add(action);
|
|
160
|
-
}
|
|
161
|
-
const resourcesPayload = {};
|
|
162
|
-
for (const [resourceType, idMap] of Object.entries(resourcesAccumulator)) {
|
|
163
|
-
resourcesPayload[resourceType] = {};
|
|
164
|
-
for (const [idStr, actionsSet] of Object.entries(idMap)) {
|
|
165
|
-
const idNum = Number(idStr);
|
|
166
|
-
resourcesPayload[resourceType][idNum] = Array.from(actionsSet);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return resourcesPayload;
|
|
170
|
-
}
|
|
171
|
-
static async fetchGraphIsAllowed(internalAuthToken, scopedActions) {
|
|
172
|
-
const httpClient = tridentBackendApi.Api.getPart('httpClient');
|
|
173
|
-
const attributionHeaders = attributionsService.getAttributionsFromApi();
|
|
174
|
-
const bodyPayload = this.buildGraphRequestBody(scopedActions);
|
|
175
|
-
try {
|
|
176
|
-
const response = await httpClient.fetch({
|
|
177
|
-
url: {
|
|
178
|
-
appName: 'authorization-graph',
|
|
179
|
-
path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
|
|
180
|
-
},
|
|
181
|
-
method: 'POST',
|
|
182
|
-
headers: {
|
|
183
|
-
Authorization: internalAuthToken,
|
|
184
|
-
'Content-Type': 'application/json',
|
|
185
|
-
...attributionHeaders,
|
|
186
|
-
},
|
|
187
|
-
body: JSON.stringify(bodyPayload),
|
|
188
|
-
}, {
|
|
189
|
-
timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
|
|
190
|
-
retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
|
|
191
|
-
});
|
|
192
|
-
prometheusService.setGraphAvailability(true);
|
|
193
|
-
return response;
|
|
194
|
-
}
|
|
195
|
-
catch (err) {
|
|
196
|
-
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
197
|
-
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
198
|
-
prometheusService.incrementAuthorizationError(this.scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status);
|
|
199
|
-
}
|
|
200
|
-
throw err;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
static mapGraphResponse(scopedActions, userId, graphResponse) {
|
|
204
|
-
const resources = graphResponse ?? {};
|
|
205
|
-
return scopedActions.map(scopedAction => {
|
|
206
|
-
const { action, scope } = scopedAction;
|
|
207
|
-
const { resourceType, resourceId } = this.scopeToResource(scope);
|
|
208
|
-
const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
|
|
209
|
-
const permit = {
|
|
210
|
-
can: permissionResult?.can ?? false,
|
|
211
|
-
reason: { key: permissionResult?.reason ?? 'unknown' },
|
|
212
|
-
technicalReason: 0,
|
|
213
|
-
};
|
|
214
|
-
return { scopedAction, permit };
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
static async fetchPlatformCanActions(profile, internalAuthToken, userId, scopedActionsPayload) {
|
|
218
|
-
const attributionHeaders = attributionsService.getAttributionsFromApi();
|
|
219
|
-
const httpClient = tridentBackendApi.Api.getPart('httpClient');
|
|
220
|
-
try {
|
|
221
|
-
const response = await httpClient.fetch({
|
|
222
|
-
url: {
|
|
223
|
-
appName: 'platform',
|
|
224
|
-
path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
|
|
225
|
-
profile,
|
|
226
|
-
},
|
|
227
|
-
method: 'POST',
|
|
228
|
-
headers: {
|
|
229
|
-
Authorization: internalAuthToken,
|
|
230
|
-
'Content-Type': 'application/json',
|
|
231
|
-
...attributionHeaders,
|
|
232
|
-
},
|
|
233
|
-
body: JSON.stringify({ user_id: userId, scoped_actions: scopedActionsPayload }),
|
|
234
|
-
}, {
|
|
235
|
-
timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
|
|
236
|
-
retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
|
|
237
|
-
});
|
|
238
|
-
return response;
|
|
239
|
-
}
|
|
240
|
-
catch (err) {
|
|
241
|
-
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
242
|
-
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
243
|
-
prometheusService.incrementAuthorizationError(this.scopeToResource(this.toCamelCase(scopedActionsPayload[0].scope)).resourceType, scopedActionsPayload[0].action, err.status);
|
|
111
|
+
prometheusService.sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, 200, time, apiType);
|
|
112
|
+
if (obj.permit.can) {
|
|
113
|
+
prometheusService.incrementAuthorizationSuccess(resourceType, action);
|
|
244
114
|
}
|
|
245
|
-
throw err;
|
|
246
115
|
}
|
|
247
|
-
|
|
248
|
-
static toCamelCase(obj) {
|
|
249
|
-
return mapKeys__default.default(obj, (_, key) => camelCase__default.default(key));
|
|
250
|
-
}
|
|
251
|
-
static mapPlatformResponse(response) {
|
|
252
|
-
if (!response) {
|
|
253
|
-
authorizationInternalService.logger.error({ tag: 'authorization-service', response }, 'AuthorizationService: missing response');
|
|
254
|
-
throw new Error('AuthorizationService: missing response');
|
|
255
|
-
}
|
|
256
|
-
return response.result.map(responseObject => {
|
|
257
|
-
const { scopedAction, permit } = responseObject;
|
|
258
|
-
const { scope } = scopedAction;
|
|
259
|
-
return {
|
|
260
|
-
...responseObject,
|
|
261
|
-
scopedAction: { ...scopedAction, scope: this.toCamelCase(scope) },
|
|
262
|
-
permit: this.toCamelCase(permit),
|
|
263
|
-
};
|
|
264
|
-
});
|
|
116
|
+
return scopedActionResponseObjects;
|
|
265
117
|
}
|
|
266
118
|
static async isAuthorizedSingular(accountId, userId, resources, action) {
|
|
267
119
|
const { authorizationObjects } = createAuthorizationParams(resources, action);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
|
|
2
|
+
import { GraphIsAllowedDto, GraphIsAllowedResponse } from '../types/graph-api.types';
|
|
3
|
+
/**
|
|
4
|
+
* Client for handling Graph API authorization operations
|
|
5
|
+
*/
|
|
6
|
+
export declare class GraphApiClient {
|
|
7
|
+
/**
|
|
8
|
+
* Builds the request body for Graph API calls
|
|
9
|
+
*/
|
|
10
|
+
static buildRequestBody(scopedActions: ScopedAction[]): GraphIsAllowedDto;
|
|
11
|
+
/**
|
|
12
|
+
* Fetches authorization data from the Graph API
|
|
13
|
+
*/
|
|
14
|
+
static fetchPermissions(internalAuthToken: string, scopedActions: ScopedAction[]): Promise<GraphIsAllowedResponse>;
|
|
15
|
+
/**
|
|
16
|
+
* Maps Graph API response to the expected format
|
|
17
|
+
*/
|
|
18
|
+
static mapResponse(scopedActions: ScopedAction[], graphResponse: GraphIsAllowedResponse): ScopedActionResponseObject[];
|
|
19
|
+
/**
|
|
20
|
+
* Performs a complete authorization check using the Graph API
|
|
21
|
+
*/
|
|
22
|
+
static checkPermissions(internalAuthToken: string, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=graph-api.client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-api.client.d.ts","sourceRoot":"","sources":["../../src/clients/graph-api.client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAsB,MAAM,mCAAmC,CAAC;AAGjH,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EAIvB,MAAM,0BAA0B,CAAC;AAMlC;;GAEG;AACH,qBAAa,cAAc;IACzB;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,iBAAiB;IAyBzE;;OAEG;WACU,gBAAgB,CAC3B,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,sBAAsB,CAAC;IAyClC;;OAEG;IACH,MAAM,CAAC,WAAW,CAChB,aAAa,EAAE,YAAY,EAAE,EAC7B,aAAa,EAAE,sBAAsB,GACpC,0BAA0B,EAAE;IAkB/B;;OAEG;WACU,gBAAgB,CAC3B,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;CAIzC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const tridentBackendApi = require('@mondaydotcomorg/trident-backend-api');
|
|
4
|
+
const mondayFetchApi = require('@mondaydotcomorg/monday-fetch-api');
|
|
5
|
+
const authorizationInternalService = require('../authorization-internal-service.js');
|
|
6
|
+
const attributionsService = require('../attributions-service.js');
|
|
7
|
+
const utils_authorization_utils = require('../utils/authorization.utils.js');
|
|
8
|
+
const prometheusService = require('../prometheus-service.js');
|
|
9
|
+
|
|
10
|
+
const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
|
|
11
|
+
/**
|
|
12
|
+
* Client for handling Graph API authorization operations
|
|
13
|
+
*/
|
|
14
|
+
class GraphApiClient {
|
|
15
|
+
/**
|
|
16
|
+
* Builds the request body for Graph API calls
|
|
17
|
+
*/
|
|
18
|
+
static buildRequestBody(scopedActions) {
|
|
19
|
+
const resourcesAccumulator = {};
|
|
20
|
+
for (const { action, scope } of scopedActions) {
|
|
21
|
+
const { resourceType, resourceId } = utils_authorization_utils.scopeToResource(scope);
|
|
22
|
+
if (!resourcesAccumulator[resourceType]) {
|
|
23
|
+
resourcesAccumulator[resourceType] = {};
|
|
24
|
+
}
|
|
25
|
+
if (!resourcesAccumulator[resourceType][resourceId]) {
|
|
26
|
+
resourcesAccumulator[resourceType][resourceId] = new Set();
|
|
27
|
+
}
|
|
28
|
+
resourcesAccumulator[resourceType][resourceId].add(action);
|
|
29
|
+
}
|
|
30
|
+
const resourcesPayload = {};
|
|
31
|
+
for (const [resourceType, idMap] of Object.entries(resourcesAccumulator)) {
|
|
32
|
+
resourcesPayload[resourceType] = {};
|
|
33
|
+
for (const [idStr, actionsSet] of Object.entries(idMap)) {
|
|
34
|
+
const idNum = Number(idStr);
|
|
35
|
+
resourcesPayload[resourceType][idNum] = Array.from(actionsSet);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return resourcesPayload;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fetches authorization data from the Graph API
|
|
42
|
+
*/
|
|
43
|
+
static async fetchPermissions(internalAuthToken, scopedActions) {
|
|
44
|
+
const httpClient = tridentBackendApi.Api.getPart('httpClient');
|
|
45
|
+
const attributionHeaders = attributionsService.getAttributionsFromApi();
|
|
46
|
+
const bodyPayload = this.buildRequestBody(scopedActions);
|
|
47
|
+
try {
|
|
48
|
+
const response = await httpClient.fetch({
|
|
49
|
+
url: {
|
|
50
|
+
appName: 'authorization-graph',
|
|
51
|
+
path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
|
|
52
|
+
},
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: internalAuthToken,
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
...attributionHeaders,
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(bodyPayload),
|
|
60
|
+
}, {
|
|
61
|
+
timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
|
|
62
|
+
retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
|
|
63
|
+
});
|
|
64
|
+
prometheusService.setGraphAvailability(true);
|
|
65
|
+
return response;
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
prometheusService.setGraphAvailability(false);
|
|
69
|
+
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
70
|
+
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
71
|
+
prometheusService.incrementAuthorizationError(utils_authorization_utils.scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status);
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Maps Graph API response to the expected format
|
|
78
|
+
*/
|
|
79
|
+
static mapResponse(scopedActions, graphResponse) {
|
|
80
|
+
const resources = graphResponse ?? {};
|
|
81
|
+
return scopedActions.map(scopedAction => {
|
|
82
|
+
const { action, scope } = scopedAction;
|
|
83
|
+
const { resourceType, resourceId } = utils_authorization_utils.scopeToResource(scope);
|
|
84
|
+
const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
|
|
85
|
+
const permit = {
|
|
86
|
+
can: permissionResult?.can ?? false,
|
|
87
|
+
reason: { key: permissionResult?.reason ?? 'unknown' },
|
|
88
|
+
technicalReason: 0,
|
|
89
|
+
};
|
|
90
|
+
return { scopedAction, permit };
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Performs a complete authorization check using the Graph API
|
|
95
|
+
*/
|
|
96
|
+
static async checkPermissions(internalAuthToken, scopedActions) {
|
|
97
|
+
const response = await this.fetchPermissions(internalAuthToken, scopedActions);
|
|
98
|
+
return this.mapResponse(scopedActions, response);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
exports.GraphApiClient = GraphApiClient;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
|
|
2
|
+
import { PlatformProfile } from '../attributions-service';
|
|
3
|
+
type ScopedActionPlatformPayload = Omit<ScopedAction, 'scope'> & {
|
|
4
|
+
scope: Record<string, number>;
|
|
5
|
+
};
|
|
6
|
+
interface CanActionsInScopesResponse {
|
|
7
|
+
result: ScopedActionResponseObject[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Client for handling Platform API authorization operations
|
|
11
|
+
*/
|
|
12
|
+
export declare class PlatformApiClient {
|
|
13
|
+
/**
|
|
14
|
+
* Builds the request payload for Platform API calls
|
|
15
|
+
*/
|
|
16
|
+
static buildRequestPayload(scopedActions: ScopedAction[]): ScopedActionPlatformPayload[];
|
|
17
|
+
/**
|
|
18
|
+
* Fetches authorization data from the Platform API
|
|
19
|
+
*/
|
|
20
|
+
static fetchPermissions(profile: PlatformProfile, internalAuthToken: string, userId: number, scopedActionsPayload: ScopedActionPlatformPayload[]): Promise<CanActionsInScopesResponse>;
|
|
21
|
+
/**
|
|
22
|
+
* Maps Platform API response to the expected format
|
|
23
|
+
*/
|
|
24
|
+
static mapResponse(response: CanActionsInScopesResponse): ScopedActionResponseObject[];
|
|
25
|
+
/**
|
|
26
|
+
* Performs a complete authorization check using the Platform API
|
|
27
|
+
*/
|
|
28
|
+
static checkPermissions(profile: PlatformProfile, internalAuthToken: string, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=platform-api.client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-api.client.d.ts","sourceRoot":"","sources":["../../src/clients/platform-api.client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAE7F,OAAO,EAA0B,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAOlF,KAAK,2BAA2B,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG;IAC/D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF,UAAU,0BAA0B;IAClC,MAAM,EAAE,0BAA0B,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC5B;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,2BAA2B,EAAE;IAOxF;;OAEG;WACU,gBAAgB,CAC3B,OAAO,EAAE,eAAe,EACxB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EACd,oBAAoB,EAAE,2BAA2B,EAAE,GAClD,OAAO,CAAC,0BAA0B,CAAC;IAuCtC;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,0BAA0B,GAAG,0BAA0B,EAAE;IAkBtF;;OAEG;WACU,gBAAgB,CAC3B,OAAO,EAAE,eAAe,EACxB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;CAKzC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const tridentBackendApi = require('@mondaydotcomorg/trident-backend-api');
|
|
4
|
+
const mondayFetchApi = require('@mondaydotcomorg/monday-fetch-api');
|
|
5
|
+
const authorizationInternalService = require('../authorization-internal-service.js');
|
|
6
|
+
const attributionsService = require('../attributions-service.js');
|
|
7
|
+
const utils_authorization_utils = require('../utils/authorization.utils.js');
|
|
8
|
+
const prometheusService = require('../prometheus-service.js');
|
|
9
|
+
|
|
10
|
+
const PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH = '/internal_ms/authorization/can_actions_in_scopes';
|
|
11
|
+
/**
|
|
12
|
+
* Client for handling Platform API authorization operations
|
|
13
|
+
*/
|
|
14
|
+
class PlatformApiClient {
|
|
15
|
+
/**
|
|
16
|
+
* Builds the request payload for Platform API calls
|
|
17
|
+
*/
|
|
18
|
+
static buildRequestPayload(scopedActions) {
|
|
19
|
+
return scopedActions.map(scopedAction => ({
|
|
20
|
+
...scopedAction,
|
|
21
|
+
scope: utils_authorization_utils.toSnakeCase(scopedAction.scope),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fetches authorization data from the Platform API
|
|
26
|
+
*/
|
|
27
|
+
static async fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload) {
|
|
28
|
+
const attributionHeaders = attributionsService.getAttributionsFromApi();
|
|
29
|
+
const httpClient = tridentBackendApi.Api.getPart('httpClient');
|
|
30
|
+
try {
|
|
31
|
+
const response = await httpClient.fetch({
|
|
32
|
+
url: {
|
|
33
|
+
appName: 'platform',
|
|
34
|
+
path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
|
|
35
|
+
profile,
|
|
36
|
+
},
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
Authorization: internalAuthToken,
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
...attributionHeaders,
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ user_id: userId, scoped_actions: scopedActionsPayload }),
|
|
44
|
+
}, {
|
|
45
|
+
timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
|
|
46
|
+
retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
|
|
47
|
+
});
|
|
48
|
+
return response;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
52
|
+
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
53
|
+
prometheusService.incrementAuthorizationError(utils_authorization_utils.scopeToResource(utils_authorization_utils.toCamelCase(scopedActionsPayload[0].scope)).resourceType, scopedActionsPayload[0].action, err.status);
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Maps Platform API response to the expected format
|
|
60
|
+
*/
|
|
61
|
+
static mapResponse(response) {
|
|
62
|
+
if (!response) {
|
|
63
|
+
authorizationInternalService.logger.error({ tag: 'platform-api-client', response }, 'PlatformApiClient: missing response');
|
|
64
|
+
throw new Error('PlatformApiClient: missing response');
|
|
65
|
+
}
|
|
66
|
+
return response.result.map(responseObject => {
|
|
67
|
+
const { scopedAction, permit } = responseObject;
|
|
68
|
+
const { scope } = scopedAction;
|
|
69
|
+
return {
|
|
70
|
+
...responseObject,
|
|
71
|
+
scopedAction: { ...scopedAction, scope: utils_authorization_utils.toCamelCase(scope) },
|
|
72
|
+
permit: utils_authorization_utils.toCamelCase(permit),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Performs a complete authorization check using the Platform API
|
|
78
|
+
*/
|
|
79
|
+
static async checkPermissions(profile, internalAuthToken, userId, scopedActions) {
|
|
80
|
+
const scopedActionsPayload = this.buildRequestPayload(scopedActions);
|
|
81
|
+
const platformResponse = await this.fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload);
|
|
82
|
+
return this.mapResponse(platformResponse);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
exports.PlatformApiClient = PlatformApiClient;
|
|
@@ -30,14 +30,6 @@ export declare class AuthorizationService {
|
|
|
30
30
|
static canActionInScope(accountId: number, userId: number, action: string, scope: ScopeOptions): Promise<ScopedActionPermit>;
|
|
31
31
|
private static getProfile;
|
|
32
32
|
static canActionInScopeMultiple(accountId: number, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
|
|
33
|
-
private static buildScopedActionsPayload;
|
|
34
|
-
private static scopeToResource;
|
|
35
|
-
private static buildGraphRequestBody;
|
|
36
|
-
private static fetchGraphIsAllowed;
|
|
37
|
-
private static mapGraphResponse;
|
|
38
|
-
private static fetchPlatformCanActions;
|
|
39
|
-
private static toCamelCase;
|
|
40
|
-
private static mapPlatformResponse;
|
|
41
33
|
private static isAuthorizedSingular;
|
|
42
34
|
private static isAuthorizedMultiple;
|
|
43
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../../src/authorization-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7F,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAe1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAM9B,OAAO,CAAC,MAAM,CAAC,UAAU;WAsBZ,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBAuCnB,oBAAoB;mBAUpB,oBAAoB;CAoF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAepG"}
|