@mondaydotcomorg/monday-authorization 1.1.9-bugmoshefixlodashesm.3791 → 1.1.9-featurebelkaauthz-sdk-update.2209

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 (65) hide show
  1. package/CHANGELOG.md +0 -5
  2. package/README.md +0 -25
  3. package/dist/index.d.ts +4 -4
  4. package/dist/index.js +41 -18
  5. package/dist/lib/authorization-attributes-service.d.ts +23 -0
  6. package/dist/lib/authorization-attributes-service.js +65 -0
  7. package/dist/{authorization-internal-service.d.ts → lib/authorization-internal-service.d.ts} +4 -2
  8. package/dist/lib/authorization-internal-service.js +78 -0
  9. package/dist/{authorization-middleware.d.ts → lib/authorization-middleware.d.ts} +1 -1
  10. package/dist/lib/authorization-middleware.js +57 -0
  11. package/dist/lib/authorization-service.js +218 -0
  12. package/dist/{prometheus-service.js → lib/prometheus-service.js} +12 -17
  13. package/dist/{testKit → lib/testKit}/index.d.ts +2 -2
  14. package/dist/lib/testKit/index.js +58 -0
  15. package/dist/lib/types/authorization-attributes-contracts.d.ts +10 -0
  16. package/dist/lib/types/authorization-attributes-contracts.js +2 -0
  17. package/dist/lib/types/express.js +1 -0
  18. package/dist/{types → lib/types}/general.d.ts +4 -6
  19. package/dist/lib/types/general.js +2 -0
  20. package/dist/{types → lib/types}/scoped-actions-contracts.js +4 -3
  21. package/package.json +10 -31
  22. package/dist/attributions-service.d.ts +0 -3
  23. package/dist/attributions-service.js +0 -55
  24. package/dist/authorization-attributes-service.d.ts +0 -44
  25. package/dist/authorization-attributes-service.js +0 -144
  26. package/dist/authorization-internal-service.js +0 -80
  27. package/dist/authorization-middleware.js +0 -48
  28. package/dist/authorization-service.js +0 -184
  29. package/dist/constants/sns.d.ts +0 -3
  30. package/dist/constants/sns.js +0 -9
  31. package/dist/esm/attributions-service.d.ts +0 -3
  32. package/dist/esm/attributions-service.mjs +0 -53
  33. package/dist/esm/authorization-attributes-service.d.ts +0 -44
  34. package/dist/esm/authorization-attributes-service.mjs +0 -138
  35. package/dist/esm/authorization-internal-service.d.ts +0 -13
  36. package/dist/esm/authorization-internal-service.mjs +0 -57
  37. package/dist/esm/authorization-middleware.d.ts +0 -6
  38. package/dist/esm/authorization-middleware.mjs +0 -39
  39. package/dist/esm/authorization-service.d.ts +0 -29
  40. package/dist/esm/authorization-service.mjs +0 -174
  41. package/dist/esm/constants/sns.d.ts +0 -3
  42. package/dist/esm/constants/sns.mjs +0 -5
  43. package/dist/esm/index.d.ts +0 -13
  44. package/dist/esm/index.mjs +0 -21
  45. package/dist/esm/prometheus-service.mjs +0 -49
  46. package/dist/esm/testKit/index.d.ts +0 -11
  47. package/dist/esm/testKit/index.mjs +0 -44
  48. package/dist/esm/types/authorization-attributes-contracts.d.ts +0 -27
  49. package/dist/esm/types/authorization-attributes-contracts.mjs +0 -7
  50. package/dist/esm/types/express.mjs +0 -1
  51. package/dist/esm/types/general.d.ts +0 -32
  52. package/dist/esm/types/general.mjs +0 -1
  53. package/dist/esm/types/scoped-actions-contracts.mjs +0 -8
  54. package/dist/prometheus-service.d.ts +0 -10
  55. package/dist/testKit/index.js +0 -48
  56. package/dist/types/authorization-attributes-contracts.d.ts +0 -27
  57. package/dist/types/authorization-attributes-contracts.js +0 -7
  58. package/dist/types/express.d.ts +0 -10
  59. package/dist/types/express.js +0 -1
  60. package/dist/types/general.js +0 -1
  61. package/dist/types/scoped-actions-contracts.d.ts +0 -38
  62. package/dist/{authorization-service.d.ts → lib/authorization-service.d.ts} +1 -1
  63. /package/dist/{esm → lib}/prometheus-service.d.ts +0 -0
  64. /package/dist/{esm → lib}/types/express.d.ts +0 -0
  65. /package/dist/{esm → lib}/types/scoped-actions-contracts.d.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -5,11 +5,6 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
