@mondaydotcomorg/monday-authorization 3.2.3 → 3.3.0-feature-bashanye-navigate-can-action-in-scope-to-graph-8132586
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/README.md +12 -0
- package/dist/authorization-service.d.ts +8 -0
- package/dist/authorization-service.d.ts.map +1 -1
- package/dist/authorization-service.js +133 -21
- package/dist/esm/authorization-service.d.ts +8 -0
- package/dist/esm/authorization-service.d.ts.map +1 -1
- package/dist/esm/authorization-service.mjs +134 -22
- package/dist/esm/prometheus-service.d.ts +4 -1
- package/dist/esm/prometheus-service.d.ts.map +1 -1
- package/dist/esm/prometheus-service.mjs +17 -5
- package/dist/prometheus-service.d.ts +4 -1
- package/dist/prometheus-service.d.ts.map +1 -1
- package/dist/prometheus-service.js +19 -4
- 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).
|
|
@@ -30,6 +30,14 @@ export declare class AuthorizationService {
|
|
|
30
30
|
static canActionInScope(accountId: number, userId: number, action: string, scope: ScopeOptions): Promise<ScopedActionPermit>;
|
|
31
31
|
private static getProfile;
|
|
32
32
|
static canActionInScopeMultiple(accountId: number, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
|
|
33
|
+
private static buildScopedActionsPayload;
|
|
34
|
+
private static scopeToResource;
|
|
35
|
+
private static buildGraphRequestBody;
|
|
36
|
+
private static fetchGraphIsAllowed;
|
|
37
|
+
private static mapGraphResponse;
|
|
38
|
+
private static fetchPlatformCanActions;
|
|
39
|
+
private static toCamelCase;
|
|
40
|
+
private static mapPlatformResponse;
|
|
33
41
|
private static isAuthorizedSingular;
|
|
34
42
|
private static isAuthorizedMultiple;
|
|
35
43
|
}
|
|
@@ -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;
|
|
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;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;AAsCD,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;IAyCxC,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAMxC,OAAO,CAAC,MAAM,CAAC,eAAe;IAmB9B,OAAO,CAAC,MAAM,CAAC,qBAAqB;mBAyBf,mBAAmB;IAoCxC,OAAO,CAAC,MAAM,CAAC,gBAAgB;mBAoBV,uBAAuB;IAuC5C,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;mBAiBb,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"}
|
|
@@ -20,9 +20,11 @@ const mapKeys__default = /*#__PURE__*/_interopDefault(mapKeys);
|
|
|
20
20
|
const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
|
|
21
21
|
const PLATFORM_AUTHORIZE_PATH = '/internal_ms/authorization/authorize';
|
|
22
22
|
const PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH = '/internal_ms/authorization/can_actions_in_scopes';
|
|
23
|
+
const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
|
|
23
24
|
const ALLOWED_SDK_PLATFORM_PROFILES_KEY = 'allowed-sdk-platform-profiles';
|
|
24
25
|
const IN_RELEASE_SDK_PLATFORM_PROFILES_KEY = 'in-release-sdk-platform-profile';
|
|
25
26
|
const PLATFORM_PROFILE_RELEASE_FF = 'sdk-platform-profiles';
|
|
27
|
+
const NAVIGATE_CAN_ACTION_IN_SCOPE_TO_GRAPH_FF = 'navigate-can-action-in-scope-to-graph';
|
|
26
28
|
function setRequestFetchOptions(customMondayFetchOptions) {
|
|
27
29
|
authorizationInternalService.AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
|
|
28
30
|
}
|
|
@@ -95,16 +97,128 @@ class AuthorizationService {
|
|
|
95
97
|
return attributionsService.PlatformProfile.INTERNAL;
|
|
96
98
|
}
|
|
97
99
|
static async canActionInScopeMultiple(accountId, userId, scopedActions) {
|
|
98
|
-
const
|
|
100
|
+
const shouldNavigateToGraph = Boolean(this.igniteClient?.isReleased(NAVIGATE_CAN_ACTION_IN_SCOPE_TO_GRAPH_FF, { accountId, userId }));
|
|
99
101
|
const internalAuthToken = authorizationInternalService.AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
+
const startTime = perf_hooks.performance.now();
|
|
103
|
+
let scopedActionResponseObjects;
|
|
104
|
+
if (shouldNavigateToGraph) {
|
|
105
|
+
const response = await this.fetchGraphIsAllowed(internalAuthToken, scopedActions);
|
|
106
|
+
scopedActionResponseObjects = this.mapGraphResponse(scopedActions, userId, response);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const profile = this.getProfile(accountId, userId);
|
|
110
|
+
const scopedActionsPayload = this.buildScopedActionsPayload(scopedActions);
|
|
111
|
+
const platformResponse = await this.fetchPlatformCanActions(profile, internalAuthToken, userId, scopedActionsPayload);
|
|
112
|
+
scopedActionResponseObjects = this.mapPlatformResponse(platformResponse);
|
|
113
|
+
}
|
|
114
|
+
const endTime = perf_hooks.performance.now();
|
|
115
|
+
const time = endTime - startTime;
|
|
116
|
+
const apiType = shouldNavigateToGraph ? 'graph' : 'platform';
|
|
117
|
+
for (const obj of scopedActionResponseObjects) {
|
|
118
|
+
const { action } = obj.scopedAction;
|
|
119
|
+
const resource_type = this.scopeToResource(obj.scopedAction.scope).resourceType;
|
|
120
|
+
const isAuthorized = obj.permit.can;
|
|
121
|
+
prometheusService.sendAuthorizationCheckResponseTimeMetric(resource_type, action, isAuthorized, 200, time, apiType);
|
|
122
|
+
if (obj.permit.can) ;
|
|
123
|
+
}
|
|
124
|
+
return scopedActionResponseObjects;
|
|
125
|
+
}
|
|
126
|
+
static buildScopedActionsPayload(scopedActions) {
|
|
127
|
+
return scopedActions.map(scopedAction => {
|
|
128
|
+
return { ...scopedAction, scope: mapKeys__default.default(scopedAction.scope, (_, key) => snakeCase__default.default(key)) };
|
|
102
129
|
});
|
|
130
|
+
}
|
|
131
|
+
static scopeToResource(scope) {
|
|
132
|
+
if ('workspaceId' in scope) {
|
|
133
|
+
return { resourceType: 'workspace', resourceId: scope.workspaceId };
|
|
134
|
+
}
|
|
135
|
+
if ('boardId' in scope) {
|
|
136
|
+
return { resourceType: 'board', resourceId: scope.boardId };
|
|
137
|
+
}
|
|
138
|
+
if ('pulseId' in scope) {
|
|
139
|
+
return { resourceType: 'pulse', resourceId: scope.pulseId };
|
|
140
|
+
}
|
|
141
|
+
if ('accountProductId' in scope) {
|
|
142
|
+
return { resourceType: 'account_product', resourceId: scope.accountProductId };
|
|
143
|
+
}
|
|
144
|
+
if ('accountId' in scope) {
|
|
145
|
+
return { resourceType: 'account', resourceId: scope.accountId };
|
|
146
|
+
}
|
|
147
|
+
throw new Error('Unsupported scope provided');
|
|
148
|
+
}
|
|
149
|
+
static buildGraphRequestBody(scopedActions) {
|
|
150
|
+
const resourcesAccumulator = {};
|
|
151
|
+
for (const { action, scope } of scopedActions) {
|
|
152
|
+
const { resourceType, resourceId } = this.scopeToResource(scope);
|
|
153
|
+
if (!resourcesAccumulator[resourceType]) {
|
|
154
|
+
resourcesAccumulator[resourceType] = {};
|
|
155
|
+
}
|
|
156
|
+
if (!resourcesAccumulator[resourceType][resourceId]) {
|
|
157
|
+
resourcesAccumulator[resourceType][resourceId] = new Set();
|
|
158
|
+
}
|
|
159
|
+
resourcesAccumulator[resourceType][resourceId].add(action);
|
|
160
|
+
}
|
|
161
|
+
const resourcesPayload = {};
|
|
162
|
+
for (const [resourceType, idMap] of Object.entries(resourcesAccumulator)) {
|
|
163
|
+
resourcesPayload[resourceType] = {};
|
|
164
|
+
for (const [idStr, actionsSet] of Object.entries(idMap)) {
|
|
165
|
+
const idNum = Number(idStr);
|
|
166
|
+
resourcesPayload[resourceType][idNum] = Array.from(actionsSet);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return resourcesPayload;
|
|
170
|
+
}
|
|
171
|
+
static async fetchGraphIsAllowed(internalAuthToken, scopedActions) {
|
|
172
|
+
const httpClient = tridentBackendApi.Api.getPart('httpClient');
|
|
173
|
+
const attributionHeaders = attributionsService.getAttributionsFromApi();
|
|
174
|
+
const bodyPayload = this.buildGraphRequestBody(scopedActions);
|
|
175
|
+
try {
|
|
176
|
+
const response = await httpClient.fetch({
|
|
177
|
+
url: {
|
|
178
|
+
appName: 'authorization-graph',
|
|
179
|
+
path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
|
|
180
|
+
},
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: {
|
|
183
|
+
Authorization: internalAuthToken,
|
|
184
|
+
'Content-Type': 'application/json',
|
|
185
|
+
...attributionHeaders,
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify(bodyPayload),
|
|
188
|
+
}, {
|
|
189
|
+
timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
|
|
190
|
+
retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
|
|
191
|
+
});
|
|
192
|
+
prometheusService.setGraphAvailability(true);
|
|
193
|
+
return response;
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
197
|
+
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
198
|
+
prometheusService.incrementAuthorizationError(this.scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status);
|
|
199
|
+
}
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
static mapGraphResponse(scopedActions, userId, graphResponse) {
|
|
204
|
+
const resources = graphResponse ?? {};
|
|
205
|
+
return scopedActions.map(scopedAction => {
|
|
206
|
+
const { action, scope } = scopedAction;
|
|
207
|
+
const { resourceType, resourceId } = this.scopeToResource(scope);
|
|
208
|
+
const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
|
|
209
|
+
const permit = {
|
|
210
|
+
can: permissionResult?.can ?? false,
|
|
211
|
+
reason: { key: permissionResult?.reason ?? 'unknown' },
|
|
212
|
+
technicalReason: 0,
|
|
213
|
+
};
|
|
214
|
+
return { scopedAction, permit };
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
static async fetchPlatformCanActions(profile, internalAuthToken, userId, scopedActionsPayload) {
|
|
103
218
|
const attributionHeaders = attributionsService.getAttributionsFromApi();
|
|
104
219
|
const httpClient = tridentBackendApi.Api.getPart('httpClient');
|
|
105
|
-
let response;
|
|
106
220
|
try {
|
|
107
|
-
response = await httpClient.fetch({
|
|
221
|
+
const response = await httpClient.fetch({
|
|
108
222
|
url: {
|
|
109
223
|
appName: 'platform',
|
|
110
224
|
path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
|
|
@@ -116,40 +230,38 @@ class AuthorizationService {
|
|
|
116
230
|
'Content-Type': 'application/json',
|
|
117
231
|
...attributionHeaders,
|
|
118
232
|
},
|
|
119
|
-
body: JSON.stringify({
|
|
120
|
-
user_id: userId,
|
|
121
|
-
scoped_actions: scopedActionsPayload,
|
|
122
|
-
}),
|
|
233
|
+
body: JSON.stringify({ user_id: userId, scoped_actions: scopedActionsPayload }),
|
|
123
234
|
}, {
|
|
124
235
|
timeout: authorizationInternalService.AuthorizationInternalService.getRequestTimeout(),
|
|
125
236
|
retryPolicy: authorizationInternalService.AuthorizationInternalService.getRetriesPolicy(),
|
|
126
237
|
});
|
|
238
|
+
return response;
|
|
127
239
|
}
|
|
128
240
|
catch (err) {
|
|
129
241
|
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
130
242
|
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
243
|
+
prometheusService.incrementAuthorizationError(this.scopeToResource(this.toCamelCase(scopedActionsPayload[0].scope)).resourceType, scopedActionsPayload[0].action, err.status);
|
|
131
244
|
}
|
|
132
|
-
|
|
133
|
-
throw err;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function toCamelCase(obj) {
|
|
137
|
-
return mapKeys__default.default(obj, (_, key) => camelCase__default.default(key));
|
|
245
|
+
throw err;
|
|
138
246
|
}
|
|
247
|
+
}
|
|
248
|
+
static toCamelCase(obj) {
|
|
249
|
+
return mapKeys__default.default(obj, (_, key) => camelCase__default.default(key));
|
|
250
|
+
}
|
|
251
|
+
static mapPlatformResponse(response) {
|
|
139
252
|
if (!response) {
|
|
140
253
|
authorizationInternalService.logger.error({ tag: 'authorization-service', response }, 'AuthorizationService: missing response');
|
|
141
254
|
throw new Error('AuthorizationService: missing response');
|
|
142
255
|
}
|
|
143
|
-
|
|
256
|
+
return response.result.map(responseObject => {
|
|
144
257
|
const { scopedAction, permit } = responseObject;
|
|
145
258
|
const { scope } = scopedAction;
|
|
146
259
|
return {
|
|
147
260
|
...responseObject,
|
|
148
|
-
scopedAction: { ...scopedAction, scope: toCamelCase(scope) },
|
|
149
|
-
permit: toCamelCase(permit),
|
|
261
|
+
scopedAction: { ...scopedAction, scope: this.toCamelCase(scope) },
|
|
262
|
+
permit: this.toCamelCase(permit),
|
|
150
263
|
};
|
|
151
264
|
});
|
|
152
|
-
return scopedActionsResponseObjects;
|
|
153
265
|
}
|
|
154
266
|
static async isAuthorizedSingular(accountId, userId, resources, action) {
|
|
155
267
|
const { authorizationObjects } = createAuthorizationParams(resources, action);
|
|
@@ -185,7 +297,7 @@ class AuthorizationService {
|
|
|
185
297
|
});
|
|
186
298
|
}
|
|
187
299
|
catch (err) {
|
|
188
|
-
if (err instanceof
|
|
300
|
+
if (err instanceof mondayFetchApi.HttpFetcherError) {
|
|
189
301
|
authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'isAuthorizedMultiple');
|
|
190
302
|
}
|
|
191
303
|
else {
|
|
@@ -204,7 +316,7 @@ class AuthorizationService {
|
|
|
204
316
|
if (!isAuthorized) {
|
|
205
317
|
unauthorizedObjects.push(authorizationObject);
|
|
206
318
|
}
|
|
207
|
-
prometheusService.sendAuthorizationCheckResponseTimeMetric(authorizationObject.resource_type, authorizationObject.action, isAuthorized, 200, time);
|
|
319
|
+
prometheusService.sendAuthorizationCheckResponseTimeMetric(authorizationObject.resource_type, authorizationObject.action, isAuthorized, 200, time, 'platform');
|
|
208
320
|
});
|
|
209
321
|
if (unauthorizedObjects.length > 0) {
|
|
210
322
|
authorizationInternalService.logger.info({
|
|
@@ -30,6 +30,14 @@ export declare class AuthorizationService {
|
|
|
30
30
|
static canActionInScope(accountId: number, userId: number, action: string, scope: ScopeOptions): Promise<ScopedActionPermit>;
|
|
31
31
|
private static getProfile;
|
|
32
32
|
static canActionInScopeMultiple(accountId: number, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
|
|
33
|
+
private static buildScopedActionsPayload;
|
|
34
|
+
private static scopeToResource;
|
|
35
|
+
private static buildGraphRequestBody;
|
|
36
|
+
private static fetchGraphIsAllowed;
|
|
37
|
+
private static mapGraphResponse;
|
|
38
|
+
private static fetchPlatformCanActions;
|
|
39
|
+
private static toCamelCase;
|
|
40
|
+
private static mapPlatformResponse;
|
|
33
41
|
private static isAuthorizedSingular;
|
|
34
42
|
private static isAuthorizedMultiple;
|
|
35
43
|
}
|
|
@@ -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;
|
|
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;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;AAsCD,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;IAyCxC,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAMxC,OAAO,CAAC,MAAM,CAAC,eAAe;IAmB9B,OAAO,CAAC,MAAM,CAAC,qBAAqB;mBAyBf,mBAAmB;IAoCxC,OAAO,CAAC,MAAM,CAAC,gBAAgB;mBAoBV,uBAAuB;IAuC5C,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;mBAiBb,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"}
|
|
@@ -5,16 +5,18 @@ import mapKeys from 'lodash/mapKeys.js';
|
|
|
5
5
|
import { Api } from '@mondaydotcomorg/trident-backend-api';
|
|
6
6
|
import { HttpFetcherError } from '@mondaydotcomorg/monday-fetch-api';
|
|
7
7
|
import { getIgniteClient } from '@mondaydotcomorg/ignite-sdk';
|
|
8
|
-
import { sendAuthorizationCheckResponseTimeMetric } from './prometheus-service.mjs';
|
|
8
|
+
import { sendAuthorizationCheckResponseTimeMetric, setGraphAvailability, incrementAuthorizationError } from './prometheus-service.mjs';
|
|
9
9
|
import { AuthorizationInternalService, logger } from './authorization-internal-service.mjs';
|
|
10
10
|
import { getProfile, PlatformProfile, getAttributionsFromApi } from './attributions-service.mjs';
|
|
11
11
|
|
|
12
12
|
const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
|
|
13
13
|
const PLATFORM_AUTHORIZE_PATH = '/internal_ms/authorization/authorize';
|
|
14
14
|
const PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH = '/internal_ms/authorization/can_actions_in_scopes';
|
|
15
|
+
const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
|
|
15
16
|
const ALLOWED_SDK_PLATFORM_PROFILES_KEY = 'allowed-sdk-platform-profiles';
|
|
16
17
|
const IN_RELEASE_SDK_PLATFORM_PROFILES_KEY = 'in-release-sdk-platform-profile';
|
|
17
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';
|
|
18
20
|
function setRequestFetchOptions(customMondayFetchOptions) {
|
|
19
21
|
AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
|
|
20
22
|
}
|
|
@@ -87,16 +89,128 @@ class AuthorizationService {
|
|
|
87
89
|
return PlatformProfile.INTERNAL;
|
|
88
90
|
}
|
|
89
91
|
static async canActionInScopeMultiple(accountId, userId, scopedActions) {
|
|
90
|
-
const
|
|
92
|
+
const shouldNavigateToGraph = Boolean(this.igniteClient?.isReleased(NAVIGATE_CAN_ACTION_IN_SCOPE_TO_GRAPH_FF, { accountId, userId }));
|
|
91
93
|
const internalAuthToken = AuthorizationInternalService.generateInternalAuthToken(accountId, userId);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
+
const startTime = performance.now();
|
|
95
|
+
let scopedActionResponseObjects;
|
|
96
|
+
if (shouldNavigateToGraph) {
|
|
97
|
+
const response = await this.fetchGraphIsAllowed(internalAuthToken, scopedActions);
|
|
98
|
+
scopedActionResponseObjects = this.mapGraphResponse(scopedActions, userId, response);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const profile = this.getProfile(accountId, userId);
|
|
102
|
+
const scopedActionsPayload = this.buildScopedActionsPayload(scopedActions);
|
|
103
|
+
const platformResponse = await this.fetchPlatformCanActions(profile, internalAuthToken, userId, scopedActionsPayload);
|
|
104
|
+
scopedActionResponseObjects = this.mapPlatformResponse(platformResponse);
|
|
105
|
+
}
|
|
106
|
+
const endTime = performance.now();
|
|
107
|
+
const time = endTime - startTime;
|
|
108
|
+
const apiType = shouldNavigateToGraph ? 'graph' : 'platform';
|
|
109
|
+
for (const obj of scopedActionResponseObjects) {
|
|
110
|
+
const { action } = obj.scopedAction;
|
|
111
|
+
const resource_type = this.scopeToResource(obj.scopedAction.scope).resourceType;
|
|
112
|
+
const isAuthorized = obj.permit.can;
|
|
113
|
+
sendAuthorizationCheckResponseTimeMetric(resource_type, action, isAuthorized, 200, time, apiType);
|
|
114
|
+
if (obj.permit.can) ;
|
|
115
|
+
}
|
|
116
|
+
return scopedActionResponseObjects;
|
|
117
|
+
}
|
|
118
|
+
static buildScopedActionsPayload(scopedActions) {
|
|
119
|
+
return scopedActions.map(scopedAction => {
|
|
120
|
+
return { ...scopedAction, scope: mapKeys(scopedAction.scope, (_, key) => snakeCase(key)) };
|
|
94
121
|
});
|
|
122
|
+
}
|
|
123
|
+
static scopeToResource(scope) {
|
|
124
|
+
if ('workspaceId' in scope) {
|
|
125
|
+
return { resourceType: 'workspace', resourceId: scope.workspaceId };
|
|
126
|
+
}
|
|
127
|
+
if ('boardId' in scope) {
|
|
128
|
+
return { resourceType: 'board', resourceId: scope.boardId };
|
|
129
|
+
}
|
|
130
|
+
if ('pulseId' in scope) {
|
|
131
|
+
return { resourceType: 'pulse', resourceId: scope.pulseId };
|
|
132
|
+
}
|
|
133
|
+
if ('accountProductId' in scope) {
|
|
134
|
+
return { resourceType: 'account_product', resourceId: scope.accountProductId };
|
|
135
|
+
}
|
|
136
|
+
if ('accountId' in scope) {
|
|
137
|
+
return { resourceType: 'account', resourceId: scope.accountId };
|
|
138
|
+
}
|
|
139
|
+
throw new Error('Unsupported scope provided');
|
|
140
|
+
}
|
|
141
|
+
static buildGraphRequestBody(scopedActions) {
|
|
142
|
+
const resourcesAccumulator = {};
|
|
143
|
+
for (const { action, scope } of scopedActions) {
|
|
144
|
+
const { resourceType, resourceId } = this.scopeToResource(scope);
|
|
145
|
+
if (!resourcesAccumulator[resourceType]) {
|
|
146
|
+
resourcesAccumulator[resourceType] = {};
|
|
147
|
+
}
|
|
148
|
+
if (!resourcesAccumulator[resourceType][resourceId]) {
|
|
149
|
+
resourcesAccumulator[resourceType][resourceId] = new Set();
|
|
150
|
+
}
|
|
151
|
+
resourcesAccumulator[resourceType][resourceId].add(action);
|
|
152
|
+
}
|
|
153
|
+
const resourcesPayload = {};
|
|
154
|
+
for (const [resourceType, idMap] of Object.entries(resourcesAccumulator)) {
|
|
155
|
+
resourcesPayload[resourceType] = {};
|
|
156
|
+
for (const [idStr, actionsSet] of Object.entries(idMap)) {
|
|
157
|
+
const idNum = Number(idStr);
|
|
158
|
+
resourcesPayload[resourceType][idNum] = Array.from(actionsSet);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return resourcesPayload;
|
|
162
|
+
}
|
|
163
|
+
static async fetchGraphIsAllowed(internalAuthToken, scopedActions) {
|
|
164
|
+
const httpClient = Api.getPart('httpClient');
|
|
165
|
+
const attributionHeaders = getAttributionsFromApi();
|
|
166
|
+
const bodyPayload = this.buildGraphRequestBody(scopedActions);
|
|
167
|
+
try {
|
|
168
|
+
const response = await httpClient.fetch({
|
|
169
|
+
url: {
|
|
170
|
+
appName: 'authorization-graph',
|
|
171
|
+
path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
|
|
172
|
+
},
|
|
173
|
+
method: 'POST',
|
|
174
|
+
headers: {
|
|
175
|
+
Authorization: internalAuthToken,
|
|
176
|
+
'Content-Type': 'application/json',
|
|
177
|
+
...attributionHeaders,
|
|
178
|
+
},
|
|
179
|
+
body: JSON.stringify(bodyPayload),
|
|
180
|
+
}, {
|
|
181
|
+
timeout: AuthorizationInternalService.getRequestTimeout(),
|
|
182
|
+
retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
|
|
183
|
+
});
|
|
184
|
+
setGraphAvailability(true);
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if (err instanceof HttpFetcherError) {
|
|
189
|
+
AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
190
|
+
incrementAuthorizationError(this.scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status);
|
|
191
|
+
}
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
static mapGraphResponse(scopedActions, userId, graphResponse) {
|
|
196
|
+
const resources = graphResponse ?? {};
|
|
197
|
+
return scopedActions.map(scopedAction => {
|
|
198
|
+
const { action, scope } = scopedAction;
|
|
199
|
+
const { resourceType, resourceId } = this.scopeToResource(scope);
|
|
200
|
+
const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
|
|
201
|
+
const permit = {
|
|
202
|
+
can: permissionResult?.can ?? false,
|
|
203
|
+
reason: { key: permissionResult?.reason ?? 'unknown' },
|
|
204
|
+
technicalReason: 0,
|
|
205
|
+
};
|
|
206
|
+
return { scopedAction, permit };
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
static async fetchPlatformCanActions(profile, internalAuthToken, userId, scopedActionsPayload) {
|
|
95
210
|
const attributionHeaders = getAttributionsFromApi();
|
|
96
211
|
const httpClient = Api.getPart('httpClient');
|
|
97
|
-
let response;
|
|
98
212
|
try {
|
|
99
|
-
response = await httpClient.fetch({
|
|
213
|
+
const response = await httpClient.fetch({
|
|
100
214
|
url: {
|
|
101
215
|
appName: 'platform',
|
|
102
216
|
path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
|
|
@@ -108,40 +222,38 @@ class AuthorizationService {
|
|
|
108
222
|
'Content-Type': 'application/json',
|
|
109
223
|
...attributionHeaders,
|
|
110
224
|
},
|
|
111
|
-
body: JSON.stringify({
|
|
112
|
-
user_id: userId,
|
|
113
|
-
scoped_actions: scopedActionsPayload,
|
|
114
|
-
}),
|
|
225
|
+
body: JSON.stringify({ user_id: userId, scoped_actions: scopedActionsPayload }),
|
|
115
226
|
}, {
|
|
116
227
|
timeout: AuthorizationInternalService.getRequestTimeout(),
|
|
117
228
|
retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
|
|
118
229
|
});
|
|
230
|
+
return response;
|
|
119
231
|
}
|
|
120
232
|
catch (err) {
|
|
121
233
|
if (err instanceof HttpFetcherError) {
|
|
122
234
|
AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
235
|
+
incrementAuthorizationError(this.scopeToResource(this.toCamelCase(scopedActionsPayload[0].scope)).resourceType, scopedActionsPayload[0].action, err.status);
|
|
123
236
|
}
|
|
124
|
-
|
|
125
|
-
throw err;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function toCamelCase(obj) {
|
|
129
|
-
return mapKeys(obj, (_, key) => camelCase(key));
|
|
237
|
+
throw err;
|
|
130
238
|
}
|
|
239
|
+
}
|
|
240
|
+
static toCamelCase(obj) {
|
|
241
|
+
return mapKeys(obj, (_, key) => camelCase(key));
|
|
242
|
+
}
|
|
243
|
+
static mapPlatformResponse(response) {
|
|
131
244
|
if (!response) {
|
|
132
245
|
logger.error({ tag: 'authorization-service', response }, 'AuthorizationService: missing response');
|
|
133
246
|
throw new Error('AuthorizationService: missing response');
|
|
134
247
|
}
|
|
135
|
-
|
|
248
|
+
return response.result.map(responseObject => {
|
|
136
249
|
const { scopedAction, permit } = responseObject;
|
|
137
250
|
const { scope } = scopedAction;
|
|
138
251
|
return {
|
|
139
252
|
...responseObject,
|
|
140
|
-
scopedAction: { ...scopedAction, scope: toCamelCase(scope) },
|
|
141
|
-
permit: toCamelCase(permit),
|
|
253
|
+
scopedAction: { ...scopedAction, scope: this.toCamelCase(scope) },
|
|
254
|
+
permit: this.toCamelCase(permit),
|
|
142
255
|
};
|
|
143
256
|
});
|
|
144
|
-
return scopedActionsResponseObjects;
|
|
145
257
|
}
|
|
146
258
|
static async isAuthorizedSingular(accountId, userId, resources, action) {
|
|
147
259
|
const { authorizationObjects } = createAuthorizationParams(resources, action);
|
|
@@ -177,7 +289,7 @@ class AuthorizationService {
|
|
|
177
289
|
});
|
|
178
290
|
}
|
|
179
291
|
catch (err) {
|
|
180
|
-
if (err instanceof
|
|
292
|
+
if (err instanceof HttpFetcherError) {
|
|
181
293
|
AuthorizationInternalService.throwOnHttpError(err.status, 'isAuthorizedMultiple');
|
|
182
294
|
}
|
|
183
295
|
else {
|
|
@@ -196,7 +308,7 @@ class AuthorizationService {
|
|
|
196
308
|
if (!isAuthorized) {
|
|
197
309
|
unauthorizedObjects.push(authorizationObject);
|
|
198
310
|
}
|
|
199
|
-
sendAuthorizationCheckResponseTimeMetric(authorizationObject.resource_type, authorizationObject.action, isAuthorized, 200, time);
|
|
311
|
+
sendAuthorizationCheckResponseTimeMetric(authorizationObject.resource_type, authorizationObject.action, isAuthorized, 200, time, 'platform');
|
|
200
312
|
});
|
|
201
313
|
if (unauthorizedObjects.length > 0) {
|
|
202
314
|
logger.info({
|
|
@@ -6,5 +6,8 @@ export declare const METRICS: {
|
|
|
6
6
|
};
|
|
7
7
|
export declare function setPrometheus(customPrometheus: any): void;
|
|
8
8
|
export declare function getMetricsManager(): any;
|
|
9
|
-
export declare function sendAuthorizationCheckResponseTimeMetric(resourceType: string, action: Action, isAuthorized: boolean, responseStatus: number, time: number): void;
|
|
9
|
+
export declare function sendAuthorizationCheckResponseTimeMetric(resourceType: string, action: Action, isAuthorized: boolean, responseStatus: number, time: number, apiType?: 'platform' | 'graph'): void;
|
|
10
|
+
export declare function incrementAuthorizationSuccess(resourceType: string, action: Action): void;
|
|
11
|
+
export declare function incrementAuthorizationError(resourceType: string, action: Action, statusCode: number): void;
|
|
12
|
+
export declare function setGraphAvailability(isAvailable: boolean): void;
|
|
10
13
|
//# sourceMappingURL=prometheus-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prometheus-service.d.ts","sourceRoot":"","sources":["../../src/prometheus-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAKzC,eAAO,MAAM,OAAO;;;;CAInB,CAAC;AAQF,wBAAgB,aAAa,CAAC,gBAAgB,KAAA,
|
|
1
|
+
{"version":3,"file":"prometheus-service.d.ts","sourceRoot":"","sources":["../../src/prometheus-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAKzC,eAAO,MAAM,OAAO;;;;CAInB,CAAC;AAQF,wBAAgB,aAAa,CAAC,gBAAgB,KAAA,QAiB7C;AAED,wBAAgB,iBAAiB,QAEhC;AAED,wBAAgB,wCAAwC,CACtD,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,UAAU,GAAG,OAAoB,QAW3C;AAED,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAI;AAEtF,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAI;AAExG,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,OAAO,QAAI"}
|
|
@@ -7,26 +7,38 @@ const METRICS = {
|
|
|
7
7
|
};
|
|
8
8
|
const authorizationCheckResponseTimeMetricConfig = {
|
|
9
9
|
name: METRICS.AUTHORIZATION_CHECK_RESPONSE_TIME,
|
|
10
|
-
labels: ['resourceType', 'action', 'isAuthorized', 'responseStatus'],
|
|
10
|
+
labels: ['resourceType', 'action', 'isAuthorized', 'responseStatus', 'apiType'],
|
|
11
11
|
description: 'Authorization check response time summary',
|
|
12
12
|
};
|
|
13
13
|
function setPrometheus(customPrometheus) {
|
|
14
14
|
prometheus = customPrometheus;
|
|
15
|
+
if (!prometheus) {
|
|
16
|
+
authorizationCheckResponseTimeMetric = null;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
15
19
|
const { METRICS_TYPES } = prometheus;
|
|
16
|
-
|
|
20
|
+
const metricsManager = getMetricsManager();
|
|
21
|
+
if (metricsManager) {
|
|
22
|
+
authorizationCheckResponseTimeMetric = metricsManager.addMetric(METRICS_TYPES.SUMMARY, authorizationCheckResponseTimeMetricConfig.name, authorizationCheckResponseTimeMetricConfig.labels, authorizationCheckResponseTimeMetricConfig.description);
|
|
23
|
+
}
|
|
17
24
|
}
|
|
18
25
|
function getMetricsManager() {
|
|
19
26
|
return prometheus?.metricsManager;
|
|
20
27
|
}
|
|
21
|
-
function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, responseStatus, time) {
|
|
28
|
+
function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, responseStatus, time, apiType = 'platform') {
|
|
22
29
|
try {
|
|
23
30
|
if (authorizationCheckResponseTimeMetric) {
|
|
24
|
-
authorizationCheckResponseTimeMetric
|
|
31
|
+
authorizationCheckResponseTimeMetric
|
|
32
|
+
.labels(resourceType, action, isAuthorized, responseStatus, apiType)
|
|
33
|
+
.observe(time);
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
36
|
catch (e) {
|
|
28
37
|
// ignore
|
|
29
38
|
}
|
|
30
39
|
}
|
|
40
|
+
function incrementAuthorizationSuccess(resourceType, action) { }
|
|
41
|
+
function incrementAuthorizationError(resourceType, action, statusCode) { }
|
|
42
|
+
function setGraphAvailability(isAvailable) { }
|
|
31
43
|
|
|
32
|
-
export { METRICS, getMetricsManager, sendAuthorizationCheckResponseTimeMetric, setPrometheus };
|
|
44
|
+
export { METRICS, getMetricsManager, incrementAuthorizationError, incrementAuthorizationSuccess, sendAuthorizationCheckResponseTimeMetric, setGraphAvailability, setPrometheus };
|
|
@@ -6,5 +6,8 @@ export declare const METRICS: {
|
|
|
6
6
|
};
|
|
7
7
|
export declare function setPrometheus(customPrometheus: any): void;
|
|
8
8
|
export declare function getMetricsManager(): any;
|
|
9
|
-
export declare function sendAuthorizationCheckResponseTimeMetric(resourceType: string, action: Action, isAuthorized: boolean, responseStatus: number, time: number): void;
|
|
9
|
+
export declare function sendAuthorizationCheckResponseTimeMetric(resourceType: string, action: Action, isAuthorized: boolean, responseStatus: number, time: number, apiType?: 'platform' | 'graph'): void;
|
|
10
|
+
export declare function incrementAuthorizationSuccess(resourceType: string, action: Action): void;
|
|
11
|
+
export declare function incrementAuthorizationError(resourceType: string, action: Action, statusCode: number): void;
|
|
12
|
+
export declare function setGraphAvailability(isAvailable: boolean): void;
|
|
10
13
|
//# sourceMappingURL=prometheus-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prometheus-service.d.ts","sourceRoot":"","sources":["../src/prometheus-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAKzC,eAAO,MAAM,OAAO;;;;CAInB,CAAC;AAQF,wBAAgB,aAAa,CAAC,gBAAgB,KAAA,
|
|
1
|
+
{"version":3,"file":"prometheus-service.d.ts","sourceRoot":"","sources":["../src/prometheus-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAKzC,eAAO,MAAM,OAAO;;;;CAInB,CAAC;AAQF,wBAAgB,aAAa,CAAC,gBAAgB,KAAA,QAiB7C;AAED,wBAAgB,iBAAiB,QAEhC;AAED,wBAAgB,wCAAwC,CACtD,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,UAAU,GAAG,OAAoB,QAW3C;AAED,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAI;AAEtF,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAI;AAExG,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,OAAO,QAAI"}
|
|
@@ -9,29 +9,44 @@ const METRICS = {
|
|
|
9
9
|
};
|
|
10
10
|
const authorizationCheckResponseTimeMetricConfig = {
|
|
11
11
|
name: METRICS.AUTHORIZATION_CHECK_RESPONSE_TIME,
|
|
12
|
-
labels: ['resourceType', 'action', 'isAuthorized', 'responseStatus'],
|
|
12
|
+
labels: ['resourceType', 'action', 'isAuthorized', 'responseStatus', 'apiType'],
|
|
13
13
|
description: 'Authorization check response time summary',
|
|
14
14
|
};
|
|
15
15
|
function setPrometheus(customPrometheus) {
|
|
16
16
|
prometheus = customPrometheus;
|
|
17
|
+
if (!prometheus) {
|
|
18
|
+
authorizationCheckResponseTimeMetric = null;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
17
21
|
const { METRICS_TYPES } = prometheus;
|
|
18
|
-
|
|
22
|
+
const metricsManager = getMetricsManager();
|
|
23
|
+
if (metricsManager) {
|
|
24
|
+
authorizationCheckResponseTimeMetric = metricsManager.addMetric(METRICS_TYPES.SUMMARY, authorizationCheckResponseTimeMetricConfig.name, authorizationCheckResponseTimeMetricConfig.labels, authorizationCheckResponseTimeMetricConfig.description);
|
|
25
|
+
}
|
|
19
26
|
}
|
|
20
27
|
function getMetricsManager() {
|
|
21
28
|
return prometheus?.metricsManager;
|
|
22
29
|
}
|
|
23
|
-
function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, responseStatus, time) {
|
|
30
|
+
function sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, responseStatus, time, apiType = 'platform') {
|
|
24
31
|
try {
|
|
25
32
|
if (authorizationCheckResponseTimeMetric) {
|
|
26
|
-
authorizationCheckResponseTimeMetric
|
|
33
|
+
authorizationCheckResponseTimeMetric
|
|
34
|
+
.labels(resourceType, action, isAuthorized, responseStatus, apiType)
|
|
35
|
+
.observe(time);
|
|
27
36
|
}
|
|
28
37
|
}
|
|
29
38
|
catch (e) {
|
|
30
39
|
// ignore
|
|
31
40
|
}
|
|
32
41
|
}
|
|
42
|
+
function incrementAuthorizationSuccess(resourceType, action) { }
|
|
43
|
+
function incrementAuthorizationError(resourceType, action, statusCode) { }
|
|
44
|
+
function setGraphAvailability(isAvailable) { }
|
|
33
45
|
|
|
34
46
|
exports.METRICS = METRICS;
|
|
35
47
|
exports.getMetricsManager = getMetricsManager;
|
|
48
|
+
exports.incrementAuthorizationError = incrementAuthorizationError;
|
|
49
|
+
exports.incrementAuthorizationSuccess = incrementAuthorizationSuccess;
|
|
36
50
|
exports.sendAuthorizationCheckResponseTimeMetric = sendAuthorizationCheckResponseTimeMetric;
|
|
51
|
+
exports.setGraphAvailability = setGraphAvailability;
|
|
37
52
|
exports.setPrometheus = setPrometheus;
|
package/package.json
CHANGED