@mondaydotcomorg/monday-authorization 3.3.0-feature-bashanye-navigate-can-action-in-scope-to-graph-2992133 → 3.3.0-feature-bashanye-navigate-can-action-in-scope-to-graph-36f311f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authorization-service.d.ts.map +1 -1
- package/dist/authorization-service.js +7 -0
- package/dist/clients/graph-api.client.d.ts.map +1 -1
- package/dist/clients/graph-api.client.js +37 -2
- package/dist/clients/platform-api.client.d.ts.map +1 -1
- package/dist/esm/authorization-service.d.ts.map +1 -1
- package/dist/esm/authorization-service.mjs +7 -0
- package/dist/esm/clients/graph-api.client.d.ts.map +1 -1
- package/dist/esm/clients/graph-api.client.mjs +37 -2
- package/dist/esm/clients/platform-api.client.d.ts.map +1 -1
- package/dist/esm/types/graph-api.types.d.ts +9 -1
- package/dist/esm/types/graph-api.types.d.ts.map +1 -1
- package/dist/esm/utils/authorization.utils.d.ts.map +1 -1
- package/dist/types/graph-api.types.d.ts +9 -1
- package/dist/types/graph-api.types.d.ts.map +1 -1
- package/dist/utils/authorization.utils.d.ts.map +1 -1
- package/package.json +2 -3
- package/src/attributions-service.ts +0 -92
- package/src/authorization-attributes-service.ts +0 -234
- package/src/authorization-internal-service.ts +0 -129
- package/src/authorization-middleware.ts +0 -51
- package/src/authorization-service.ts +0 -357
- package/src/clients/graph-api.client.ts +0 -134
- package/src/clients/platform-api.client.ts +0 -118
- package/src/constants/sns.ts +0 -5
- package/src/constants.ts +0 -22
- package/src/index.ts +0 -46
- package/src/prometheus-service.ts +0 -147
- package/src/roles-service.ts +0 -125
- package/src/testKit/index.ts +0 -69
- package/src/types/authorization-attributes-contracts.ts +0 -33
- package/src/types/express.ts +0 -8
- package/src/types/general.ts +0 -32
- package/src/types/graph-api.types.ts +0 -19
- package/src/types/roles.ts +0 -42
- package/src/types/scoped-actions-contracts.ts +0 -48
- package/src/utils/authorization.utils.ts +0 -48
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { Api } from '@mondaydotcomorg/trident-backend-api';
|
|
2
|
-
import { HttpFetcherError } from '@mondaydotcomorg/monday-fetch-api';
|
|
3
|
-
import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
|
|
4
|
-
import { AuthorizationInternalService, logger } from '../authorization-internal-service';
|
|
5
|
-
import { getAttributionsFromApi, PlatformProfile } from '../attributions-service';
|
|
6
|
-
import { toCamelCase, toSnakeCase, scopeToResource } from '../utils/authorization.utils';
|
|
7
|
-
import { incrementAuthorizationError } from '../prometheus-service';
|
|
8
|
-
|
|
9
|
-
const PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH = '/internal_ms/authorization/can_actions_in_scopes';
|
|
10
|
-
|
|
11
|
-
// ScopedAction with snake_case scope keys for Platform API payload
|
|
12
|
-
type ScopedActionPlatformPayload = Omit<ScopedAction, 'scope'> & {
|
|
13
|
-
scope: Record<string, number>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
interface CanActionsInScopesResponse {
|
|
17
|
-
result: ScopedActionResponseObject[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Client for handling Platform API authorization operations
|
|
22
|
-
*/
|
|
23
|
-
export class PlatformApiClient {
|
|
24
|
-
/**
|
|
25
|
-
* Builds the request payload for Platform API calls
|
|
26
|
-
*/
|
|
27
|
-
static buildRequestPayload(scopedActions: ScopedAction[]): ScopedActionPlatformPayload[] {
|
|
28
|
-
return scopedActions.map(scopedAction => ({
|
|
29
|
-
...scopedAction,
|
|
30
|
-
scope: toSnakeCase(scopedAction.scope) as Record<string, number>,
|
|
31
|
-
}));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Fetches authorization data from the Platform API
|
|
36
|
-
*/
|
|
37
|
-
static async fetchPermissions(
|
|
38
|
-
profile: PlatformProfile,
|
|
39
|
-
internalAuthToken: string,
|
|
40
|
-
userId: number,
|
|
41
|
-
scopedActionsPayload: ScopedActionPlatformPayload[]
|
|
42
|
-
): Promise<CanActionsInScopesResponse> {
|
|
43
|
-
const attributionHeaders = getAttributionsFromApi();
|
|
44
|
-
const httpClient = Api.getPart('httpClient');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const response = await httpClient!.fetch<CanActionsInScopesResponse>(
|
|
49
|
-
{
|
|
50
|
-
url: {
|
|
51
|
-
appName: 'platform',
|
|
52
|
-
path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
|
|
53
|
-
profile,
|
|
54
|
-
},
|
|
55
|
-
method: 'POST',
|
|
56
|
-
headers: {
|
|
57
|
-
Authorization: internalAuthToken,
|
|
58
|
-
'Content-Type': 'application/json',
|
|
59
|
-
...attributionHeaders,
|
|
60
|
-
},
|
|
61
|
-
body: JSON.stringify({ user_id: userId, scoped_actions: scopedActionsPayload }),
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
timeout: AuthorizationInternalService.getRequestTimeout(),
|
|
65
|
-
retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return response;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
if (err instanceof HttpFetcherError) {
|
|
73
|
-
AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
|
|
74
|
-
incrementAuthorizationError(
|
|
75
|
-
scopeToResource(toCamelCase(scopedActionsPayload[0].scope) as any).resourceType,
|
|
76
|
-
scopedActionsPayload[0].action,
|
|
77
|
-
err.status
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
throw err;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Maps Platform API response to the expected format
|
|
86
|
-
*/
|
|
87
|
-
static mapResponse(response: CanActionsInScopesResponse): ScopedActionResponseObject[] {
|
|
88
|
-
if (!response) {
|
|
89
|
-
logger.error({ tag: 'platform-api-client', response }, 'PlatformApiClient: missing response');
|
|
90
|
-
throw new Error('PlatformApiClient: missing response');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return response.result.map(responseObject => {
|
|
94
|
-
const { scopedAction, permit } = responseObject;
|
|
95
|
-
const { scope } = scopedAction;
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
...responseObject,
|
|
99
|
-
scopedAction: { ...scopedAction, scope: toCamelCase(scope) },
|
|
100
|
-
permit: toCamelCase(permit),
|
|
101
|
-
};
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Performs a complete authorization check using the Platform API
|
|
107
|
-
*/
|
|
108
|
-
static async checkPermissions(
|
|
109
|
-
profile: PlatformProfile,
|
|
110
|
-
internalAuthToken: string,
|
|
111
|
-
userId: number,
|
|
112
|
-
scopedActions: ScopedAction[]
|
|
113
|
-
): Promise<ScopedActionResponseObject[]> {
|
|
114
|
-
const scopedActionsPayload = this.buildRequestPayload(scopedActions);
|
|
115
|
-
const platformResponse = await this.fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload);
|
|
116
|
-
return this.mapResponse(platformResponse);
|
|
117
|
-
}
|
|
118
|
-
}
|
package/src/constants/sns.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export const SNS_ARN_ENV_VAR_NAME = 'SHARED_AUTHORIZATION_SNS_ENDPOINT_RESOURCE_ATTRIBUTES';
|
|
2
|
-
export const SNS_DEV_TEST_NAME =
|
|
3
|
-
'arn:aws:sns:us-east-1:000000000000:monday-authorization-resource-attributes-sns-local';
|
|
4
|
-
export const RESOURCE_ATTRIBUTES_SNS_UPDATE_OPERATION_MESSAGE_KIND = 'resourceAttributeModification';
|
|
5
|
-
export const ASYNC_RESOURCE_ATTRIBUTES_MAX_OPERATIONS_PER_MESSAGE = 100;
|
package/src/constants.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { RecursivePartial } from '@mondaydotcomorg/monday-fetch-api';
|
|
2
|
-
import { FetcherConfig } from '@mondaydotcomorg/trident-backend-api';
|
|
3
|
-
|
|
4
|
-
export const APP_NAME = 'authorization';
|
|
5
|
-
|
|
6
|
-
export const ERROR_MESSAGES = {
|
|
7
|
-
HTTP_CLIENT_NOT_INITIALIZED: 'MondayAuthorization: HTTP client is not initialized',
|
|
8
|
-
REQUEST_FAILED: (method: string, status: number, reason: string) =>
|
|
9
|
-
`MondayAuthorization: [${method}] request failed with status ${status} with reason: ${reason}`,
|
|
10
|
-
} as const;
|
|
11
|
-
|
|
12
|
-
export const DEFAULT_FETCH_OPTIONS: RecursivePartial<FetcherConfig> = {
|
|
13
|
-
retryPolicy: {
|
|
14
|
-
useRetries: true,
|
|
15
|
-
maxRetries: 3,
|
|
16
|
-
retryDelayMS: 10,
|
|
17
|
-
},
|
|
18
|
-
logPolicy: {
|
|
19
|
-
logErrors: 'error',
|
|
20
|
-
logRequests: 'info',
|
|
21
|
-
},
|
|
22
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
|
|
2
|
-
import { setPrometheus } from './prometheus-service';
|
|
3
|
-
import { setIgniteClient, setRedisClient, setRequestFetchOptions } from './authorization-service';
|
|
4
|
-
import * as TestKit from './testKit';
|
|
5
|
-
|
|
6
|
-
export interface InitOptions {
|
|
7
|
-
prometheus?: any;
|
|
8
|
-
mondayFetchOptions?: MondayFetchOptions;
|
|
9
|
-
redisClient?: any;
|
|
10
|
-
grantedFeatureRedisExpirationInSeconds?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function init(options: InitOptions = {}) {
|
|
14
|
-
if (options.prometheus) {
|
|
15
|
-
setPrometheus(options.prometheus);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (options.mondayFetchOptions) {
|
|
19
|
-
setRequestFetchOptions(options.mondayFetchOptions);
|
|
20
|
-
}
|
|
21
|
-
if (options.redisClient) {
|
|
22
|
-
setRedisClient(options.redisClient, options.grantedFeatureRedisExpirationInSeconds);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// add an ignite client for gradual release features
|
|
26
|
-
await setIgniteClient();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export {
|
|
30
|
-
authorizationCheckMiddleware,
|
|
31
|
-
getAuthorizationMiddleware,
|
|
32
|
-
skipAuthorizationMiddleware,
|
|
33
|
-
} from './authorization-middleware';
|
|
34
|
-
export { AuthorizationService, AuthorizeResponse } from './authorization-service';
|
|
35
|
-
export { AuthorizationAttributesService } from './authorization-attributes-service';
|
|
36
|
-
export { RolesService } from './roles-service';
|
|
37
|
-
export { AuthorizationObject, Resource, BaseRequest, ResourceGetter, ContextGetter } from './types/general';
|
|
38
|
-
export {
|
|
39
|
-
Translation,
|
|
40
|
-
ScopedAction,
|
|
41
|
-
ScopedActionResponseObject,
|
|
42
|
-
ScopedActionPermit,
|
|
43
|
-
} from './types/scoped-actions-contracts';
|
|
44
|
-
export { CustomRole, BasicRole, RoleType, RoleCreateRequest, RoleUpdateRequest, RolesResponse } from './types/roles';
|
|
45
|
-
|
|
46
|
-
export { TestKit };
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { Action } from './types/general';
|
|
2
|
-
|
|
3
|
-
let prometheus: any = null;
|
|
4
|
-
let authorizationCheckResponseTimeMetric: any = null;
|
|
5
|
-
let authorizationSuccessMetric: any = null;
|
|
6
|
-
let authorizationErrorMetric: any = null;
|
|
7
|
-
let graphAvailabilityMetric: any = null;
|
|
8
|
-
|
|
9
|
-
export const METRICS = {
|
|
10
|
-
AUTHORIZATION_CHECK: 'authorization_check',
|
|
11
|
-
AUTHORIZATION_CHECKS_PER_REQUEST: 'authorization_checks_per_request',
|
|
12
|
-
AUTHORIZATION_CHECK_RESPONSE_TIME: 'authorization_check_response_time',
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const authorizationCheckResponseTimeMetricConfig = {
|
|
16
|
-
name: METRICS.AUTHORIZATION_CHECK_RESPONSE_TIME,
|
|
17
|
-
labels: ['resourceType', 'action', 'isAuthorized', 'responseStatus', 'apiType'],
|
|
18
|
-
description: 'Authorization check response time summary',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export function setPrometheus(customPrometheus) {
|
|
22
|
-
prometheus = customPrometheus;
|
|
23
|
-
if (!prometheus) {
|
|
24
|
-
authorizationCheckResponseTimeMetric = null;
|
|
25
|
-
authorizationSuccessMetric = null;
|
|
26
|
-
authorizationErrorMetric = null;
|
|
27
|
-
graphAvailabilityMetric = null;
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const { METRICS_TYPES } = prometheus;
|
|
32
|
-
const metricsManager = getMetricsManager();
|
|
33
|
-
if (metricsManager) {
|
|
34
|
-
authorizationCheckResponseTimeMetric = metricsManager.addMetric(
|
|
35
|
-
METRICS_TYPES.SUMMARY,
|
|
36
|
-
authorizationCheckResponseTimeMetricConfig.name,
|
|
37
|
-
authorizationCheckResponseTimeMetricConfig.labels,
|
|
38
|
-
authorizationCheckResponseTimeMetricConfig.description
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
initializeAdditionalMetrics();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function getMetricsManager() {
|
|
46
|
-
return prometheus?.metricsManager;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function sendAuthorizationCheckResponseTimeMetric(
|
|
50
|
-
resourceType: string,
|
|
51
|
-
action: Action,
|
|
52
|
-
isAuthorized: boolean,
|
|
53
|
-
responseStatus: number,
|
|
54
|
-
time: number,
|
|
55
|
-
apiType: 'platform' | 'graph' = 'platform'
|
|
56
|
-
) {
|
|
57
|
-
try {
|
|
58
|
-
if (authorizationCheckResponseTimeMetric) {
|
|
59
|
-
authorizationCheckResponseTimeMetric
|
|
60
|
-
.labels(resourceType, action, isAuthorized, responseStatus, apiType)
|
|
61
|
-
.observe(time);
|
|
62
|
-
}
|
|
63
|
-
} catch (e) {
|
|
64
|
-
// ignore
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const authorizationSuccessMetricConfig = {
|
|
69
|
-
name: 'authorization_success_total',
|
|
70
|
-
labels: ['resourceType', 'action'],
|
|
71
|
-
description: 'Total number of successful authorization checks',
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const authorizationErrorMetricConfig = {
|
|
75
|
-
name: 'authorization_error_total',
|
|
76
|
-
labels: ['resourceType', 'action', 'statusCode'],
|
|
77
|
-
description: 'Total number of authorization errors',
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const graphAvailabilityMetricConfig = {
|
|
81
|
-
name: 'graph_api_availability',
|
|
82
|
-
labels: ['available'],
|
|
83
|
-
description: 'Graph API availability status',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export function incrementAuthorizationSuccess(resourceType: string, action: Action) {
|
|
87
|
-
try {
|
|
88
|
-
if (authorizationSuccessMetric) {
|
|
89
|
-
authorizationSuccessMetric.labels(resourceType, action).inc();
|
|
90
|
-
}
|
|
91
|
-
} catch (e) {
|
|
92
|
-
// ignore
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function incrementAuthorizationError(resourceType: string, action: Action, statusCode: number) {
|
|
97
|
-
try {
|
|
98
|
-
if (authorizationErrorMetric) {
|
|
99
|
-
authorizationErrorMetric.labels(resourceType, action, statusCode).inc();
|
|
100
|
-
}
|
|
101
|
-
} catch (e) {
|
|
102
|
-
// ignore
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function setGraphAvailability(isAvailable: boolean) {
|
|
107
|
-
try {
|
|
108
|
-
if (graphAvailabilityMetric) {
|
|
109
|
-
graphAvailabilityMetric.labels(isAvailable ? 'true' : 'false').set(isAvailable ? 1 : 0);
|
|
110
|
-
}
|
|
111
|
-
} catch (e) {
|
|
112
|
-
// ignore
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Initialize additional metrics when prometheus is set
|
|
117
|
-
function initializeAdditionalMetrics() {
|
|
118
|
-
if (!prometheus) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const { METRICS_TYPES } = prometheus;
|
|
123
|
-
const metricsManager = getMetricsManager();
|
|
124
|
-
|
|
125
|
-
if (metricsManager) {
|
|
126
|
-
authorizationSuccessMetric = metricsManager.addMetric(
|
|
127
|
-
METRICS_TYPES.COUNTER,
|
|
128
|
-
authorizationSuccessMetricConfig.name,
|
|
129
|
-
authorizationSuccessMetricConfig.labels,
|
|
130
|
-
authorizationSuccessMetricConfig.description
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
authorizationErrorMetric = metricsManager.addMetric(
|
|
134
|
-
METRICS_TYPES.COUNTER,
|
|
135
|
-
authorizationErrorMetricConfig.name,
|
|
136
|
-
authorizationErrorMetricConfig.labels,
|
|
137
|
-
authorizationErrorMetricConfig.description
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
graphAvailabilityMetric = metricsManager.addMetric(
|
|
141
|
-
METRICS_TYPES.GAUGE,
|
|
142
|
-
graphAvailabilityMetricConfig.name,
|
|
143
|
-
graphAvailabilityMetricConfig.labels,
|
|
144
|
-
graphAvailabilityMetricConfig.description
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
package/src/roles-service.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { Api, FetcherConfig, HttpClient } from '@mondaydotcomorg/trident-backend-api';
|
|
2
|
-
import { HttpFetcherError, RecursivePartial } from '@mondaydotcomorg/monday-fetch-api';
|
|
3
|
-
import { RoleCreateRequest, RolesResponse, RoleUpdateRequest } from 'types/roles';
|
|
4
|
-
import { getAttributionsFromApi } from 'attributions-service';
|
|
5
|
-
import { APP_NAME, DEFAULT_FETCH_OPTIONS, ERROR_MESSAGES } from './constants';
|
|
6
|
-
|
|
7
|
-
const API_PATH = '/roles/account/{accountId}';
|
|
8
|
-
|
|
9
|
-
export class RolesService {
|
|
10
|
-
private httpClient: HttpClient;
|
|
11
|
-
private fetchOptions: RecursivePartial<FetcherConfig>;
|
|
12
|
-
private attributionHeaders: { [key: string]: string };
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Public constructor to create the AuthorizationAttributesService instance.
|
|
16
|
-
* @param httpClient The HTTP client to use for API requests, if not provided, the default HTTP client from Api will be used.
|
|
17
|
-
* @param fetchOptions The fetch options to use for API requests, if not provided, the default fetch options will be used.
|
|
18
|
-
*/
|
|
19
|
-
constructor(httpClient?: HttpClient, fetchOptions?: RecursivePartial<FetcherConfig>) {
|
|
20
|
-
if (!httpClient) {
|
|
21
|
-
httpClient = Api.getPart('httpClient');
|
|
22
|
-
if (!httpClient) {
|
|
23
|
-
throw new Error(ERROR_MESSAGES.HTTP_CLIENT_NOT_INITIALIZED);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!fetchOptions) {
|
|
28
|
-
fetchOptions = DEFAULT_FETCH_OPTIONS;
|
|
29
|
-
} else {
|
|
30
|
-
fetchOptions = {
|
|
31
|
-
...DEFAULT_FETCH_OPTIONS,
|
|
32
|
-
...fetchOptions,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
this.httpClient = httpClient;
|
|
36
|
-
this.fetchOptions = fetchOptions;
|
|
37
|
-
this.attributionHeaders = getAttributionsFromApi();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get all roles for an account
|
|
42
|
-
* @param accountId - The account ID
|
|
43
|
-
* @param style - The style of the roles to return, either 'A' or 'B', default is 'A'. 'B' is not deprecated and only available for backward compatibility.
|
|
44
|
-
* @returns - The roles for the account, both basic and custom roles. Note that basic role ids are returned in A style and not B style.
|
|
45
|
-
*/
|
|
46
|
-
async getRoles(accountId: number, resourceTypes: string[], style: 'A' | 'B' = 'A'): Promise<RolesResponse> {
|
|
47
|
-
return await this.sendRoleRequest('GET', accountId, {}, { resourceTypes, style });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Create a custom role for an account
|
|
52
|
-
* @param accountId - The account ID
|
|
53
|
-
* @param roles - The roles to create
|
|
54
|
-
* @returns - The created roles
|
|
55
|
-
* Note that basic role ids should be provided in A style and not in B style.
|
|
56
|
-
*/
|
|
57
|
-
async createCustomRole(accountId: number, roles: RoleCreateRequest[]): Promise<RolesResponse> {
|
|
58
|
-
if (roles.length === 0) {
|
|
59
|
-
throw new Error('Roles array cannot be empty');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return await this.sendRoleRequest('PUT', accountId, {
|
|
63
|
-
customRoles: roles,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Delete a custom role for an account
|
|
69
|
-
* @param accountId - The account ID
|
|
70
|
-
* @param roleIds - The ids of the roles to delete
|
|
71
|
-
* @returns - The deleted roles. Note that basic role ids should be provided in A style and not in B style.
|
|
72
|
-
*/
|
|
73
|
-
async deleteCustomRole(accountId: number, roleIds: number[]): Promise<RolesResponse> {
|
|
74
|
-
return await this.sendRoleRequest('DELETE', accountId, {
|
|
75
|
-
ids: roleIds,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Update a custom role for an account
|
|
81
|
-
* @param accountId - The account ID
|
|
82
|
-
* @param updateRequests - The requests to update the roles
|
|
83
|
-
* @returns - The updated roles. Note that basic role ids should be provided in A style and not in B style.
|
|
84
|
-
*/
|
|
85
|
-
async updateCustomRole(accountId: number, updateRequests: RoleUpdateRequest[]): Promise<RolesResponse> {
|
|
86
|
-
return await this.sendRoleRequest('PATCH', accountId, {
|
|
87
|
-
customRoles: updateRequests,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private async sendRoleRequest(
|
|
92
|
-
method: 'PUT' | 'GET' | 'DELETE' | 'PATCH',
|
|
93
|
-
accountId: number,
|
|
94
|
-
body: any,
|
|
95
|
-
additionalQueryParams: { [key: string]: any } = {},
|
|
96
|
-
style: 'A' | 'B' = 'A'
|
|
97
|
-
): Promise<RolesResponse> {
|
|
98
|
-
try {
|
|
99
|
-
return await this.httpClient.fetch<RolesResponse>(
|
|
100
|
-
{
|
|
101
|
-
url: {
|
|
102
|
-
appName: APP_NAME,
|
|
103
|
-
path: API_PATH.replace('{accountId}', accountId.toString()),
|
|
104
|
-
},
|
|
105
|
-
query: {
|
|
106
|
-
style: style,
|
|
107
|
-
...additionalQueryParams,
|
|
108
|
-
},
|
|
109
|
-
method,
|
|
110
|
-
headers: {
|
|
111
|
-
'Content-Type': 'application/json',
|
|
112
|
-
...this.attributionHeaders,
|
|
113
|
-
},
|
|
114
|
-
body: method === 'GET' ? undefined : body,
|
|
115
|
-
},
|
|
116
|
-
this.fetchOptions
|
|
117
|
-
);
|
|
118
|
-
} catch (err) {
|
|
119
|
-
if (err instanceof HttpFetcherError) {
|
|
120
|
-
throw new Error(ERROR_MESSAGES.REQUEST_FAILED('sendRoleRequest', err.status, err.message));
|
|
121
|
-
}
|
|
122
|
-
throw err;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/testKit/index.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { Action, BaseRequest, BaseResponse, ContextGetter, Resource, ResourceGetter } from '../types/general';
|
|
2
|
-
import { defaultContextGetter } from '../authorization-middleware';
|
|
3
|
-
import { AuthorizationInternalService } from '../authorization-internal-service';
|
|
4
|
-
import type { NextFunction } from 'express';
|
|
5
|
-
|
|
6
|
-
export type TestPermittedAction = {
|
|
7
|
-
accountId: number;
|
|
8
|
-
userId: number;
|
|
9
|
-
resources: Resource[];
|
|
10
|
-
action: Action;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
let testPermittedActions: TestPermittedAction[] = [];
|
|
14
|
-
export const addTestPermittedAction = (accountId: number, userId: number, resources: Resource[], action: Action) => {
|
|
15
|
-
testPermittedActions.push({ accountId, userId, resources, action });
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const clearTestPermittedActions = () => {
|
|
19
|
-
testPermittedActions = [];
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const isActionAuthorized = (accountId: number, userId: number, resources: Resource[], action: Action) => {
|
|
23
|
-
// If no resources to check, deny access
|
|
24
|
-
if (resources.length === 0) {
|
|
25
|
-
return { isAuthorized: false };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
isAuthorized: resources.every(resource => {
|
|
30
|
-
return testPermittedActions.some(combination => {
|
|
31
|
-
return (
|
|
32
|
-
combination.accountId === accountId &&
|
|
33
|
-
combination.userId === userId &&
|
|
34
|
-
combination.action === action &&
|
|
35
|
-
combination.resources.some(combinationResource => {
|
|
36
|
-
return (
|
|
37
|
-
combinationResource.id === resource.id &&
|
|
38
|
-
combinationResource.type === resource.type &&
|
|
39
|
-
JSON.stringify(combinationResource.wrapperData) === JSON.stringify(resource.wrapperData)
|
|
40
|
-
);
|
|
41
|
-
})
|
|
42
|
-
);
|
|
43
|
-
});
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const getTestAuthorizationMiddleware = (
|
|
49
|
-
action: Action,
|
|
50
|
-
resourceGetter: ResourceGetter,
|
|
51
|
-
contextGetter?: ContextGetter
|
|
52
|
-
) => {
|
|
53
|
-
return async function authorizationMiddleware(
|
|
54
|
-
request: BaseRequest,
|
|
55
|
-
response: BaseResponse,
|
|
56
|
-
next: NextFunction
|
|
57
|
-
): Promise<void> {
|
|
58
|
-
contextGetter ||= defaultContextGetter;
|
|
59
|
-
const { userId, accountId } = contextGetter(request);
|
|
60
|
-
const resources = resourceGetter(request);
|
|
61
|
-
const { isAuthorized } = isActionAuthorized(accountId, userId, resources, action);
|
|
62
|
-
if (!isAuthorized) {
|
|
63
|
-
response.status(403).json({ message: 'Access denied' });
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
AuthorizationInternalService.markAuthorized(request);
|
|
67
|
-
next();
|
|
68
|
-
};
|
|
69
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Resource } from './general';
|
|
2
|
-
|
|
3
|
-
export interface ResourceAttributeAssignment {
|
|
4
|
-
resourceType: Resource['type'];
|
|
5
|
-
resourceId: Resource['id'];
|
|
6
|
-
key: string;
|
|
7
|
-
value: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ResourceAttributeResponse {
|
|
11
|
-
attributes: ResourceAttributeAssignment[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ResourceAttributeDelete {
|
|
15
|
-
resourceType: Resource['type'];
|
|
16
|
-
resourceId: Resource['id'];
|
|
17
|
-
key: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export enum ResourceAttributeOperationEnum {
|
|
21
|
-
UPSERT = 'upsert',
|
|
22
|
-
DELETE = 'delete',
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface UpsertResourceAttributeOperation extends ResourceAttributeAssignment {
|
|
26
|
-
operationType: ResourceAttributeOperationEnum.UPSERT;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface DeleteResourceAttributeOperation extends ResourceAttributeDelete {
|
|
30
|
-
operationType: ResourceAttributeOperationEnum.DELETE;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type ResourceAttributesOperation = UpsertResourceAttributeOperation | DeleteResourceAttributeOperation;
|
package/src/types/express.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace, @typescript-eslint/no-unused-vars
|
|
2
|
-
declare namespace Express {
|
|
3
|
-
export interface Request {
|
|
4
|
-
payload: { accountId: number; userId: number };
|
|
5
|
-
authorizationCheckPerformed: boolean;
|
|
6
|
-
authorizationSkipPerformed: boolean;
|
|
7
|
-
}
|
|
8
|
-
}
|
package/src/types/general.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { Request, Response } from 'express';
|
|
2
|
-
|
|
3
|
-
export interface Resource {
|
|
4
|
-
id?: number;
|
|
5
|
-
type: string;
|
|
6
|
-
wrapperData?: object;
|
|
7
|
-
}
|
|
8
|
-
export type Action = string;
|
|
9
|
-
export interface Context {
|
|
10
|
-
accountId: number;
|
|
11
|
-
userId: number;
|
|
12
|
-
}
|
|
13
|
-
export interface AuthorizationObject {
|
|
14
|
-
resource_id?: Resource['id'];
|
|
15
|
-
resource_type: Resource['type'];
|
|
16
|
-
wrapper_data?: Resource['wrapperData'];
|
|
17
|
-
action: Action;
|
|
18
|
-
}
|
|
19
|
-
export interface AuthorizationParams {
|
|
20
|
-
authorizationObjects: AuthorizationObject[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type BasicObject = { [key: string]: unknown };
|
|
24
|
-
|
|
25
|
-
export type BaseParameters = BasicObject;
|
|
26
|
-
export type BaseResponseBody = BasicObject;
|
|
27
|
-
export type BaseBodyParameters = BasicObject;
|
|
28
|
-
export type BaseQueryParameters = BasicObject;
|
|
29
|
-
export type BaseRequest = Request<BaseParameters, BaseResponseBody, BaseBodyParameters, BaseQueryParameters>;
|
|
30
|
-
export type BaseResponse = Response<BaseResponseBody>;
|
|
31
|
-
export type ResourceGetter = (request: BaseRequest) => Resource[];
|
|
32
|
-
export type ContextGetter = (request: BaseRequest) => Context;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Graph API related types and interfaces
|
|
2
|
-
|
|
3
|
-
export type ResourceType = string;
|
|
4
|
-
export type ResourceId = number;
|
|
5
|
-
export type ActionName = string;
|
|
6
|
-
|
|
7
|
-
export type GraphIsAllowedDto = Record<ResourceType, Record<ResourceId, ActionName[]>>;
|
|
8
|
-
|
|
9
|
-
export type GraphPermissionResult = {
|
|
10
|
-
can: boolean;
|
|
11
|
-
reason: string;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// Permission to its result
|
|
15
|
-
export type GraphPermissionResults = Record<ActionName, GraphPermissionResult>;
|
|
16
|
-
|
|
17
|
-
// Resource type to map of resource ids to their permissions results
|
|
18
|
-
// Note: Resource IDs are string keys in the API response (JSON object keys are always strings)
|
|
19
|
-
export type GraphIsAllowedResponse = Record<ResourceType, Record<string, GraphPermissionResults>>;
|
package/src/types/roles.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export enum RoleType {
|
|
2
|
-
CUSTOM = 'custom_role',
|
|
3
|
-
BASIC = 'basic_role',
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface CustomRole {
|
|
7
|
-
id?: number;
|
|
8
|
-
name: string;
|
|
9
|
-
resourceType: string;
|
|
10
|
-
resourceId: number;
|
|
11
|
-
basicRoleId: number;
|
|
12
|
-
basicRoleType: RoleType;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface BasicRole {
|
|
16
|
-
id: number;
|
|
17
|
-
resourceType: string;
|
|
18
|
-
roleType: string;
|
|
19
|
-
name: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface RolesResponse {
|
|
23
|
-
customRoles: CustomRole[];
|
|
24
|
-
basicRoles?: BasicRole[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface RoleCreateRequest {
|
|
28
|
-
name: string;
|
|
29
|
-
resourceType: string;
|
|
30
|
-
resourceId: number;
|
|
31
|
-
sourceRole: {
|
|
32
|
-
id: number;
|
|
33
|
-
type: RoleType;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface RoleUpdateRequest {
|
|
38
|
-
id: number;
|
|
39
|
-
updateAttributes: {
|
|
40
|
-
name: string;
|
|
41
|
-
};
|
|
42
|
-
}
|