- ## [1.2.9] - 2024-10-06
9
- ### Added
10
- - [`authz/bashanye/add-async-resource-attributes-support`](https://github.com/DaPulse/monday-npm-packages/pull/6859)
11
- - `AuthorizationAttributesService` - now supports async upsert and delete - requests sent through SNS-SQS).
12
-
13
8
  ## [1.2.3] - 2024-06-10
14
9
  ### Added
15
10
 
package/README.md CHANGED
@@ -138,10 +138,7 @@ const canActionInScopeMultipleResponse: ScopedActionResponseObject[] =
138
138
  ```
139
139
 
140
140
  ### Authorization Attributes API
141
- Authorization attributes have 2 options to get called: sync (http request) and async (send to SNS and consumed asynchronously).
142
- When you have to make sure the change in the attributes applied before the function return, please use the sync method, otherwise use the async
143
141
 
144
- #### Sync method
145
142
  Use `AuthorizationAttributesService.upsertResourceAttributesSync` to upsert multiple resource attributes in the authorization MS synchronously.
146
143
 
147
144
  ```ts
@@ -171,25 +168,3 @@ const attributeKeys: string[] = ['is_default_workspace', 'workspace_kind'];
171
168
  const response: ResourceAttributeResponse = await AuthorizationAttributesService.deleteResourceAttributesSync(accountId, userId, resource, attributeKeys);
