@mondaydotcomorg/monday-authorization 3.2.3 → 3.3.0-feat-add-graph-api-routing-support-cb899c0

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.
Files changed (40) hide show
  1. package/README.md +29 -0
  2. package/dist/authorization-service.d.ts.map +1 -1
  3. package/dist/authorization-service.js +43 -61
  4. package/dist/clients/graph-api.client.d.ts +24 -0
  5. package/dist/clients/graph-api.client.d.ts.map +1 -0
  6. package/dist/clients/graph-api.client.js +123 -0
  7. package/dist/clients/platform-api.client.d.ts +31 -0
  8. package/dist/clients/platform-api.client.d.ts.map +1 -0
  9. package/dist/clients/platform-api.client.js +89 -0
  10. package/dist/esm/authorization-service.d.ts.map +1 -1
  11. package/dist/esm/authorization-service.mjs +44 -56
  12. package/dist/esm/clients/graph-api.client.d.ts +24 -0
  13. package/dist/esm/clients/graph-api.client.d.ts.map +1 -0
  14. package/dist/esm/clients/graph-api.client.mjs +121 -0
  15. package/dist/esm/clients/platform-api.client.d.ts +31 -0
  16. package/dist/esm/clients/platform-api.client.d.ts.map +1 -0
  17. package/dist/esm/clients/platform-api.client.mjs +87 -0
  18. package/dist/esm/prometheus-service.d.ts +3 -1
  19. package/dist/esm/prometheus-service.d.ts.map +1 -1
  20. package/dist/esm/prometheus-service.mjs +61 -5
  21. package/dist/esm/testKit/index.d.ts.map +1 -1
  22. package/dist/esm/testKit/index.mjs +9 -7
  23. package/dist/esm/types/graph-api.types.d.ts +15 -0
  24. package/dist/esm/types/graph-api.types.d.ts.map +1 -0
  25. package/dist/esm/types/graph-api.types.mjs +1 -0
  26. package/dist/esm/utils/authorization.utils.d.ts +22 -0
  27. package/dist/esm/utils/authorization.utils.d.ts.map +1 -0
  28. package/dist/esm/utils/authorization.utils.mjs +39 -0
  29. package/dist/prometheus-service.d.ts +3 -1
  30. package/dist/prometheus-service.d.ts.map +1 -1
  31. package/dist/prometheus-service.js +62 -4
  32. package/dist/testKit/index.d.ts.map +1 -1
  33. package/dist/testKit/index.js +9 -7
  34. package/dist/types/graph-api.types.d.ts +15 -0
  35. package/dist/types/graph-api.types.d.ts.map +1 -0
  36. package/dist/types/graph-api.types.js +1 -0
  37. package/dist/utils/authorization.utils.d.ts +22 -0
  38. package/dist/utils/authorization.utils.d.ts.map +1 -0
  39. package/dist/utils/authorization.utils.js +49 -0
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -137,6 +137,18 @@ const canActionInScopeMultipleResponse: ScopedActionResponseObject[] =
137
137
  * /
138
138
  ```
139
139
 
140
+ **Graph API Routing (v3.3.0+):**
141
+
142
+ Starting from version 3.3.0, `canActionInScope` and `canActionInScopeMultiple` can route authorization checks to the Graph API (`authorization-graph` service) instead of the Platform API. This routing is controlled by the Ignite feature flag `navigate-can-action-in-scope-to-graph`.
143
+
144
+ - **Feature Flag**: `navigate-can-action-in-scope-to-graph`
145
+ - **Default Behavior**: When the feature flag is disabled (default), the SDK routes to Platform API (backward compatible)
146
+ - **Graph Routing**: When enabled, authorization checks are routed to the Graph API endpoint `/permissions/is-allowed`
147
+ - **Automatic Fallback**: The SDK automatically falls back to Platform API if the feature flag is disabled
148
+ - **No Code Changes Required**: The routing is transparent - your code doesn't need to change. Simply upgrade to v3.3.0+ and the feature flag controls the routing behavior
149
+
150
+ The Graph API provides the same authorization results with improved performance and scalability. The feature flag allows for gradual rollout and easy rollback if needed.
151
+
140
152
  ### Authorization Attributes API
141
153
 
142
154
  Authorization attributes have 2 options to get called: sync (http request) and async (send to SNS and consumed asynchronously).
@@ -244,6 +256,7 @@ const rolesResponse = await rolesService.getRoles(accountId, resourceTypes, styl
244
256
  ```
