@mondaydotcomorg/monday-authorization 1.1.9-featuremosheauthorizationesm.3695 → 1.1.9-featuremosheauthorizationesm.3696
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/attributions-service.js +2 -2
- package/dist/authorization-attributes-service.js +8 -8
- package/dist/authorization-internal-service.d.ts +1 -1
- package/dist/authorization-internal-service.js +1 -1
- package/dist/authorization-middleware.d.ts +1 -1
- package/dist/authorization-service.d.ts +1 -1
- package/dist/authorization-service.js +16 -16
- package/dist/esm/attributions-service.mjs +2 -2
- package/dist/esm/authorization-attributes-service.mjs +8 -8
- package/dist/esm/authorization-internal-service.d.ts +1 -1
- package/dist/esm/authorization-internal-service.mjs +1 -1
- package/dist/esm/authorization-middleware.d.ts +1 -1
- package/dist/esm/authorization-service.d.ts +1 -1
- package/dist/esm/authorization-service.mjs +16 -16
- package/dist/esm/prometheus-service.mjs +6 -2
- package/dist/esm/testKit/index.d.ts +2 -2
- package/dist/esm/testKit/index.mjs +6 -6
- package/dist/esm/types/general.d.ts +6 -4
- package/dist/prometheus-service.js +6 -2
- package/dist/testKit/index.d.ts +2 -2
- package/dist/testKit/index.js +6 -6
- package/dist/types/general.d.ts +6 -4
- package/package.json +9 -11
|
@@ -8,7 +8,7 @@ const APP_NAME_HEADER_NAME = 'x-caller-app-name-from-sdk';
|
|
|
8
8
|
const FROM_SDK_HEADER_SUFFIX = `-from-sdk`;
|
|
9
9
|
let didSendFailureLogOnce = false;
|
|
10
10
|
function getAttributionsFromApi() {
|
|
11
|
-
|
|
11
|
+
const callerAppNameFromSdk = {
|
|
12
12
|
[APP_NAME_HEADER_NAME]: tryJsonParse(getEnvVariable(APP_NAME_VARIABLE_KEY)),
|
|
13
13
|
};
|
|
14
14
|
try {
|
|
@@ -17,7 +17,7 @@ function getAttributionsFromApi() {
|
|
|
17
17
|
return callerAppNameFromSdk;
|
|
18
18
|
}
|
|
19
19
|
const { runtimeAttributions } = tridentContext;
|
|
20
|
-
|
|
20
|
+
const runtimeAttributionsOutgoingHeaders = runtimeAttributions?.buildOutgoingHeaders('HTTP_INTERNAL');
|
|
21
21
|
if (!runtimeAttributionsOutgoingHeaders) {
|
|
22
22
|
return callerAppNameFromSdk;
|
|
23
23
|
}
|
|
@@ -2,10 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const chunk = require('lodash/chunk');
|
|
4
4
|
const mondayFetch = require('@mondaydotcomorg/monday-fetch');
|
|
5
|
-
const authorizationInternalService = require('./authorization-internal-service.js');
|
|
6
|
-
const attributionsService = require('./attributions-service.js');
|
|
7
5
|
const tridentBackendApi = require('@mondaydotcomorg/trident-backend-api');
|
|
8
6
|
const mondaySns = require('@mondaydotcomorg/monday-sns');
|
|
7
|
+
const authorizationInternalService = require('./authorization-internal-service.js');
|
|
8
|
+
const attributionsService = require('./attributions-service.js');
|
|
9
9
|
const constants_sns = require('./constants/sns.js');
|
|
10
10
|
|
|
11
11
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
@@ -13,7 +13,7 @@ const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
|
13
13
|
const chunk__default = /*#__PURE__*/_interopDefault(chunk);
|
|
14
14
|
|
|
15
15
|
class AuthorizationAttributesService {
|
|
16
|
-
static LOG_TAG =
|
|
16
|
+
static LOG_TAG = 'authorization_attributes';
|
|
17
17
|
/**
|
|
18
18
|
* Upsert resource attributes synchronously, performing http call to the authorization MS to assign the given attributes to the given resource.
|
|
19
19
|
* @param accountId
|
|
@@ -90,14 +90,14 @@ class AuthorizationAttributesService {
|
|
|
90
90
|
callerAppName: appName,
|
|
91
91
|
callerActionIdentifier: callerActionIdentifier,
|
|
92
92
|
operations: operations,
|
|
93
|
-
}
|
|
93
|
+
},
|
|
94
94
|
};
|
|
95
95
|
try {
|
|
96
96
|
await mondaySns.sendToSns(payload, topicArn);
|
|
97
97
|
return operations;
|
|
98
98
|
}
|
|
99
99
|
catch (error) {
|
|
100
|
-
authorizationInternalService.logger.error({ error, tag: this.LOG_TAG },
|
|
100
|
+
authorizationInternalService.logger.error({ error, tag: this.LOG_TAG }, 'Authorization resource attributes async update: failed to send operations to SNS');
|
|
101
101
|
return [];
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -128,14 +128,14 @@ class AuthorizationAttributesService {
|
|
|
128
128
|
try {
|
|
129
129
|
const requestedTopicArn = this.getSnsTopicArn();
|
|
130
130
|
const attributes = await mondaySns.getTopicAttributes(requestedTopicArn);
|
|
131
|
-
const isHealthy = !(!attributes || !(
|
|
131
|
+
const isHealthy = !(!attributes || !('TopicArn' in attributes) || attributes.TopicArn !== requestedTopicArn);
|
|
132
132
|
if (!isHealthy) {
|
|
133
|
-
authorizationInternalService.logger.error({ requestedTopicArn, snsReturnedAttributes: attributes, tag: this.LOG_TAG },
|
|
133
|
+
authorizationInternalService.logger.error({ requestedTopicArn, snsReturnedAttributes: attributes, tag: this.LOG_TAG }, 'authorization-attributes-service failed in health check');
|
|
134
134
|
}
|
|
135
135
|
return isHealthy;
|
|
136
136
|
}
|
|
137
137
|
catch (error) {
|
|
138
|
-
authorizationInternalService.logger.error({ error, tag: this.LOG_TAG },
|
|
138
|
+
authorizationInternalService.logger.error({ error, tag: this.LOG_TAG }, 'authorization-attributes-service got error during health check');
|
|
139
139
|
return false;
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Request } from 'express';
|
|
2
1
|
import { fetch, MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
|
|
2
|
+
import type { Request } from 'express';
|
|
3
3
|
export declare const logger: import("bunyan");
|
|
4
4
|
export declare class AuthorizationInternalService {
|
|
5
5
|
static skipAuthorization(requset: Request): void;
|
|
@@ -28,6 +28,7 @@ const defaultMondayFetchOptions = {
|
|
|
28
28
|
retries: 3,
|
|
29
29
|
callback: logOnFetchFail,
|
|
30
30
|
};
|
|
31
|
+
const logger = MondayLogger__namespace.getLogger();
|
|
31
32
|
function logOnFetchFail(retriesLeft, error) {
|
|
32
33
|
if (retriesLeft == 0) {
|
|
33
34
|
logger.error({ retriesLeft, error }, 'Authorization attempt failed due to network issues');
|
|
@@ -37,7 +38,6 @@ function logOnFetchFail(retriesLeft, error) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
let mondayFetchOptions = defaultMondayFetchOptions;
|
|
40
|
-
const logger = MondayLogger__namespace.getLogger();
|
|
41
41
|
class AuthorizationInternalService {
|
|
42
42
|
static skipAuthorization(requset) {
|
|
43
43
|
requset.authorizationSkipPerformed = true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { NextFunction } from 'express';
|
|
2
1
|
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;
|
|
@@ -7,7 +7,6 @@ export interface AuthorizeResponse {
|
|
|
7
7
|
unauthorizedObjects?: AuthorizationObject[];
|
|
8
8
|
}
|
|
9
9
|
export declare function setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
|
|
10
|
-
export declare function setRedisClient(client: any, grantedFeatureRedisExpirationInSeconds?: number): void;
|
|
11
10
|
export declare class AuthorizationService {
|
|
12
11
|
static redisClient?: any;
|
|
13
12
|
static grantedFeatureRedisExpirationInSeconds?: number;
|
|
@@ -27,3 +26,4 @@ export declare class AuthorizationService {
|
|
|
27
26
|
private static isAuthorizedSingular;
|
|
28
27
|
private static isAuthorizedMultiple;
|
|
29
28
|
}
|
|
29
|
+
export declare function setRedisClient(client: any, grantedFeatureRedisExpirationInSeconds?: number): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const lodash = require('lodash');
|
|
4
3
|
const perf_hooks = require('perf_hooks');
|
|
4
|
+
const lodash = require('lodash');
|
|
5
5
|
const mondayFetch = require('@mondaydotcomorg/monday-fetch');
|
|
6
6
|
const prometheusService = require('./prometheus-service.js');
|
|
7
7
|
const authorizationInternalService = require('./authorization-internal-service.js');
|
|
@@ -11,16 +11,6 @@ const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
|
|
|
11
11
|
function setRequestFetchOptions(customMondayFetchOptions) {
|
|
12
12
|
authorizationInternalService.AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
|
|
13
13
|
}
|
|
14
|
-
function setRedisClient(client, grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS) {
|
|
15
|
-
AuthorizationService.redisClient = client;
|
|
16
|
-
if (grantedFeatureRedisExpirationInSeconds && grantedFeatureRedisExpirationInSeconds > 0) {
|
|
17
|
-
AuthorizationService.grantedFeatureRedisExpirationInSeconds = grantedFeatureRedisExpirationInSeconds;
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
authorizationInternalService.logger.warn({ grantedFeatureRedisExpirationInSeconds }, 'Invalid input for grantedFeatureRedisExpirationInSeconds, must be positive number. using default ttl.');
|
|
21
|
-
AuthorizationService.grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
14
|
class AuthorizationService {
|
|
25
15
|
static redisClient;
|
|
26
16
|
static grantedFeatureRedisExpirationInSeconds;
|
|
@@ -36,27 +26,27 @@ class AuthorizationService {
|
|
|
36
26
|
}
|
|
37
27
|
}
|
|
38
28
|
static async isUserGrantedWithFeature(accountId, userId, featureName, options = {}) {
|
|
39
|
-
|
|
29
|
+
const cachedKey = this.getCachedKeyName(userId, featureName);
|
|
40
30
|
const shouldSkipCache = options.shouldSkipCache ?? false;
|
|
41
31
|
if (this.redisClient && !shouldSkipCache) {
|
|
42
|
-
|
|
32
|
+
const grantedFeatureValue = await this.redisClient.get(cachedKey);
|
|
43
33
|
if (!(grantedFeatureValue === undefined || grantedFeatureValue === null)) {
|
|
44
34
|
// redis returns the value as string
|
|
45
35
|
return grantedFeatureValue === 'true';
|
|
46
36
|
}
|
|
47
37
|
}
|
|
48
|
-
|
|
38
|
+
const grantedFeatureValue = await this.fetchIsUserGrantedWithFeature(featureName, accountId, userId);
|
|
49
39
|
if (this.redisClient) {
|
|
50
40
|
await this.redisClient.set(cachedKey, grantedFeatureValue, 'EX', this.grantedFeatureRedisExpirationInSeconds);
|
|
51
41
|
}
|
|
52
42
|
return grantedFeatureValue;
|
|
53
43
|
}
|
|
54
44
|
static async fetchIsUserGrantedWithFeature(featureName, accountId, userId) {
|
|
55
|
-
|
|
45
|
+
const authorizationObject = {
|
|
56
46
|
action: featureName,
|
|
57
47
|
resource_type: 'feature',
|
|
58
48
|
};
|
|
59
|
-
|
|
49
|
+
const authorizeResponsePromise = await this.isAuthorized(accountId, userId, [authorizationObject]);
|
|
60
50
|
return authorizeResponsePromise.isAuthorized;
|
|
61
51
|
}
|
|
62
52
|
static getCachedKeyName(userId, featureName) {
|
|
@@ -148,6 +138,16 @@ class AuthorizationService {
|
|
|
148
138
|
return { isAuthorized: true };
|
|
149
139
|
}
|
|
150
140
|
}
|
|
141
|
+
function setRedisClient(client, grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS) {
|
|
142
|
+
AuthorizationService.redisClient = client;
|
|
143
|
+
if (grantedFeatureRedisExpirationInSeconds && grantedFeatureRedisExpirationInSeconds > 0) {
|
|
144
|
+
AuthorizationService.grantedFeatureRedisExpirationInSeconds = grantedFeatureRedisExpirationInSeconds;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
authorizationInternalService.logger.warn({ grantedFeatureRedisExpirationInSeconds }, 'Invalid input for grantedFeatureRedisExpirationInSeconds, must be positive number. using default ttl.');
|
|
148
|
+
AuthorizationService.grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
151
|
function createAuthorizationParams(resources, action) {
|
|
152
152
|
const params = {
|
|
153
153
|
authorizationObjects: resources.map((resource) => {
|
|
@@ -6,7 +6,7 @@ const APP_NAME_HEADER_NAME = 'x-caller-app-name-from-sdk';
|
|
|
6
6
|
const FROM_SDK_HEADER_SUFFIX = `-from-sdk`;
|
|
7
7
|
let didSendFailureLogOnce = false;
|
|
8
8
|
function getAttributionsFromApi() {
|
|
9
|
-
|
|
9
|
+
const callerAppNameFromSdk = {
|
|
10
10
|
[APP_NAME_HEADER_NAME]: tryJsonParse(getEnvVariable(APP_NAME_VARIABLE_KEY)),
|
|
11
11
|
};
|
|
12
12
|
try {
|
|
@@ -15,7 +15,7 @@ function getAttributionsFromApi() {
|
|
|
15
15
|
return callerAppNameFromSdk;
|
|
16
16
|
}
|
|
17
17
|
const { runtimeAttributions } = tridentContext;
|
|
18
|
-
|
|
18
|
+
const runtimeAttributionsOutgoingHeaders = runtimeAttributions?.buildOutgoingHeaders('HTTP_INTERNAL');
|
|
19
19
|
if (!runtimeAttributionsOutgoingHeaders) {
|
|
20
20
|
return callerAppNameFromSdk;
|
|
21
21
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import chunk from 'lodash/chunk';
|
|
2
2
|
import { fetch } from '@mondaydotcomorg/monday-fetch';
|
|
3
|
-
import { AuthorizationInternalService, logger } from './authorization-internal-service.mjs';
|
|
4
|
-
import { getAttributionsFromApi } from './attributions-service.mjs';
|
|
5
3
|
import { Api } from '@mondaydotcomorg/trident-backend-api';
|
|
6
4
|
import { sendToSns, getTopicAttributes } from '@mondaydotcomorg/monday-sns';
|
|
5
|
+
import { AuthorizationInternalService, logger } from './authorization-internal-service.mjs';
|
|
6
|
+
import { getAttributionsFromApi } from './attributions-service.mjs';
|
|
7
7
|
import { ASYNC_RESOURCE_ATTRIBUTES_MAX_OPERATIONS_PER_MESSAGE, RESOURCE_ATTRIBUTES_SNS_ARN_SECRET_NAME, RESOURCE_ATTRIBUTES_SNS_UPDATE_OPERATION_MESSAGE_KIND } from './constants/sns.mjs';
|
|
8
8
|
|
|
9
9
|
class AuthorizationAttributesService {
|
|
10
|
-
static LOG_TAG =
|
|
10
|
+
static LOG_TAG = 'authorization_attributes';
|
|
11
11
|
/**
|
|
12
12
|
* Upsert resource attributes synchronously, performing http call to the authorization MS to assign the given attributes to the given resource.
|
|
13
13
|
* @param accountId
|
|
@@ -84,14 +84,14 @@ class AuthorizationAttributesService {
|
|
|
84
84
|
callerAppName: appName,
|
|
85
85
|
callerActionIdentifier: callerActionIdentifier,
|
|
86
86
|
operations: operations,
|
|
87
|
-
}
|
|
87
|
+
},
|
|
88
88
|
};
|
|
89
89
|
try {
|
|
90
90
|
await sendToSns(payload, topicArn);
|
|
91
91
|
return operations;
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
94
|
-
logger.error({ error, tag: this.LOG_TAG },
|
|
94
|
+
logger.error({ error, tag: this.LOG_TAG }, 'Authorization resource attributes async update: failed to send operations to SNS');
|
|
95
95
|
return [];
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -122,14 +122,14 @@ class AuthorizationAttributesService {
|
|
|
122
122
|
try {
|
|
123
123
|
const requestedTopicArn = this.getSnsTopicArn();
|
|
124
124
|
const attributes = await getTopicAttributes(requestedTopicArn);
|
|
125
|
-
const isHealthy = !(!attributes || !(
|
|
125
|
+
const isHealthy = !(!attributes || !('TopicArn' in attributes) || attributes.TopicArn !== requestedTopicArn);
|
|
126
126
|
if (!isHealthy) {
|
|
127
|
-
logger.error({ requestedTopicArn, snsReturnedAttributes: attributes, tag: this.LOG_TAG },
|
|
127
|
+
logger.error({ requestedTopicArn, snsReturnedAttributes: attributes, tag: this.LOG_TAG }, 'authorization-attributes-service failed in health check');
|
|
128
128
|
}
|
|
129
129
|
return isHealthy;
|
|
130
130
|
}
|
|
131
131
|
catch (error) {
|
|
132
|
-
logger.error({ error, tag: this.LOG_TAG },
|
|
132
|
+
logger.error({ error, tag: this.LOG_TAG }, 'authorization-attributes-service got error during health check');
|
|
133
133
|
return false;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Request } from 'express';
|
|
2
1
|
import { fetch, MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
|
|
2
|
+
import type { Request } from 'express';
|
|
3
3
|
export declare const logger: import("bunyan");
|
|
4
4
|
export declare class AuthorizationInternalService {
|
|
5
5
|
static skipAuthorization(requset: Request): void;
|
|
@@ -6,6 +6,7 @@ const defaultMondayFetchOptions = {
|
|
|
6
6
|
retries: 3,
|
|
7
7
|
callback: logOnFetchFail,
|
|
8
8
|
};
|
|
9
|
+
const logger = MondayLogger.getLogger();
|
|
9
10
|
function logOnFetchFail(retriesLeft, error) {
|
|
10
11
|
if (retriesLeft == 0) {
|
|
11
12
|
logger.error({ retriesLeft, error }, 'Authorization attempt failed due to network issues');
|
|
@@ -15,7 +16,6 @@ function logOnFetchFail(retriesLeft, error) {
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
let mondayFetchOptions = defaultMondayFetchOptions;
|
|
18
|
-
const logger = MondayLogger.getLogger();
|
|
19
19
|
class AuthorizationInternalService {
|
|
20
20
|
static skipAuthorization(requset) {
|
|
21
21
|
requset.authorizationSkipPerformed = true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { NextFunction } from 'express';
|
|
2
1
|
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;
|
|
@@ -7,7 +7,6 @@ export interface AuthorizeResponse {
|
|
|
7
7
|
unauthorizedObjects?: AuthorizationObject[];
|
|
8
8
|
}
|
|
9
9
|
export declare function setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
|
|
10
|
-
export declare function setRedisClient(client: any, grantedFeatureRedisExpirationInSeconds?: number): void;
|
|
11
10
|
export declare class AuthorizationService {
|
|
12
11
|
static redisClient?: any;
|
|
13
12
|
static grantedFeatureRedisExpirationInSeconds?: number;
|
|
@@ -27,3 +26,4 @@ export declare class AuthorizationService {
|
|
|
27
26
|
private static isAuthorizedSingular;
|
|
28
27
|
private static isAuthorizedMultiple;
|
|
29
28
|
}
|
|
29
|
+
export declare function setRedisClient(client: any, grantedFeatureRedisExpirationInSeconds?: number): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mapKeys, snakeCase, camelCase } from 'lodash';
|
|
2
1
|
import { performance } from 'perf_hooks';
|
|
2
|
+
import { mapKeys, snakeCase, camelCase } from 'lodash';
|
|
3
3
|
import { fetch } from '@mondaydotcomorg/monday-fetch';
|
|
4
4
|
import { sendAuthorizationChecksPerRequestMetric, sendAuthorizationCheckResponseTimeMetric } from './prometheus-service.mjs';
|
|
5
5
|
import { AuthorizationInternalService, logger } from './authorization-internal-service.mjs';
|
|
@@ -9,16 +9,6 @@ const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
|
|
|
9
9
|
function setRequestFetchOptions(customMondayFetchOptions) {
|
|
10
10
|
AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
|
|
11
11
|
}
|
|
12
|
-
function setRedisClient(client, grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS) {
|
|
13
|
-
AuthorizationService.redisClient = client;
|
|
14
|
-
if (grantedFeatureRedisExpirationInSeconds && grantedFeatureRedisExpirationInSeconds > 0) {
|
|
15
|
-
AuthorizationService.grantedFeatureRedisExpirationInSeconds = grantedFeatureRedisExpirationInSeconds;
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
logger.warn({ grantedFeatureRedisExpirationInSeconds }, 'Invalid input for grantedFeatureRedisExpirationInSeconds, must be positive number. using default ttl.');
|
|
19
|
-
AuthorizationService.grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
12
|
class AuthorizationService {
|
|
23
13
|
static redisClient;
|
|
24
14
|
static grantedFeatureRedisExpirationInSeconds;
|
|
@@ -34,27 +24,27 @@ class AuthorizationService {
|
|
|
34
24
|
}
|
|
35
25
|
}
|
|
36
26
|
static async isUserGrantedWithFeature(accountId, userId, featureName, options = {}) {
|
|
37
|
-
|
|
27
|
+
const cachedKey = this.getCachedKeyName(userId, featureName);
|
|
38
28
|
const shouldSkipCache = options.shouldSkipCache ?? false;
|
|
39
29
|
if (this.redisClient && !shouldSkipCache) {
|
|
40
|
-
|
|
30
|
+
const grantedFeatureValue = await this.redisClient.get(cachedKey);
|
|
41
31
|
if (!(grantedFeatureValue === undefined || grantedFeatureValue === null)) {
|
|
42
32
|
// redis returns the value as string
|
|
43
33
|
return grantedFeatureValue === 'true';
|
|
44
34
|
}
|
|
45
35
|
}
|
|
46
|
-
|
|
36
|
+
const grantedFeatureValue = await this.fetchIsUserGrantedWithFeature(featureName, accountId, userId);
|
|
47
37
|
if (this.redisClient) {
|
|
48
38
|
await this.redisClient.set(cachedKey, grantedFeatureValue, 'EX', this.grantedFeatureRedisExpirationInSeconds);
|
|
49
39
|
}
|
|
50
40
|
return grantedFeatureValue;
|
|
51
41
|
}
|
|
52
42
|
static async fetchIsUserGrantedWithFeature(featureName, accountId, userId) {
|
|
53
|
-
|
|
43
|
+
const authorizationObject = {
|
|
54
44
|
action: featureName,
|
|
55
45
|
resource_type: 'feature',
|
|
56
46
|
};
|
|
57
|
-
|
|
47
|
+
const authorizeResponsePromise = await this.isAuthorized(accountId, userId, [authorizationObject]);
|
|
58
48
|
return authorizeResponsePromise.isAuthorized;
|
|
59
49
|
}
|
|
60
50
|
static getCachedKeyName(userId, featureName) {
|
|
@@ -146,6 +136,16 @@ class AuthorizationService {
|
|
|
146
136
|
return { isAuthorized: true };
|
|
147
137
|
}
|
|
148
138
|
}
|
|
139
|
+
function setRedisClient(client, grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS) {
|
|
140
|
+
AuthorizationService.redisClient = client;
|
|
141
|
+
if (grantedFeatureRedisExpirationInSeconds && grantedFeatureRedisExpirationInSeconds > 0) {
|
|
142
|
+
AuthorizationService.grantedFeatureRedisExpirationInSeconds = grantedFeatureRedisExpirationInSeconds;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
logger.warn({ grantedFeatureRedisExpirationInSeconds }, 'Invalid input for grantedFeatureRedisExpirationInSeconds, must be positive number. using default ttl.');
|
|
146
|
+
AuthorizationService.grantedFeatureRedisExpirationInSeconds = GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
149
|
function createAuthorizationParams(resources, action) {
|
|
150
150
|
const params = {
|
|
151
151
|
authorizationObjects: resources.map((resource) => {
|
|
@@ -31,7 +31,9 @@ function sendAuthorizationChecksPerRequestMetric(responseStatus, amountOfAuthori
|
|
|
31
31
|
authorizationChecksPerRequestMetric.labels(responseStatus).observe(amountOfAuthorizationObjects);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
catch (e) {
|
|
34
|
+
catch (e) {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
35
37
|
}
|
|
36
38
|
function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, responseStatus, time) {
|
|
37
39
|
try {
|
|
@@ -39,7 +41,9 @@ function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthor
|
|
|
39
41
|
authorizationCheckResponseTimeMetric.labels(resourceType, action, isAuthorized, responseStatus).observe(time);
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
|
-
catch (e) {
|
|
44
|
+
catch (e) {
|
|
45
|
+
// ignore
|
|
46
|
+
}
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
export { METRICS, getMetricsManager, sendAuthorizationCheckResponseTimeMetric, sendAuthorizationChecksPerRequestMetric, setPrometheus };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Action, BaseRequest, BaseResponse, ContextGetter, Resource, ResourceGetter } from '../types/general';
|
|
2
|
+
import type { NextFunction } from 'express';
|
|
3
3
|
export type TestPermittedAction = {
|
|
4
4
|
accountId: number;
|
|
5
5
|
userId: number;
|
|
@@ -10,20 +10,20 @@ const clearTestPermittedActions = () => {
|
|
|
10
10
|
};
|
|
11
11
|
const isActionAuthorized = (accountId, userId, resources, action) => {
|
|
12
12
|
return {
|
|
13
|
-
isAuthorized: resources.every(
|
|
13
|
+
isAuthorized: resources.every(_ => {
|
|
14
14
|
return testPermittedActions.some(combination => {
|
|
15
|
-
return combination.accountId === accountId &&
|
|
15
|
+
return (combination.accountId === accountId &&
|
|
16
16
|
combination.userId === userId &&
|
|
17
17
|
combination.action === action &&
|
|
18
18
|
combination.resources.some(combinationResource => {
|
|
19
19
|
return resources.some(resource => {
|
|
20
|
-
return combinationResource.id === resource.id &&
|
|
20
|
+
return (combinationResource.id === resource.id &&
|
|
21
21
|
combinationResource.type === resource.type &&
|
|
22
|
-
JSON.stringify(combinationResource.wrapperData) === JSON.stringify(resource.wrapperData);
|
|
22
|
+
JSON.stringify(combinationResource.wrapperData) === JSON.stringify(resource.wrapperData));
|
|
23
23
|
});
|
|
24
|
-
});
|
|
24
|
+
}));
|
|
25
25
|
});
|
|
26
|
-
})
|
|
26
|
+
}),
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
29
|
const getTestAuthorizationMiddleware = (action, resourceGetter, contextGetter) => {
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
2
|
export interface Resource {
|
|
3
3
|
id?: number;
|
|
4
4
|
type: string;
|
|
5
5
|
wrapperData?: object;
|
|
6
6
|
}
|
|
7
7
|
export type Action = string;
|
|
8
|
-
export type ResourceGetter = (request: BaseRequest) => Resource[];
|
|
9
8
|
export interface Context {
|
|
10
9
|
accountId: number;
|
|
11
10
|
userId: number;
|
|
12
11
|
}
|
|
13
|
-
export type ContextGetter = (request: BaseRequest) => Context;
|
|
14
12
|
export interface AuthorizationObject {
|
|
15
13
|
resource_id?: Resource['id'];
|
|
16
14
|
resource_type: Resource['type'];
|
|
@@ -20,11 +18,15 @@ export interface AuthorizationObject {
|
|
|
20
18
|
export interface AuthorizationParams {
|
|
21
19
|
authorizationObjects: AuthorizationObject[];
|
|
22
20
|
}
|
|
23
|
-
type BasicObject = {
|
|
21
|
+
type BasicObject = {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
};
|
|
24
24
|
export type BaseParameters = BasicObject;
|
|
25
25
|
export type BaseResponseBody = BasicObject;
|
|
26
26
|
export type BaseBodyParameters = BasicObject;
|
|
27
27
|
export type BaseQueryParameters = BasicObject;
|
|
28
28
|
export type BaseRequest = Request<BaseParameters, BaseResponseBody, BaseBodyParameters, BaseQueryParameters>;
|
|
29
29
|
export type BaseResponse = Response<BaseResponseBody>;
|
|
30
|
+
export type ResourceGetter = (request: BaseRequest) => Resource[];
|
|
31
|
+
export type ContextGetter = (request: BaseRequest) => Context;
|
|
30
32
|
export {};
|
|
@@ -33,7 +33,9 @@ function sendAuthorizationChecksPerRequestMetric(responseStatus, amountOfAuthori
|
|
|
33
33
|
authorizationChecksPerRequestMetric.labels(responseStatus).observe(amountOfAuthorizationObjects);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
catch (e) {
|
|
36
|
+
catch (e) {
|
|
37
|
+
// ignore
|
|
38
|
+
}
|
|
37
39
|
}
|
|
38
40
|
function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, responseStatus, time) {
|
|
39
41
|
try {
|
|
@@ -41,7 +43,9 @@ function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthor
|
|
|
41
43
|
authorizationCheckResponseTimeMetric.labels(resourceType, action, isAuthorized, responseStatus).observe(time);
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
|
-
catch (e) {
|
|
46
|
+
catch (e) {
|
|
47
|
+
// ignore
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
exports.METRICS = METRICS;
|
package/dist/testKit/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Action, BaseRequest, BaseResponse, ContextGetter, Resource, ResourceGetter } from '../types/general';
|
|
2
|
+
import type { NextFunction } from 'express';
|
|
3
3
|
export type TestPermittedAction = {
|
|
4
4
|
accountId: number;
|
|
5
5
|
userId: number;
|
package/dist/testKit/index.js
CHANGED
|
@@ -12,20 +12,20 @@ const clearTestPermittedActions = () => {
|
|
|
12
12
|
};
|
|
13
13
|
const isActionAuthorized = (accountId, userId, resources, action) => {
|
|
14
14
|
return {
|
|
15
|
-
isAuthorized: resources.every(
|
|
15
|
+
isAuthorized: resources.every(_ => {
|
|
16
16
|
return testPermittedActions.some(combination => {
|
|
17
|
-
return combination.accountId === accountId &&
|
|
17
|
+
return (combination.accountId === accountId &&
|
|
18
18
|
combination.userId === userId &&
|
|
19
19
|
combination.action === action &&
|
|
20
20
|
combination.resources.some(combinationResource => {
|
|
21
21
|
return resources.some(resource => {
|
|
22
|
-
return combinationResource.id === resource.id &&
|
|
22
|
+
return (combinationResource.id === resource.id &&
|
|
23
23
|
combinationResource.type === resource.type &&
|
|
24
|
-
JSON.stringify(combinationResource.wrapperData) === JSON.stringify(resource.wrapperData);
|
|
24
|
+
JSON.stringify(combinationResource.wrapperData) === JSON.stringify(resource.wrapperData));
|
|
25
25
|
});
|
|
26
|
-
});
|
|
26
|
+
}));
|
|
27
27
|
});
|
|
28
|
-
})
|
|
28
|
+
}),
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
31
|
const getTestAuthorizationMiddleware = (action, resourceGetter, contextGetter) => {
|
package/dist/types/general.d.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
2
|
export interface Resource {
|
|
3
3
|
id?: number;
|
|
4
4
|
type: string;
|
|
5
5
|
wrapperData?: object;
|
|
6
6
|
}
|
|
7
7
|
export type Action = string;
|
|
8
|
-
export type ResourceGetter = (request: BaseRequest) => Resource[];
|
|
9
8
|
export interface Context {
|
|
10
9
|
accountId: number;
|
|
11
10
|
userId: number;
|
|
12
11
|
}
|
|
13
|
-
export type ContextGetter = (request: BaseRequest) => Context;
|
|
14
12
|
export interface AuthorizationObject {
|
|
15
13
|
resource_id?: Resource['id'];
|
|
16
14
|
resource_type: Resource['type'];
|
|
@@ -20,11 +18,15 @@ export interface AuthorizationObject {
|
|
|
20
18
|
export interface AuthorizationParams {
|
|
21
19
|
authorizationObjects: AuthorizationObject[];
|
|
22
20
|
}
|
|
23
|
-
type BasicObject = {
|
|
21
|
+
type BasicObject = {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
};
|
|
24
24
|
export type BaseParameters = BasicObject;
|
|
25
25
|
export type BaseResponseBody = BasicObject;
|
|
26
26
|
export type BaseBodyParameters = BasicObject;
|
|
27
27
|
export type BaseQueryParameters = BasicObject;
|
|
28
28
|
export type BaseRequest = Request<BaseParameters, BaseResponseBody, BaseBodyParameters, BaseQueryParameters>;
|
|
29
29
|
export type BaseResponse = Response<BaseResponseBody>;
|
|
30
|
+
export type ResourceGetter = (request: BaseRequest) => Resource[];
|
|
31
|
+
export type ContextGetter = (request: BaseRequest) => Context;
|
|
30
32
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mondaydotcomorg/monday-authorization",
|
|
3
|
-
"version": "1.1.9-featuremosheauthorizationesm.
|
|
3
|
+
"version": "1.1.9-featuremosheauthorizationesm.3696+a0b925038",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
"@mondaydotcomorg/monday-jwt": "^3.0.14",
|
|
24
24
|
"@mondaydotcomorg/monday-logger": "^4.0.11",
|
|
25
25
|
"@mondaydotcomorg/monday-sns": "^1.0.6",
|
|
26
|
-
"@mondaydotcomorg/trident-backend-api": "^0.
|
|
27
|
-
"@types/lodash": "^4.17.10",
|
|
26
|
+
"@mondaydotcomorg/trident-backend-api": "^0.24.3",
|
|
28
27
|
"lodash": "^4.17.21",
|
|
29
28
|
"node-fetch": "^2.6.7",
|
|
30
29
|
"on-headers": "^1.0.2",
|
|
@@ -33,27 +32,26 @@
|
|
|
33
32
|
"devDependencies": {
|
|
34
33
|
"@mondaydotcomorg/trident-library": "^0.6.53",
|
|
35
34
|
"@types/express": "^4.17.20",
|
|
36
|
-
"@types/
|
|
37
|
-
"@types/mocha": "^8.2.2",
|
|
35
|
+
"@types/lodash": "^4.17.10",
|
|
38
36
|
"@types/on-headers": "^1.0.0",
|
|
39
37
|
"@types/supertest": "^2.0.11",
|
|
40
38
|
"express": "^4.17.1",
|
|
41
39
|
"ioredis": "^5.2.4",
|
|
42
40
|
"ioredis-mock": "^8.2.2",
|
|
43
|
-
"jest": "^27.5.1",
|
|
44
|
-
"mocha": "^9.0.1",
|
|
45
41
|
"supertest": "^6.1.3",
|
|
46
|
-
"
|
|
47
|
-
"tsconfig-paths": "^3.9.0",
|
|
48
|
-
"typescript": "^5.1.6"
|
|
42
|
+
"typescript": "^5.2.2"
|
|
49
43
|
},
|
|
50
44
|
"files": [
|
|
51
45
|
"dist/"
|
|
52
46
|
],
|
|
47
|
+
"eslintConfig": {
|
|
48
|
+
"extends": "@mondaydotcomorg/trident-library",
|
|
49
|
+
"root": true
|
|
50
|
+
},
|
|
53
51
|
"trident": {
|
|
54
52
|
"build": {
|
|
55
53
|
"esmMjsRename": true
|
|
56
54
|
}
|
|
57
55
|
},
|
|
58
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "a0b925038818937a00502a1f81800ec3b4789262"
|
|
59
57
|
}
|