172
169
  ```
173
170
 
174
- #### Async method
175
- use `AuthorizationAttributesService.updateResourceAttributesAsync` to upsert or delete multiple resource attributes at once.
176
-
177
- ```ts
178
- import { AuthorizationAttributesService, ResourceAttributeAssignment, ResourceAttributeResponse } from '@mondaydotcomorg/monday-authorization';
179
-
180
- const accountId = 739630;
181
- const appName = process.env.APP_NAME;
182
- const callerActionIdentifier = "actions_v2";
183
- const resourceAttributeOperations: ResourceAttributesOperation[] = [
184
- { operationType: 'upsert', resourceId: 18, resourceType: 'workspace', key: 'is_default_workspace', value: 'true' },
185
- { operationType: 'delete', resourceId: 23, resourceType: 'board', key: 'board_kind' }
186
- ];
187
-
188
- const response: ResourceAttributeResponse = await AuthorizationAttributesService.updateResourceAttributesAsync(accountId, appName, callerActionIdentifier, resourceAttributeOperations);
189
- ```
190
-
191
- Special notes for asynchronous operations:
192
- 1. There is no guarantee about the order of the updates, so don't do multiple operations on the same key in the same resource.
193
- 2. To update an existing key, just use upsert operation, it'll override previous value.
194
- 3. Requests with a lot of operations might split to chunks that will be consumed either sequence or in parallel, so there might be a timeframe where some of the operations already applied and some not. Eventually all of them will be applied.
195
- 4. If your MS depends on the access to the asynchronous operation, you can use a health check operation `asyncResourceAttributesHealthCheck` that will return false if it can't reach to SNS or can't find the required topic. Note it doesn't check write permissions so make sure your MS have permissions to write to the SNS topic.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
- import * as TestKit from './testKit';
2
+ import * as TestKit from './lib/testKit';
3
3
  export interface InitOptions {
4
4
  prometheus?: any;
5
5
  mondayFetchOptions?: MondayFetchOptions;
@@ -7,7 +7,7 @@ export interface InitOptions {
7
7
  grantedFeatureRedisExpirationInSeconds?: number;
8
8
  }
9
9
  export declare function init(options?: InitOptions): void;
10
- export { authorizationCheckMiddleware, getAuthorizationMiddleware, skipAuthorizationMiddleware, } from './authorization-middleware';
11
- export { AuthorizationService } from './authorization-service';
12
- export { AuthorizationAttributesService } from './authorization-attributes-service';
10
+ export { authorizationCheckMiddleware, getAuthorizationMiddleware, skipAuthorizationMiddleware, } from './lib/authorization-middleware';
11
+ export { AuthorizationService } from './lib/authorization-service';
12
+ export { AuthorizationAttributesService } from './lib/authorization-attributes-service';
13
13
  export { TestKit };
package/dist/index.js CHANGED
@@ -1,27 +1,50 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
-
3
- const prometheusService = require('./prometheus-service.js');
4
- const authorizationService = require('./authorization-service.js');
5
- const testKit_index = require('./testKit/index.js');
6
- const authorizationMiddleware = require('./authorization-middleware.js');
7
- const authorizationAttributesService = require('./authorization-attributes-service.js');
8
-
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.TestKit = exports.AuthorizationAttributesService = exports.AuthorizationService = exports.skipAuthorizationMiddleware = exports.getAuthorizationMiddleware = exports.authorizationCheckMiddleware = exports.init = void 0;
27
+ const prometheus_service_1 = require("./lib/prometheus-service");
28
+ const authorization_service_1 = require("./lib/authorization-service");
29
+ const TestKit = __importStar(require("./lib/testKit"));
30
+ exports.TestKit = TestKit;
9
31
  function init(options = {}) {
10
32
  if (options.prometheus) {
11
- prometheusService.setPrometheus(options.prometheus);
33
+ (0, prometheus_service_1.setPrometheus)(options.prometheus);
12
34
  }
13
35
  if (options.mondayFetchOptions) {
14
- authorizationService.setRequestFetchOptions(options.mondayFetchOptions);
36
+ (0, authorization_service_1.setRequestFetchOptions)(options.mondayFetchOptions);
15
37
  }
16
38
  if (options.redisClient) {
17
- authorizationService.setRedisClient(options.redisClient, options.grantedFeatureRedisExpirationInSeconds);
39
+ (0, authorization_service_1.setRedisClient)(options.redisClient, options.grantedFeatureRedisExpirationInSeconds);
18
40
  }
19
41
  }
20
-
21
- exports.AuthorizationService = authorizationService.AuthorizationService;
22
- exports.TestKit = testKit_index;
23
- exports.authorizationCheckMiddleware = authorizationMiddleware.authorizationCheckMiddleware;
24
- exports.getAuthorizationMiddleware = authorizationMiddleware.getAuthorizationMiddleware;
25
- exports.skipAuthorizationMiddleware = authorizationMiddleware.skipAuthorizationMiddleware;
26
- exports.AuthorizationAttributesService = authorizationAttributesService.AuthorizationAttributesService;
27
42
  exports.init = init;
43
+ var authorization_middleware_1 = require("./lib/authorization-middleware");
44
+ Object.defineProperty(exports, "authorizationCheckMiddleware", { enumerable: true, get: function () { return authorization_middleware_1.authorizationCheckMiddleware; } });
45
+ Object.defineProperty(exports, "getAuthorizationMiddleware", { enumerable: true, get: function () { return authorization_middleware_1.getAuthorizationMiddleware; } });
46
+ Object.defineProperty(exports, "skipAuthorizationMiddleware", { enumerable: true, get: function () { return authorization_middleware_1.skipAuthorizationMiddleware; } });
47
+ var authorization_service_2 = require("./lib/authorization-service");
48
+ Object.defineProperty(exports, "AuthorizationService", { enumerable: true, get: function () { return authorization_service_2.AuthorizationService; } });
49
+ var authorization_attributes_service_1 = require("./lib/authorization-attributes-service");
50
+ Object.defineProperty(exports, "AuthorizationAttributesService", { enumerable: true, get: function () { return authorization_attributes_service_1.AuthorizationAttributesService; } });
@@ -0,0 +1,23 @@
1
+ import { ResourceAttributeAssignment, ResourceAttributeResponse } from './types/authorization-attributes-contracts';
2
+ import { Resource } from './types/general';
3
+ export declare class AuthorizationAttributesService {
4
+ /**
5
+ * Upsert resource attributes synchronously, performing http call to the authorization MS to assign the given attributes to the given resource.
6
+ * @param accountId
7
+ * @param userId
8
+ * @param resourceAttributeAssignments - Array of resource (resourceType, resourceId) and attribute (key, value) pairs to upsert in the authorization MS.
9
+ * e.g. [{ resourceType: 'board', resourceId: 123, key: 'board_kind', value: 'private' }]
10
+ * @returns ResourceAttributeResponse - The affected (created and updated_ resource attributes assignments in the `attributes` field.
11
+ */
12
+ static upsertResourceAttributes(accountId: number, userId: number, resourceAttributeAssignments: ResourceAttributeAssignment[]): Promise<ResourceAttributeResponse>;
13
+ /**
14
+ * Delete resource attributes assignments synchronously, performing http call to the authorization MS to delete the given attributes from the given singular resource.
15
+ * @param accountId
16
+ * @param userId
17
+ * @param resource - The resource (resourceType, resourceId) to delete the attributes for.
18
+ * @param attributeKeys - Array of attribute keys to delete for the resource.
19
+ * @returns ResourceAttributeResponse - The affected (deleted) resource attributes assignments in the `attributes` field.
20
+ */
21
+ static deleteResourceAttributes(accountId: number, userId: number, resource: Resource, attributeKeys: string[]): Promise<ResourceAttributeResponse>;
22
+ private static getResourceAttributesUrl;
23
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AuthorizationAttributesService = void 0;
13
+ const monday_fetch_1 = require("@mondaydotcomorg/monday-fetch");
14
+ const authorization_internal_service_1 = require("./authorization-internal-service");
15
+ class AuthorizationAttributesService {
16
+ /**
17
+ * Upsert resource attributes synchronously, performing http call to the authorization MS to assign the given attributes to the given resource.
18
+ * @param accountId
19
+ * @param userId
20
+ * @param resourceAttributeAssignments - Array of resource (resourceType, resourceId) and attribute (key, value) pairs to upsert in the authorization MS.
21
+ * e.g. [{ resourceType: 'board', resourceId: 123, key: 'board_kind', value: 'private' }]
22
+ * @returns ResourceAttributeResponse - The affected (created and updated_ resource attributes assignments in the `attributes` field.
23
+ */
24
+ static upsertResourceAttributes(accountId, userId, resourceAttributeAssignments) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ const internalAuthToken = authorization_internal_service_1.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
27
+ const response = yield (0, monday_fetch_1.fetch)(this.getResourceAttributesUrl(accountId), {
28
+ method: 'POST',
29
+ headers: { Authorization: internalAuthToken, 'Content-Type': 'application/json' },
30
+ timeout: authorization_internal_service_1.AuthorizationInternalService.getRequestTimeout(),
31
+ body: JSON.stringify({ resourceAttributeAssignments }),
32
+ }, authorization_internal_service_1.AuthorizationInternalService.getRequestFetchOptions());
33
+ const responseBody = yield response.json();
34
+ authorization_internal_service_1.AuthorizationInternalService.throwOnHttpErrorIfNeeded(response, 'upsertResourceAttributesSync');
35
+ return { attributes: responseBody['attributes'] };
36
+ });
37
+ }
38
+ /**
39
+ * Delete resource attributes assignments synchronously, performing http call to the authorization MS to delete the given attributes from the given singular resource.
40
+ * @param accountId
41
+ * @param userId
42
+ * @param resource - The resource (resourceType, resourceId) to delete the attributes for.
43
+ * @param attributeKeys - Array of attribute keys to delete for the resource.
44
+ * @returns ResourceAttributeResponse - The affected (deleted) resource attributes assignments in the `attributes` field.
45
+ */
46
+ static deleteResourceAttributes(accountId, userId, resource, attributeKeys) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ const internalAuthToken = authorization_internal_service_1.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
49
+ const url = `${this.getResourceAttributesUrl(accountId)}/${resource.type}/${resource.id}`;
50
+ const response = yield (0, monday_fetch_1.fetch)(url, {
51
+ method: 'DELETE',
52
+ headers: { Authorization: internalAuthToken, 'Content-Type': 'application/json' },
53
+ timeout: authorization_internal_service_1.AuthorizationInternalService.getRequestTimeout(),
54
+ body: JSON.stringify({ keys: attributeKeys }),
55
+ }, authorization_internal_service_1.AuthorizationInternalService.getRequestFetchOptions());
56
+ const responseBody = yield response.json();
57
+ authorization_internal_service_1.AuthorizationInternalService.throwOnHttpErrorIfNeeded(response, 'deleteResourceAttributesSync');
58
+ return { attributes: responseBody['attributes'] };
59
+ });
60
+ }
61
+ static getResourceAttributesUrl(accountId) {
62
+ return `${process.env.AUTHORIZATION_URL}/attributes/${accountId}/resource`;
63
+ }
64
+ }
65
+ exports.AuthorizationAttributesService = AuthorizationAttributesService;
@@ -1,6 +1,8 @@
1
+ /// <reference types="bunyan" />
2
+ import { Request } from 'express';
1
3
  import { fetch, MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
- import type { Request } from 'express';
3
- export declare const logger: import("bunyan");
4
+ import * as MondayLogger from '@mondaydotcomorg/monday-logger';
5
+ export declare const logger: MondayLogger.Logger;
4
6
  export declare class AuthorizationInternalService {
5
7
  static skipAuthorization(requset: Request): void;
6
8
  static markAuthorized(request: Request): void;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.AuthorizationInternalService = exports.logger = void 0;
27
+ const monday_jwt_1 = require("@mondaydotcomorg/monday-jwt");
28
+ const MondayLogger = __importStar(require("@mondaydotcomorg/monday-logger"));
29
+ const INTERNAL_APP_NAME = 'internal_ms';
30
+ const defaultMondayFetchOptions = {
31
+ retries: 3,
32
+ callback: logOnFetchFail,
33
+ };
34
+ function logOnFetchFail(retriesLeft, error) {
35
+ if (retriesLeft == 0) {
36
+ exports.logger.error({ retriesLeft, error }, 'Authorization attempt failed due to network issues');
37
+ }
38
+ else {
39
+ exports.logger.info({ retriesLeft, error }, 'Authorization attempt failed due to network issues, trying again');
40
+ }
41
+ }
42
+ let mondayFetchOptions = defaultMondayFetchOptions;
43
+ exports.logger = MondayLogger.getLogger();
44
+ class AuthorizationInternalService {
45
+ static skipAuthorization(requset) {
46
+ requset.authorizationSkipPerformed = true;
47
+ }
48
+ static markAuthorized(request) {
49
+ request.authorizationCheckPerformed = true;
50
+ }
51
+ static failIfNotCoveredByAuthorization(request) {
52
+ if (!request.authorizationCheckPerformed && !request.authorizationSkipPerformed) {
53
+ throw 'Endpoint is not covered by authorization check';
54
+ }
55
+ }
56
+ static throwOnHttpErrorIfNeeded(response, placement) {
57
+ if (response.ok) {
58
+ return;
59
+ }
60
+ const status = response.status;
61
+ exports.logger.error({ tag: 'authorization-service', placement, status }, 'AuthorizationService: authorization request failed');
62
+ throw new Error(`AuthorizationService: [${placement}] authorization request failed with status ${status}`);
63
+ }
64
+ static generateInternalAuthToken(accountId, userId) {
65
+ return (0, monday_jwt_1.signAuthorizationHeader)({ appName: INTERNAL_APP_NAME, accountId, userId });
66
+ }
67
+ static setRequestFetchOptions(customMondayFetchOptions) {
68
+ mondayFetchOptions = Object.assign(Object.assign({}, defaultMondayFetchOptions), customMondayFetchOptions);
69
+ }
70
+ static getRequestFetchOptions() {
71
+ return mondayFetchOptions;
72
+ }
73
+ static getRequestTimeout() {
74
+ const isDevEnv = process.env.NODE_ENV === 'development';
75
+ return isDevEnv ? 60000 : 2000;
76
+ }
77
+ }
78
+ exports.AuthorizationInternalService = AuthorizationInternalService;
@@ -1,5 +1,5 @@
1
+ import { NextFunction } from 'express';
1
2
  import { Action, BaseRequest, BaseResponse, Context, ContextGetter, ResourceGetter } from './types/general';
2
- import type { NextFunction } from 'express';
3
3
  export declare function getAuthorizationMiddleware(action: Action, resourceGetter: ResourceGetter, contextGetter?: ContextGetter): (request: BaseRequest, response: BaseResponse, next: NextFunction) => Promise<void>;
4
4
  export declare function skipAuthorizationMiddleware(request: BaseRequest, response: BaseResponse, next: NextFunction): void;
5
5
  export declare function authorizationCheckMiddleware(request: BaseRequest, response: BaseResponse, next: NextFunction): void;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.defaultContextGetter = exports.authorizationCheckMiddleware = exports.skipAuthorizationMiddleware = exports.getAuthorizationMiddleware = void 0;
16
+ const on_headers_1 = __importDefault(require("on-headers"));
17
+ const authorization_internal_service_1 = require("./authorization-internal-service");
18
+ const authorization_service_1 = require("./authorization-service");
19
+ // getAuthorizationMiddleware is duplicated in testKit/index.ts
20
+ // If you are making changes to this function, please make sure to update the other file as well
21
+ function getAuthorizationMiddleware(action, resourceGetter, contextGetter) {
22
+ return function authorizationMiddleware(request, response, next) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ contextGetter || (contextGetter = defaultContextGetter);
25
+ const { userId, accountId } = contextGetter(request);
26
+ const resources = resourceGetter(request);
27
+ const { isAuthorized } = yield authorization_service_1.AuthorizationService.isAuthorized(accountId, userId, resources, action);
28
+ authorization_internal_service_1.AuthorizationInternalService.markAuthorized(request);
29
+ if (!isAuthorized) {
30
+ response.status(403).json({ message: 'Access denied' });
31
+ return;
32
+ }
33
+ next();
34
+ });
35
+ };
36
+ }
37
+ exports.getAuthorizationMiddleware = getAuthorizationMiddleware;
38
+ function skipAuthorizationMiddleware(request, response, next) {
39
+ authorization_internal_service_1.AuthorizationInternalService.skipAuthorization(request);
40
+ next();
41
+ }
42
+ exports.skipAuthorizationMiddleware = skipAuthorizationMiddleware;
43
+ function authorizationCheckMiddleware(request, response, next) {
44
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
45
+ (0, on_headers_1.default)(response, function () {
46
+ if (response.statusCode < 400) {
47
+ authorization_internal_service_1.AuthorizationInternalService.failIfNotCoveredByAuthorization(request);
48
+ }
49
+ });
50
+ }
51
+ next();
52
+ }
53
+ exports.authorizationCheckMiddleware = authorizationCheckMiddleware;
54
+ function defaultContextGetter(request) {
55
+ return request.payload;
56
+ }
57
+ exports.defaultContextGetter = defaultContextGetter;
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AuthorizationService = exports.setRedisClient = exports.setRequestFetchOptions = void 0;
13
+ const lodash_1 = require("lodash");
14
+ const perf_hooks_1 = require("perf_hooks");
15
+ const monday_fetch_1 = require("@mondaydotcomorg/monday-fetch");
16
+ const prometheus_service_1 = require("./prometheus-service");
17
+ const authorization_internal_service_1 = require("./authorization-internal-service");
18
+ const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
19
+ const APP_NAME_VARIABLE_KEY = 'APP_NAME';
20
+ function setRequestFetchOptions(customMondayFetchOptions) {
21
+ authorization_internal_service_1.AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
22
+ }
23
+ exports.setRequestFetchOptions = setRequestFetchOptions;
24
+ function setRedisClient(client, grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS) {
25
+ AuthorizationService.redisClient = client;
26
+ if (grantedFeatureRedisExpirationInSeconds && grantedFeatureRedisExpirationInSeconds > 0) {
27
+ AuthorizationService.grantedFeatureRedisExpirationInSeconds = grantedFeatureRedisExpirationInSeconds;
28
+ }
29
+ else {
30
+ authorization_internal_service_1.logger.warn({ grantedFeatureRedisExpirationInSeconds }, 'Invalid input for grantedFeatureRedisExpirationInSeconds, must be positive number. using default ttl.');
31
+ AuthorizationService.grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS;
32
+ }
33
+ }
34
+ exports.setRedisClient = setRedisClient;
35
+ class AuthorizationService {
36
+ static isAuthorized(...args) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ if (args.length === 3) {
39
+ return this.isAuthorizedMultiple(args[0], args[1], args[2]);
40
+ }
41
+ else if (args.length == 4) {
42
+ return this.isAuthorizedSingular(args[0], args[1], args[2], args[3]);
43
+ }
44
+ else {
45
+ throw new Error('isAuthorized accepts either 3 or 4 arguments');
46
+ }
47
+ });
48
+ }
49
+ static isUserGrantedWithFeature(accountId, userId, featureName, options = {}) {
50
+ var _a;
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ let cachedKey = this.getCachedKeyName(userId, featureName);
53
+ const shouldSkipCache = (_a = options.shouldSkipCache) !== null && _a !== void 0 ? _a : false;
54
+ if (this.redisClient && !shouldSkipCache) {
55
+ let grantedFeatureValue = yield this.redisClient.get(cachedKey);
56
+ if (!(grantedFeatureValue === undefined || grantedFeatureValue === null)) {
57
+ // redis returns the value as string
58
+ return grantedFeatureValue === 'true';
59
+ }
60
+ }
61
+ let grantedFeatureValue = yield this.fetchIsUserGrantedWithFeature(featureName, accountId, userId);
62
+ if (this.redisClient) {
63
+ yield this.redisClient.set(cachedKey, grantedFeatureValue, 'EX', this.grantedFeatureRedisExpirationInSeconds);
64
+ }
65
+ return grantedFeatureValue;
66
+ });
67
+ }
68
+ static fetchIsUserGrantedWithFeature(featureName, accountId, userId) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ let authorizationObject = {
71
+ action: featureName,
72
+ resource_type: 'feature',
73
+ };
74
+ let authorizeResponsePromise = yield this.isAuthorized(accountId, userId, [authorizationObject]);
75
+ return authorizeResponsePromise.isAuthorized;
76
+ });
77
+ }
78
+ static getCachedKeyName(userId, featureName) {
79
+ return `granted-feature-${featureName}-${userId}`;
80
+ }
81
+ static canActionInScope(accountId, userId, action, scope) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ const scopedActions = [{ action, scope }];
84
+ const scopedActionResponseObjects = yield this.canActionInScopeMultiple(accountId, userId, scopedActions);
85
+ return scopedActionResponseObjects[0].permit;
86
+ });
87
+ }
88
+ static canActionInScopeMultiple(accountId, userId, scopedActions) {
89
+ return __awaiter(this, void 0, void 0, function* () {
90
+ const internalAuthToken = authorization_internal_service_1.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
91
+ const scopedActionsPayload = scopedActions.map(scopedAction => {
92
+ return Object.assign(Object.assign({}, scopedAction), { scope: (0, lodash_1.mapKeys)(scopedAction.scope, (_, key) => (0, lodash_1.snakeCase)(key)) }); // for example: { workspaceId: 1 } => { workspace_id: 1 }
93
+ });
94
+ const response = yield (0, monday_fetch_1.fetch)(getCanActionsInScopesUrl(), {
95
+ method: 'POST',
96
+ headers: { Authorization: internalAuthToken, 'Content-Type': 'application/json' },
97
+ timeout: authorization_internal_service_1.AuthorizationInternalService.getRequestTimeout(),
98
+ body: JSON.stringify({
99
+ user_id: userId,
100
+ scoped_actions: scopedActionsPayload,
101
+ }),
102
+ }, authorization_internal_service_1.AuthorizationInternalService.getRequestFetchOptions());
103
+ authorization_internal_service_1.AuthorizationInternalService.throwOnHttpErrorIfNeeded(response, 'canActionInScopeMultiple');
104
+ const responseBody = yield response.json();
105
+ const camelCaseKeys = obj => Object.fromEntries(Object.entries(obj).map(([key, value]) => [(0, lodash_1.camelCase)(key), value]));
106
+ const scopedActionsResponseObjects = responseBody.result.map(responseObject => {
107
+ const { scopedAction, permit } = responseObject;
108
+ const { scope } = scopedAction;
109
+ const transformKeys = obj => camelCaseKeys(obj);
110
+ return Object.assign(Object.assign({}, responseObject), { scopedAction: Object.assign(Object.assign({}, scopedAction), { scope: transformKeys(scope) }), permit: transformKeys(permit) });
111
+ });
112
+ return scopedActionsResponseObjects;
113
+ });
114
+ }
115
+ static isAuthorizedSingular(accountId, userId, resources, action) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ const { authorizationObjects } = createAuthorizationParams(resources, action);
118
+ return this.isAuthorizedMultiple(accountId, userId, authorizationObjects);
119
+ });
120
+ }
121
+ static isAuthorizedMultiple(accountId, userId, authorizationRequestObjects) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ const internalAuthToken = authorization_internal_service_1.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
124
+ const startTime = perf_hooks_1.performance.now();
125
+ const response = yield (0, monday_fetch_1.fetch)(getAuthorizeUrl(), {
126
+ method: 'POST',
127
+ headers: {
128
+ Authorization: internalAuthToken,
129
+ 'Content-Type': 'application/json',
130
+ 'x-internal-controller-caller-app-name': getCurrentAppName(),
131
+ },
132
+ timeout: authorization_internal_service_1.AuthorizationInternalService.getRequestTimeout(),
133
+ body: JSON.stringify({
134
+ user_id: userId,
135
+ authorize_request_objects: authorizationRequestObjects,
136
+ }),
137
+ }, authorization_internal_service_1.AuthorizationInternalService.getRequestFetchOptions());
138
+ const endTime = perf_hooks_1.performance.now();
139
+ const time = endTime - startTime;
140
+ const responseStatus = response.status;
141
+ (0, prometheus_service_1.sendAuthorizationChecksPerRequestMetric)(responseStatus, authorizationRequestObjects.length);
142
+ authorization_internal_service_1.AuthorizationInternalService.throwOnHttpErrorIfNeeded(response, 'isAuthorizedMultiple');
143
+ const responseBody = yield response.json();
144
+ const unauthorizedObjects = [];
145
+ responseBody.result.forEach(function (isAuthorized, index) {
146
+ const authorizationObject = authorizationRequestObjects[index];
147
+ if (!isAuthorized) {
148
+ unauthorizedObjects.push(authorizationObject);
149
+ }
150
+ (0, prometheus_service_1.sendAuthorizationCheckResponseTimeMetric)(authorizationObject.resource_type, authorizationObject.action, isAuthorized, responseStatus, time);
151
+ });
152
+ if (unauthorizedObjects.length > 0) {
153
+ authorization_internal_service_1.logger.info({
154
+ resources: JSON.stringify(unauthorizedObjects),
155
+ }, 'AuthorizationService: resource is unauthorized');
156
+ const unauthorizedIds = unauthorizedObjects
157
+ .filter(obj => !!obj.resource_id)
158
+ .map(obj => obj.resource_id);
159
+ return { isAuthorized: false, unauthorizedIds, unauthorizedObjects };
160
+ }
161
+ return { isAuthorized: true };
162
+ });
163
+ }
164
+ }
165
+ exports.AuthorizationService = AuthorizationService;
166
+ function createAuthorizationParams(resources, action) {
167
+ const params = {
168
+ authorizationObjects: resources.map((resource) => {
169
+ const authorizationObject = {
170
+ resource_id: resource.id,
171
+ resource_type: resource.type,
172
+ action,
173
+ };
174
+ if (resource.wrapperData) {
175
+ authorizationObject.wrapper_data = resource.wrapperData;
176
+ }
177
+ return authorizationObject;
178
+ }),
179
+ };
180
+ return params;
181
+ }
182
+ function getAuthorizeUrl() {
183
+ return `${process.env.MONDAY_INTERNAL_URL}/internal_ms/authorization/authorize`;
184
+ }
185
+ function getCanActionsInScopesUrl() {
186
+ return `${process.env.MONDAY_INTERNAL_URL}/internal_ms/authorization/can_actions_in_scopes`;
187
+ }
188
+ function getCurrentAppName() {
189
+ try {
190
+ return getVariable(APP_NAME_VARIABLE_KEY);
191
+ }
192
+ catch (error) {
193
+ authorization_internal_service_1.logger.warn('Failed to get app name environment variable for runtime attributions', { error: error });
194
+ return 'MISSING';
195
+ }
196
+ }
197
+ function getVariable(key, options = { silent: false }) {
198
+ const result = tryJsonParse(getEnvVariable(key));
199
+ if (result === undefined && !options.silent) {
200
+ authorization_internal_service_1.logger.warn(`Configuration variable ${key} is undefined`);
201
+ }
202
+ return result;
203
+ }
204
+ function getEnvVariable(key) {
205
+ const envVar = process.env[key] || process.env[key.toUpperCase()] || process.env[key.toLowerCase()];
206
+ return envVar;
207
+ }
208
+ function tryJsonParse(value) {
209
+ if (!value) {
210
+ return value;
211
+ }
212
+ try {
213
+ return JSON.parse(value);
214
+ }
215
+ catch (_err) {
216
+ return value;
217
+ }
218
+ }