245
257
 
246
258
  **Parameters:**
259
+
247
260
  - `accountId` - The account ID
248
261
  - `resourceTypes` - Array of resource types to filter roles by (e.g., ['account', 'workspace'])
249
262
  - `style` - Deprecated, don't use it. the style of the roles to return, either 'A' or 'B' (default is 'A'). Note that basic role IDs are returned in A style and not B style.
@@ -273,6 +286,7 @@ const rolesResponse = await rolesService.createCustomRole(accountId, customRoles
273
286
  ```
274
287
 
275
288
  **Parameters:**
289
+
276
290
  - `accountId` - The account ID
277
291
  - `roles` - Array of `RoleCreateRequest` objects (cannot be empty)
278
292
 
@@ -298,6 +312,7 @@ const rolesResponse = await rolesService.updateCustomRole(accountId, updateReque
298
312
  ```
299
313
 
300
314
  **Parameters:**
315
+
301
316
  - `accountId` - The account ID
302
317
  - `updateRequests` - Array of `RoleUpdateRequest` objects
303
318
 
@@ -316,6 +331,7 @@ const rolesResponse = await rolesService.deleteCustomRole(accountId, roleIds);
316
331
  ```
317
332
 
318
333
  **Parameters:**
334
+
319
335
  - `accountId` - The account ID
320
336
  - `roleIds` - Array of custom role IDs to delete
321
337
 
@@ -376,3 +392,16 @@ interface RolesResponse {
376
392
  basicRoles?: BasicRole[];
377
393
  }
378
394
  ```
395
+
396
+ ## Development
397
+
398
+ ### Local Development and Testing
399
+
400
+ This package includes an `ignite-local-overrides.json` file for local development and testing only. It does **not** affect consumers of this package - they use their own Ignite configuration.
401
+
402
+ The file enables feature flags for testing:
403
+
404
+ - `sdk-platform-profiles`: Platform profile routing
405
+ - `navigate-can-action-in-scope-to-graph`: Graph API routing for `canActionInScope` methods
406
+
407
+ Modify this file for different local test scenarios, but remember changes only affect this package's development/testing.
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"AAIA,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;AAY1C,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;AAeD,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;mBAkEnB,oBAAoB;mBAUpB,oBAAoB;CAmF1C;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
+ {"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;mBA6DnB,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,28 +1,22 @@
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 _interopDefault = e => e && e.__esModule ? e : { default: e };
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
16
  const ALLOWED_SDK_PLATFORM_PROFILES_KEY = 'allowed-sdk-platform-profiles';
24
17
  const IN_RELEASE_SDK_PLATFORM_PROFILES_KEY = 'in-release-sdk-platform-profile';
25
18
  const PLATFORM_PROFILE_RELEASE_FF = 'sdk-platform-profiles';
19
+ const NAVIGATE_CAN_ACTION_IN_SCOPE_TO_GRAPH_FF = 'navigate-can-action-in-scope-to-graph';
26
20
  function setRequestFetchOptions(customMondayFetchOptions) {
27
21
  authorizationInternalService.AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
28
22
  }
@@ -95,61 +89,49 @@ class AuthorizationService {
95
89
  return attributionsService.PlatformProfile.INTERNAL;
96
90
  }
97
91
  static async canActionInScopeMultiple(accountId, userId, scopedActions) {
98
- const profile = this.getProfile(accountId, userId);
99
- const internalAuthToken = authorizationInternalService.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
100
- const scopedActionsPayload = scopedActions.map(scopedAction => {
101
- return { ...scopedAction, scope: mapKeys__default.default(scopedAction.scope, (_, key) => snakeCase__default.default(key)) }; // for example: { workspaceId: 1 } => { workspace_id: 1 }
102
- });
103
- const attributionHeaders = attributionsService.getAttributionsFromApi();
104
- const httpClient = tridentBackendApi.Api.getPart('httpClient');
105
- let response;
106
- try {
107
- response = await httpClient.fetch({
108
- url: {
109
- appName: 'platform',
110
- path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
111
- profile,
112
- },
113
- method: 'POST',
114
- headers: {
115
- Authorization: internalAuthToken,
116
- 'Content-Type': 'application/json',
117
- ...attributionHeaders,
118
- },
119
- body: JSON.stringify({
120
- user_id: userId,
121
- scoped_actions: scopedActionsPayload,
122
- }),
123
- }, {
124
- timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
125
- retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
126
- });
92
+ if (scopedActions.length === 0) {
93
+ return [];
127
94
  }
128
- catch (err) {
129
- if (err instanceof mondayFetchApi.HttpFetcherError) {
130
- authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
95
+ const shouldNavigateToGraph = Boolean(this.igniteClient?.isReleased(NAVIGATE_CAN_ACTION_IN_SCOPE_TO_GRAPH_FF, { accountId, userId }));
96
+ const internalAuthToken = authorizationInternalService.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
97
+ const startTime = perf_hooks.performance.now();
98
+ let scopedActionResponseObjects;
99
+ let apiType;
100
+ if (shouldNavigateToGraph) {
101
+ try {
102
+ scopedActionResponseObjects = await clients_graphApi_client.GraphApiClient.checkPermissions(internalAuthToken, scopedActions);
103
+ apiType = 'graph';
131
104
  }
132
- else {
133
- throw err;
105
+ catch (error) {
106
+ const status = error instanceof mondayFetchApi.HttpFetcherError ? error.status : undefined;
107
+ authorizationInternalService.logger.warn({
108
+ tag: 'authorization-service',
109
+ error: error instanceof Error ? error.message : String(error),
110
+ accountId,
111
+ userId,
112
+ status,
113
+ }, 'Graph API authorization failed');
114
+ throw error;
134
115
  }
135
116
  }
136
- function toCamelCase(obj) {
137
- return mapKeys__default.default(obj, (_, key) => camelCase__default.default(key));
117
+ else {
118
+ const profile = this.getProfile(accountId, userId);
119
+ scopedActionResponseObjects = await clients_platformApi_client.PlatformApiClient.checkPermissions(profile, internalAuthToken, userId, scopedActions);
120
+ apiType = 'platform';
138
121
  }
139
- if (!response) {
140
- authorizationInternalService.logger.error({ tag: 'authorization-service', response }, 'AuthorizationService: missing response');
141
- throw new Error('AuthorizationService: missing response');
122
+ const endTime = perf_hooks.performance.now();
123
+ const time = endTime - startTime;
124
+ // Record metrics for each authorization check
125
+ for (const obj of scopedActionResponseObjects) {
126
+ const { action, scope } = obj.scopedAction;
127
+ const { resourceType } = utils_authorization_utils.scopeToResource(scope);
128
+ const isAuthorized = obj.permit.can;
129
+ prometheusService.sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, 200, time, apiType);
130
+ if (obj.permit.can) {
131
+ prometheusService.incrementAuthorizationSuccess(resourceType, action, apiType);
132
+ }
142
133
  }
143
- const scopedActionsResponseObjects = response.result.map(responseObject => {
144
- const { scopedAction, permit } = responseObject;
145
- const { scope } = scopedAction;
146
- return {
147
- ...responseObject,
148
- scopedAction: { ...scopedAction, scope: toCamelCase(scope) },
149
- permit: toCamelCase(permit),
150
- };
151
- });
152
- return scopedActionsResponseObjects;
134
+ return scopedActionResponseObjects;
153
135
  }
154
136
  static async isAuthorizedSingular(accountId, userId, resources, action) {
155
137
  const { authorizationObjects } = createAuthorizationParams(resources, action);
@@ -185,7 +167,7 @@ class AuthorizationService {
185
167
  });
186
168
  }
187
169
  catch (err) {
188
- if (err instanceof httpClient.HttpFetcherError) {
170
+ if (err instanceof mondayFetchApi.HttpFetcherError) {
189
171
  authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'isAuthorizedMultiple');
190
172
  }
191
173
  else {
@@ -204,7 +186,7 @@ class AuthorizationService {
204
186
  if (!isAuthorized) {
205
187
  unauthorizedObjects.push(authorizationObject);
206
188
  }
207
- prometheusService.sendAuthorizationCheckResponseTimeMetric(authorizationObject.resource_type, authorizationObject.action, isAuthorized, 200, time);
189
+ prometheusService.sendAuthorizationCheckResponseTimeMetric(authorizationObject.resource_type, authorizationObject.action, isAuthorized, 200, time, 'platform');
208
190
  });
209
191
  if (unauthorizedObjects.length > 0) {
210
192
  authorizationInternalService.logger.info({
@@ -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,EACL,YAAY,EACZ,0BAA0B,EAG3B,MAAM,mCAAmC,CAAC;AAG3C,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;IA2ClC;;OAEG;IACH,MAAM,CAAC,WAAW,CAChB,aAAa,EAAE,YAAY,EAAE,EAC7B,aAAa,EAAE,sBAAsB,GACpC,0BAA0B,EAAE;IAsC/B;;OAEG;WACU,gBAAgB,CAC3B,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;CAIzC"}
@@ -0,0 +1,123 @@
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 types_scopedActionsContracts = require('../types/scoped-actions-contracts.js');
6
+ const authorizationInternalService = require('../authorization-internal-service.js');
7
+ const attributionsService = require('../attributions-service.js');
8
+ const utils_authorization_utils = require('../utils/authorization.utils.js');
9
+ const prometheusService = require('../prometheus-service.js');
10
+
11
+ const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
12
+ /**
13
+ * Client for handling Graph API authorization operations
14
+ */
15
+ class GraphApiClient {
16
+ /**
17
+ * Builds the request body for Graph API calls
18
+ */
19
+ static buildRequestBody(scopedActions) {
20
+ const resourcesAccumulator = {};
21
+ for (const { action, scope } of scopedActions) {
22
+ const { resourceType, resourceId } = utils_authorization_utils.scopeToResource(scope);
23
+ if (!resourcesAccumulator[resourceType]) {
24
+ resourcesAccumulator[resourceType] = {};
25
+ }
26
+ if (!resourcesAccumulator[resourceType][resourceId]) {
27
+ resourcesAccumulator[resourceType][resourceId] = new Set();
28
+ }
29
+ resourcesAccumulator[resourceType][resourceId].add(action);
30
+ }
31
+ const resourcesPayload = {};
32
+ for (const [resourceType, idMap] of Object.entries(resourcesAccumulator)) {
33
+ resourcesPayload[resourceType] = {};
34
+ for (const [idStr, actionsSet] of Object.entries(idMap)) {
35
+ const idNum = Number(idStr);
36
+ resourcesPayload[resourceType][idNum] = Array.from(actionsSet);
37
+ }
38
+ }
39
+ return resourcesPayload;
40
+ }
41
+ /**
42
+ * Fetches authorization data from the Graph API
43
+ */
44
+ static async fetchPermissions(internalAuthToken, scopedActions) {
45
+ const httpClient = tridentBackendApi.Api.getPart('httpClient');
46
+ const attributionHeaders = attributionsService.getAttributionsFromApi();
47
+ const bodyPayload = this.buildRequestBody(scopedActions);
48
+ try {
49
+ const response = await httpClient.fetch({
50
+ url: {
51
+ appName: 'authorization-graph',
52
+ path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
53
+ },
54
+ method: 'POST',
55
+ headers: {
56
+ Authorization: internalAuthToken,
57
+ 'Content-Type': 'application/json',
58
+ ...attributionHeaders,
59
+ },
60
+ body: JSON.stringify(bodyPayload),
61
+ }, {
62
+ timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
63
+ retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
64
+ });
65
+ return response;
66
+ }
67
+ catch (err) {
68
+ if (err instanceof mondayFetchApi.HttpFetcherError) {
69
+ authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
70
+ if (scopedActions.length > 0) {
71
+ prometheusService.incrementAuthorizationError(utils_authorization_utils.scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status, 'graph');
72
+ }
73
+ }
74
+ throw err;
75
+ }
76
+ }
77
+ /**
78
+ * Maps Graph API response to the expected format
79
+ */
80
+ static mapResponse(scopedActions, graphResponse) {
81
+ const resources = graphResponse ?? {};
82
+ return scopedActions.map(scopedAction => {
83
+ const { action, scope } = scopedAction;
84
+ const { resourceType, resourceId } = utils_authorization_utils.scopeToResource(scope);
85
+ const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
86
+ const graphReason = permissionResult?.reason;
87
+ let reasonKey;
88
+ let additionalOptions = {};
89
+ let technicalReason = types_scopedActionsContracts.PermitTechnicalReason.NO_REASON;
90
+ if (typeof graphReason === 'string') {
91
+ reasonKey = graphReason;
92
+ }
93
+ else if (graphReason && typeof graphReason === 'object') {
94
+ reasonKey = graphReason.key ?? 'unknown';
95
+ additionalOptions = graphReason.additionalOptions ?? {};
96
+ if (graphReason.technicalReason !== undefined) {
97
+ technicalReason = (graphReason.technicalReason ?? types_scopedActionsContracts.PermitTechnicalReason.NO_REASON);
98
+ }
99
+ }
100
+ else {
101
+ reasonKey = 'unknown';
102
+ }
103
+ const permit = {
104
+ can: permissionResult?.can ?? false,
105
+ reason: {
106
+ key: reasonKey,
107
+ ...additionalOptions,
108
+ },
109
+ technicalReason,
110
+ };
111
+ return { scopedAction, permit };
112
+ });
113
+ }
114
+ /**
115
+ * Performs a complete authorization check using the Graph API
116
+ */
117
+ static async checkPermissions(internalAuthToken, scopedActions) {
118
+ const response = await this.fetchPermissions(internalAuthToken, scopedActions);
119
+ return this.mapResponse(scopedActions, response);
120
+ }
121
+ }
122
+
123
+ 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,89 @@
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
+ if (scopedActionsPayload.length > 0) {
54
+ const { resourceType } = utils_authorization_utils.scopeToResource(utils_authorization_utils.toCamelCase(scopedActionsPayload[0].scope));
55
+ prometheusService.incrementAuthorizationError(resourceType, scopedActionsPayload[0].action, err.status, 'platform');
56
+ }
57
+ }
58
+ throw err;
59
+ }
60
+ }
61
+ /**
62
+ * Maps Platform API response to the expected format
63
+ */
64
+ static mapResponse(response) {
65
+ if (!response) {
66
+ authorizationInternalService.logger.error({ tag: 'platform-api-client', response }, 'PlatformApiClient: missing response');
67
+ throw new Error('PlatformApiClient: missing response');
68
+ }
69
+ return response.result.map(responseObject => {
70
+ const { scopedAction, permit } = responseObject;
71
+ const { scope } = scopedAction;
72
+ return {
73
+ ...responseObject,
74
+ scopedAction: { ...scopedAction, scope: utils_authorization_utils.toCamelCase(scope) },
75
+ permit: utils_authorization_utils.toCamelCase(permit),
76
+ };
77
+ });
78
+ }
79
+ /**
80
+ * Performs a complete authorization check using the Platform API
81
+ */
82
+ static async checkPermissions(profile, internalAuthToken, userId, scopedActions) {
83
+ const scopedActionsPayload = this.buildRequestPayload(scopedActions);
84
+ const platformResponse = await this.fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload);
85
+ return this.mapResponse(platformResponse);
86
+ }
87
+ }
88
+
89
+ exports.PlatformApiClient = PlatformApiClient;
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../../src/authorization-service.ts"],"names":[],"mappings":"AAIA,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;AAY1C,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;AAeD,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;mBAkEnB,oBAAoB;mBAUpB,oBAAoB;CAmF1C;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
+ {"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;mBA6DnB,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"}