@node-c/domain-iam 1.0.0-alpha9 → 1.0.0-beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/common/definitions/common.constants.d.ts +7 -1
  2. package/dist/common/definitions/common.constants.js +6 -0
  3. package/dist/common/definitions/common.constants.js.map +1 -1
  4. package/dist/module/iam.module.js.map +1 -1
  5. package/dist/services/authentication/iam.authentication.definitions.d.ts +79 -16
  6. package/dist/services/authentication/iam.authentication.definitions.js +6 -9
  7. package/dist/services/authentication/iam.authentication.definitions.js.map +1 -1
  8. package/dist/services/authentication/iam.authentication.service.d.ts +13 -5
  9. package/dist/services/authentication/iam.authentication.service.js +32 -3
  10. package/dist/services/authentication/iam.authentication.service.js.map +1 -1
  11. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.d.ts +38 -0
  12. package/dist/services/{authenticationLocal/iam.authenticationLocal.definitions.js → authenticationOAuth2/iam.authenticationOAuth2.definitions.js} +1 -1
  13. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.js.map +1 -0
  14. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.d.ts +25 -0
  15. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.js +300 -0
  16. package/dist/services/authenticationOAuth2/iam.authenticationOAuth2.service.js.map +1 -0
  17. package/dist/services/authenticationOAuth2/index.d.ts +2 -0
  18. package/dist/services/authenticationOAuth2/index.js +19 -0
  19. package/dist/services/authenticationOAuth2/index.js.map +1 -0
  20. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.d.ts +12 -0
  21. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.js +3 -0
  22. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.js.map +1 -0
  23. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.d.ts +15 -0
  24. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.js +142 -0
  25. package/dist/services/authenticationUserLocal/iam.authenticationUserLocal.service.js.map +1 -0
  26. package/dist/services/authenticationUserLocal/index.d.ts +2 -0
  27. package/dist/services/{authenticationLocal → authenticationUserLocal}/index.js +2 -2
  28. package/dist/services/authenticationUserLocal/index.js.map +1 -0
  29. package/dist/services/authorization/iam.authorization.definitions.d.ts +33 -23
  30. package/dist/services/authorization/iam.authorization.definitions.js +7 -0
  31. package/dist/services/authorization/iam.authorization.definitions.js.map +1 -1
  32. package/dist/services/authorization/iam.authorization.service.d.ts +29 -13
  33. package/dist/services/authorization/iam.authorization.service.js +233 -125
  34. package/dist/services/authorization/iam.authorization.service.js.map +1 -1
  35. package/dist/services/index.d.ts +4 -2
  36. package/dist/services/index.js +4 -2
  37. package/dist/services/index.js.map +1 -1
  38. package/dist/services/mfa/iam.mfa.definitions.d.ts +21 -0
  39. package/dist/services/mfa/iam.mfa.definitions.js +8 -0
  40. package/dist/services/mfa/iam.mfa.definitions.js.map +1 -0
  41. package/dist/services/mfa/iam.mfa.service.d.ts +10 -0
  42. package/dist/services/mfa/iam.mfa.service.js +32 -0
  43. package/dist/services/mfa/iam.mfa.service.js.map +1 -0
  44. package/dist/services/mfa/index.d.ts +2 -0
  45. package/dist/services/{users → mfa}/index.js +2 -2
  46. package/dist/services/mfa/index.js.map +1 -0
  47. package/dist/services/tokenManager/iam.tokenManager.definitions.d.ts +14 -3
  48. package/dist/services/tokenManager/iam.tokenManager.definitions.js.map +1 -1
  49. package/dist/services/tokenManager/iam.tokenManager.service.d.ts +24 -9
  50. package/dist/services/tokenManager/iam.tokenManager.service.js +113 -44
  51. package/dist/services/tokenManager/iam.tokenManager.service.js.map +1 -1
  52. package/dist/services/userManager/iam.userManager.definitions.d.ts +45 -0
  53. package/dist/services/userManager/iam.userManager.definitions.js +8 -0
  54. package/dist/services/userManager/iam.userManager.definitions.js.map +1 -0
  55. package/dist/services/userManager/iam.userManager.service.d.ts +33 -0
  56. package/dist/services/userManager/iam.userManager.service.js +332 -0
  57. package/dist/services/userManager/iam.userManager.service.js.map +1 -0
  58. package/dist/services/userManager/index.d.ts +2 -0
  59. package/dist/services/userManager/index.js +19 -0
  60. package/dist/services/userManager/index.js.map +1 -0
  61. package/package.json +10 -8
  62. package/src/common/definitions/common.constants.ts +16 -0
  63. package/src/common/definitions/index.ts +1 -0
  64. package/src/index.ts +3 -0
  65. package/src/module/iam.definitions.ts +15 -0
  66. package/src/module/iam.module.ts +29 -0
  67. package/src/module/index.ts +2 -0
  68. package/src/services/authentication/iam.authentication.definitions.ts +100 -0
  69. package/src/services/authentication/iam.authentication.service.ts +105 -0
  70. package/src/services/authentication/index.ts +2 -0
  71. package/src/services/authenticationOAuth2/iam.authenticationOAuth2.definitions.ts +72 -0
  72. package/src/services/authenticationOAuth2/iam.authenticationOAuth2.service.ts +352 -0
  73. package/src/services/authenticationOAuth2/index.ts +2 -0
  74. package/src/services/authenticationUserLocal/iam.authenticationUserLocal.definitions.ts +29 -0
  75. package/src/services/authenticationUserLocal/iam.authenticationUserLocal.service.ts +173 -0
  76. package/src/services/authenticationUserLocal/index.ts +2 -0
  77. package/src/services/authorization/iam.authorization.definitions.ts +55 -0
  78. package/src/services/authorization/iam.authorization.service.ts +387 -0
  79. package/src/services/authorization/index.ts +2 -0
  80. package/src/services/index.ts +7 -0
  81. package/src/services/mfa/iam.mfa.definitions.ts +28 -0
  82. package/src/services/mfa/iam.mfa.service.ts +40 -0
  83. package/src/services/mfa/index.ts +2 -0
  84. package/src/services/tokenManager/iam.tokenManager.definitions.ts +61 -0
  85. package/src/services/tokenManager/iam.tokenManager.service.ts +292 -0
  86. package/src/services/tokenManager/index.ts +2 -0
  87. package/src/services/userManager/iam.userManager.definitions.ts +73 -0
  88. package/src/services/userManager/iam.userManager.service.ts +463 -0
  89. package/src/services/userManager/index.ts +2 -0
  90. package/dist/services/authenticationLocal/iam.authenticationLocal.definitions.d.ts +0 -11
  91. package/dist/services/authenticationLocal/iam.authenticationLocal.definitions.js.map +0 -1
  92. package/dist/services/authenticationLocal/iam.authenticationLocal.service.d.ts +0 -10
  93. package/dist/services/authenticationLocal/iam.authenticationLocal.service.js +0 -70
  94. package/dist/services/authenticationLocal/iam.authenticationLocal.service.js.map +0 -1
  95. package/dist/services/authenticationLocal/index.d.ts +0 -2
  96. package/dist/services/authenticationLocal/index.js.map +0 -1
  97. package/dist/services/users/iam.users.definitions.d.ts +0 -30
  98. package/dist/services/users/iam.users.definitions.js +0 -8
  99. package/dist/services/users/iam.users.definitions.js.map +0 -1
  100. package/dist/services/users/iam.users.service.d.ts +0 -16
  101. package/dist/services/users/iam.users.service.js +0 -93
  102. package/dist/services/users/iam.users.service.js.map +0 -1
  103. package/dist/services/users/index.d.ts +0 -2
  104. package/dist/services/users/index.js.map +0 -1
@@ -0,0 +1,173 @@
1
+ import crypto from 'crypto';
2
+
3
+ import {
4
+ AppConfigDomainIAM,
5
+ AppConfigDomainIAMAuthenticationStep,
6
+ ApplicationError,
7
+ ConfigProviderService,
8
+ LoggerService
9
+ } from '@node-c/core';
10
+
11
+ import ld from 'lodash';
12
+
13
+ import {
14
+ IAMAuthenticationUserLocalCompleteData,
15
+ IAMAuthenticationUserLocalCompleteOptions,
16
+ IAMAuthenticationUserLocalCompleteResult,
17
+ IAMAuthenticationUserLocalGetUserCreateAccessTokenConfigResult,
18
+ IAMAuthenticationUserLocalInitiateData,
19
+ IAMAuthenticationUserLocalInitiateOptions,
20
+ IAMAuthenticationUserLocalInitiateResult
21
+ } from './iam.authenticationUserLocal.definitions';
22
+
23
+ import { IAMAuthenticationService } from '../authentication';
24
+ import { IAMMFAService, IAMMFAType } from '../mfa';
25
+
26
+ // TODO: add a LocalSecret service to take care of the hashing logic and reuse it here
27
+ export class IAMAuthenticationUserLocalService<
28
+ CompleteContext extends object,
29
+ InitiateContext extends object
30
+ > extends IAMAuthenticationService<CompleteContext, InitiateContext> {
31
+ constructor(
32
+ protected configProvider: ConfigProviderService,
33
+ protected logger: LoggerService,
34
+ protected moduleName: string,
35
+ // eslint-disable-next-line no-unused-vars
36
+ protected serviceName: string,
37
+ // eslint-disable-next-line no-unused-vars
38
+ protected mfaServices?: Record<IAMMFAType, IAMMFAService<object, object>>
39
+ ) {
40
+ super(configProvider, logger, moduleName);
41
+ this.isLocal = true;
42
+ }
43
+
44
+ async complete(
45
+ data: IAMAuthenticationUserLocalCompleteData,
46
+ options: IAMAuthenticationUserLocalCompleteOptions<CompleteContext>
47
+ ): Promise<IAMAuthenticationUserLocalCompleteResult> {
48
+ const { configProvider, logger, moduleName, mfaServices, serviceName } = this;
49
+ const { defaultUserIdentifierField } = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
50
+ const { mfaData, mfaType } = data;
51
+ const { context, mfaOptions } = options;
52
+ const userIdentifierField = options.contextIdentifierField || defaultUserIdentifierField;
53
+ const userIdentifierValue = context[userIdentifierField as keyof CompleteContext];
54
+ let mfaUsed = false;
55
+ let mfaValid = false;
56
+ if (mfaType) {
57
+ const mfaService = mfaServices?.[mfaType];
58
+ if (!mfaService) {
59
+ logger.error(
60
+ `[${moduleName}][${serviceName}]: Login attempt failed for user "${userIdentifierValue}" - MFA service ${mfaType} not configured.`
61
+ );
62
+ throw new ApplicationError('Authentication failed.');
63
+ }
64
+ if (!mfaData) {
65
+ logger.error(
66
+ `[${moduleName}][${serviceName}]: Login attempt failed for user "${userIdentifierValue}" - no MFA data provided.`
67
+ );
68
+ throw new ApplicationError('Authentication failed.');
69
+ }
70
+ const mfaResult = await mfaService.complete(mfaData, { ...(mfaOptions || {}), context });
71
+ mfaUsed = true;
72
+ mfaValid = mfaResult.valid;
73
+ }
74
+ return { mfaUsed, mfaValid, valid: true };
75
+ }
76
+
77
+ getUserCreateAccessTokenConfig(): IAMAuthenticationUserLocalGetUserCreateAccessTokenConfigResult {
78
+ const { configProvider, moduleName, serviceName } = this;
79
+ const moduleConfig = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
80
+ const { steps } = moduleConfig.authServiceSettings![serviceName];
81
+ const defaultConfig: IAMAuthenticationUserLocalGetUserCreateAccessTokenConfigResult = {
82
+ [AppConfigDomainIAMAuthenticationStep.Complete]: {
83
+ cache: {
84
+ settings: {
85
+ cacheFieldName: 'userId',
86
+ inputFieldName: 'options.context.id'
87
+ },
88
+ use: {
89
+ options: { overwrite: true, use: true }
90
+ }
91
+ },
92
+ findUser: true,
93
+ findUserBeforeAuth: true,
94
+ validWithoutUser: false
95
+ },
96
+ [AppConfigDomainIAMAuthenticationStep.Initiate]: {
97
+ cache: {
98
+ populate: {
99
+ options: [{ cacheFieldName: 'context', inputFieldName: 'options.context' }]
100
+ },
101
+ settings: {
102
+ cacheFieldName: 'userId',
103
+ inputFieldName: 'options.context.id'
104
+ }
105
+ },
106
+ findUser: true,
107
+ findUserBeforeAuth: true,
108
+ validWithoutUser: false
109
+ }
110
+ };
111
+ return ld.merge(defaultConfig, steps || {});
112
+ }
113
+
114
+ async initiate(
115
+ data: IAMAuthenticationUserLocalInitiateData,
116
+ options: IAMAuthenticationUserLocalInitiateOptions<InitiateContext>
117
+ ): Promise<IAMAuthenticationUserLocalInitiateResult> {
118
+ const { configProvider, logger, moduleName, mfaServices, serviceName } = this;
119
+ const moduleConfig = configProvider.config.domain[moduleName] as AppConfigDomainIAM;
120
+ const { secretKeyHMACAlgorithm, hashingSecret } = moduleConfig.authServiceSettings![serviceName].secretKey!;
121
+ const { mfaData, mfaType, password: authPassword } = data;
122
+ const {
123
+ context,
124
+ context: { password: userPassword },
125
+ mfaOptions
126
+ } = options;
127
+ const userIdentifierField = options.contextIdentifierField || moduleConfig.defaultUserIdentifierField;
128
+ const userIdentifierValue = context[userIdentifierField as keyof InitiateContext];
129
+ let mfaUsed = false;
130
+ let mfaValid = false;
131
+ let wrongPassword = false;
132
+ if (!secretKeyHMACAlgorithm || !hashingSecret || !userPassword) {
133
+ wrongPassword = true;
134
+ logger.error(
135
+ `[${moduleName}][${serviceName}]: secretKeyHMACAlgorithm, hashingSecret and/or userPassword not provided.`
136
+ );
137
+ } else {
138
+ const computedPassword = crypto
139
+ .createHmac(secretKeyHMACAlgorithm, hashingSecret)
140
+ .update(`${authPassword}`)
141
+ .digest('hex')
142
+ .toString();
143
+ if (computedPassword !== userPassword) {
144
+ wrongPassword = true;
145
+ }
146
+ }
147
+ if (wrongPassword) {
148
+ logger.error(
149
+ `[${moduleName}][${serviceName}]: Login attempt failed for user "${userIdentifierValue}" - wrong password.`
150
+ );
151
+ throw new ApplicationError('Authentication failed.');
152
+ }
153
+ if (mfaType) {
154
+ const mfaService = mfaServices?.[mfaType];
155
+ if (!mfaService) {
156
+ logger.error(
157
+ `[${moduleName}][${serviceName}]: Login attempt failed for user "${userIdentifierValue}" - MFA service ${mfaType} not configured.`
158
+ );
159
+ throw new ApplicationError('Authentication failed.');
160
+ }
161
+ if (!mfaData) {
162
+ logger.error(
163
+ `[${moduleName}][${serviceName}]: Login attempt failed for user "${userIdentifierValue}" - no MFA data provided.`
164
+ );
165
+ throw new ApplicationError('Authentication failed.');
166
+ }
167
+ const mfaResult = await mfaService.initiate(mfaData, { ...(mfaOptions || {}), context });
168
+ mfaUsed = true;
169
+ mfaValid = mfaResult.valid;
170
+ }
171
+ return { mfaUsed, mfaValid, valid: true };
172
+ }
173
+ }
@@ -0,0 +1,2 @@
1
+ export * from './iam.authenticationUserLocal.definitions';
2
+ export * from './iam.authenticationUserLocal.service';
@@ -0,0 +1,55 @@
1
+ import { GenericObject } from '@node-c/core';
2
+
3
+ export enum AuthorizationCheckErrorCode {
4
+ // eslint-disable-next-line no-unused-vars
5
+ FGANoAccessToModule = 'FGA_NO_ACCESS',
6
+ // eslint-disable-next-line no-unused-vars
7
+ RBACNoAccessToModule = 'RBAC_NO_ACCESS_TO_MODULE',
8
+ // eslint-disable-next-line no-unused-vars
9
+ RBACNoAccessToResource = 'RBAC_NO_ACCESS_TO_RESOURCE'
10
+ }
11
+
12
+ export interface AuthorizationPoint<Id> {
13
+ allowedInputData?: GenericObject;
14
+ allowedOutputData?: GenericObject;
15
+ forbiddenInputData?: GenericObject;
16
+ forbiddenOutputData?: GenericObject;
17
+ id: Id;
18
+ inputDataFieldName?: string;
19
+ moduleName: string;
20
+ name: string;
21
+ requiredStaticData?: GenericObject;
22
+ resources?: string[];
23
+ // required when resources is set
24
+ resourceContext?: string;
25
+ userFieldName?: string;
26
+ // userTypes: GenericObject[];
27
+ }
28
+
29
+ export interface AuthorizationStaticCheckAccessOptions {
30
+ moduleName: string;
31
+ resource?: string;
32
+ resourceContext?: string;
33
+ }
34
+
35
+ export interface AuthorizationStaticCheckAccessResult {
36
+ authorizationPoints: GenericObject<AuthorizationPoint<unknown>>;
37
+ errorCode?: AuthorizationCheckErrorCode;
38
+ hasAccess: boolean;
39
+ inputDataToBeMutated: GenericObject;
40
+ noMatchForResource: boolean;
41
+ }
42
+
43
+ export interface AuthorizationUser<AuthorizationPointId> {
44
+ currentAuthorizationPoints: GenericObject<AuthorizationPoint<AuthorizationPointId>>;
45
+ }
46
+
47
+ export interface AuthorizeApiKeyData {
48
+ apiKey: string;
49
+ signature?: string;
50
+ signatureContent?: string;
51
+ }
52
+
53
+ export interface AuthorizeApiKeyOptions {
54
+ config: { apiKey?: string; apiSecret?: string; apiSecretAlgorithm?: string };
55
+ }
@@ -0,0 +1,387 @@
1
+ import crypto from 'crypto';
2
+
3
+ import {
4
+ ApplicationError,
5
+ DataEntityService,
6
+ DomainEntityService,
7
+ DomainEntityServiceDefaultData,
8
+ DomainMethod,
9
+ GenericObject,
10
+ LoggerService,
11
+ getNested,
12
+ setNested
13
+ } from '@node-c/core';
14
+
15
+ import ld from 'lodash';
16
+
17
+ import {
18
+ AuthorizationCheckErrorCode,
19
+ AuthorizationStaticCheckAccessOptions,
20
+ AuthorizationStaticCheckAccessResult,
21
+ AuthorizationUser,
22
+ AuthorizeApiKeyData,
23
+ AuthorizeApiKeyOptions,
24
+ AuthorizationPoint as BaseAuthorizationPoint
25
+ } from './iam.authorization.definitions';
26
+
27
+ import { DecodedTokenContent, IAMTokenManagerService } from '../tokenManager';
28
+
29
+ export class IAMAuthorizationService<
30
+ AuthorizationPoint extends BaseAuthorizationPoint<unknown> = BaseAuthorizationPoint<unknown>,
31
+ Data extends DomainEntityServiceDefaultData<Partial<AuthorizationPoint>> = DomainEntityServiceDefaultData<
32
+ Partial<AuthorizationPoint>
33
+ >,
34
+ TokenManager extends IAMTokenManagerService<object> = IAMTokenManagerService<object>
35
+ > extends DomainEntityService<
36
+ AuthorizationPoint,
37
+ DataEntityService<AuthorizationPoint>,
38
+ Data,
39
+ Record<string, DataEntityService<Partial<AuthorizationPoint>>> | undefined
40
+ > {
41
+ constructor(
42
+ protected dataAuthorizationPointsService: DataEntityService<AuthorizationPoint>,
43
+ protected defaultMethods: string[] = [DomainMethod.Find],
44
+ protected logger: LoggerService,
45
+ protected additionalDataEntityServices?: GenericObject<DataEntityService<Partial<AuthorizationPoint>>>,
46
+ // eslint-disable-next-line no-unused-vars
47
+ protected tokenManager?: TokenManager
48
+ ) {
49
+ super(dataAuthorizationPointsService, defaultMethods, logger, additionalDataEntityServices);
50
+ }
51
+
52
+ async authorizeApiKey(data: AuthorizeApiKeyData, options: AuthorizeApiKeyOptions): Promise<{ valid: boolean }> {
53
+ const { logger } = this;
54
+ const { apiKey, signature, signatureContent } = data;
55
+ const {
56
+ config: { apiKey: expectedApiKey, apiSecret, apiSecretAlgorithm }
57
+ } = options;
58
+ if (!apiKey) {
59
+ logger.error('Missing api key.');
60
+ return { valid: false };
61
+ }
62
+ if (apiKey !== expectedApiKey) {
63
+ logger.error('Invalid api key.');
64
+ return { valid: false };
65
+ }
66
+ if (apiSecret && apiSecretAlgorithm) {
67
+ if (!signature) {
68
+ logger.error('Missing authorization signature.');
69
+ return { valid: false };
70
+ }
71
+ if (!signatureContent) {
72
+ logger.error('Missing authorization signature content.');
73
+ return { valid: false };
74
+ }
75
+ const calcualtedSignature = crypto
76
+ .createHmac(apiSecretAlgorithm, apiSecret)
77
+ .update(signatureContent)
78
+ .digest('hex');
79
+ if (calcualtedSignature !== signature) {
80
+ logger.error(`Invalid signature provided. Expected: ${calcualtedSignature}. Provided: ${signature}`);
81
+ return { valid: false };
82
+ }
83
+ }
84
+ return { valid: true };
85
+ }
86
+
87
+ // TODO: decouple from users
88
+ async authorizeBearer<UserTokenEnityFields = unknown>(
89
+ data: { authToken?: string; refreshToken?: string },
90
+ options?: { identifierDataField?: string }
91
+ ): Promise<{ newAuthToken?: string; tokenContent?: DecodedTokenContent<UserTokenEnityFields>; valid: boolean }> {
92
+ const { logger, tokenManager } = this;
93
+ const { authToken, refreshToken } = data;
94
+ const { identifierDataField } = options || {};
95
+ if (!tokenManager) {
96
+ logger.error('Token manager not configured.');
97
+ return { valid: false };
98
+ }
99
+ if (!authToken) {
100
+ logger.error('Missing auth token.');
101
+ return { valid: false };
102
+ }
103
+ let newAuthToken: string | undefined;
104
+ let tokenContent: DecodedTokenContent<UserTokenEnityFields> | undefined;
105
+ try {
106
+ const tokenRes = await tokenManager.verifyAccessToken(authToken, {
107
+ deleteFromStoreIfExpired: true,
108
+ identifierDataField,
109
+ persistNewToken: true,
110
+ purgeStoreOnRenew: true,
111
+ refreshToken,
112
+ refreshTokenAccessTokenIdentifierDataField: 'accessToken'
113
+ });
114
+ tokenContent = tokenRes.content as unknown as DecodedTokenContent<UserTokenEnityFields>;
115
+ if (tokenRes.newToken) {
116
+ newAuthToken = tokenRes.newToken;
117
+ }
118
+ } catch (e) {
119
+ logger.error('Failed to parse the access or refresh token:', e);
120
+ return { valid: false };
121
+ }
122
+ return { newAuthToken, tokenContent, valid: true };
123
+ }
124
+
125
+ async checkAccessWithStorage(): Promise<void> {
126
+ throw new ApplicationError('[IAMAuthorizationService.checkAccessWithStorage]: Method not implemented.');
127
+ }
128
+
129
+ static checkAccess<InputData = GenericObject>(
130
+ inputData: InputData,
131
+ user: AuthorizationUser<unknown>,
132
+ options: AuthorizationStaticCheckAccessOptions
133
+ ): AuthorizationStaticCheckAccessResult {
134
+ const { moduleName, resourceContext, resource } = options;
135
+ let hasResource = false;
136
+ if (resource) {
137
+ if (!resourceContext) {
138
+ throw new ApplicationError(
139
+ '[IAMAuthorizationService.checkAccess]: A resourceContext is required when providing a resource value.'
140
+ );
141
+ }
142
+ hasResource = true;
143
+ }
144
+ // check the access to the found authorization points
145
+ const mutatedInputData = ld.cloneDeep(inputData);
146
+ const usedAuthorizationPoints: GenericObject<BaseAuthorizationPoint<unknown>> = {};
147
+ const { currentAuthorizationPoints } = user;
148
+ let authorizationPointsCount = 0;
149
+ let authorizationPointsForDifferentModules = 0;
150
+ let authorizationPointsForDifferentContexts = 0;
151
+ let hasAccess = false;
152
+ let inputDataToBeMutated: GenericObject = {};
153
+ let noMatchForResource = false;
154
+ for (const apId in currentAuthorizationPoints) {
155
+ const apData = currentAuthorizationPoints[apId];
156
+ authorizationPointsCount++;
157
+ // RBAC - check whether the user has general access to the module.
158
+ if (moduleName !== apData.moduleName) {
159
+ authorizationPointsForDifferentModules++;
160
+ continue;
161
+ }
162
+ // RBAC - check whether the user has general access to the resource.
163
+ if (
164
+ hasResource &&
165
+ (!apData.resourceContext ||
166
+ apData.resourceContext !== resourceContext ||
167
+ !apData.resources?.includes(resource!))
168
+ ) {
169
+ authorizationPointsForDifferentContexts++;
170
+ continue;
171
+ }
172
+ // FGA - check whether the user has access based on specific input and user fields.
173
+ const { allowedInputData, forbiddenInputData, inputDataFieldName, requiredStaticData, userFieldName } = apData;
174
+ const hasStaticData = requiredStaticData && Object.keys(requiredStaticData).length;
175
+ const innerMutatedInputData = ld.cloneDeep(mutatedInputData) as GenericObject;
176
+ const innerInputDataToBeMutated: GenericObject = {};
177
+ hasAccess = true;
178
+ if (!noMatchForResource) {
179
+ noMatchForResource = true;
180
+ }
181
+ // 1. Required static data
182
+ if (hasStaticData) {
183
+ for (const fieldName in requiredStaticData) {
184
+ if (
185
+ !IAMAuthorizationService.testValue(
186
+ getNested({ inputData: innerMutatedInputData, user }, fieldName, { removeNestedFieldEscapeSign: true })
187
+ .unifiedValue,
188
+ requiredStaticData[fieldName]
189
+ )
190
+ ) {
191
+ hasAccess = false;
192
+ break;
193
+ }
194
+ }
195
+ if (!hasAccess) {
196
+ continue;
197
+ }
198
+ }
199
+ // 2. User field data vs input field data.
200
+ if (userFieldName && inputDataFieldName) {
201
+ const { paths: inputFieldPaths, unifiedValue: inputFieldValue } = getNested(
202
+ innerMutatedInputData,
203
+ inputDataFieldName,
204
+ {
205
+ removeNestedFieldEscapeSign: true
206
+ }
207
+ );
208
+ const { unifiedValue: userFieldValue } = getNested(user, userFieldName, { removeNestedFieldEscapeSign: true });
209
+ if (typeof userFieldValue === 'undefined') {
210
+ hasAccess = false;
211
+ continue;
212
+ }
213
+ if (typeof inputFieldValue === 'undefined') {
214
+ innerInputDataToBeMutated[inputDataFieldName] = userFieldValue;
215
+ setNested(innerMutatedInputData, inputDataFieldName, userFieldValue, {
216
+ removeNestedFieldEscapeSign: true,
217
+ setNestedArraysPerIndex: inputFieldPaths.length > 1
218
+ });
219
+ } else {
220
+ const allowedValues = IAMAuthorizationService.matchInputValues(innerMutatedInputData, {
221
+ [inputDataFieldName]: userFieldValue
222
+ })[inputDataFieldName] as unknown[];
223
+ const inputValueIsArray = inputFieldValue instanceof Array;
224
+ if (!allowedValues?.length) {
225
+ hasAccess = false;
226
+ continue;
227
+ }
228
+ if (inputValueIsArray) {
229
+ innerInputDataToBeMutated[inputDataFieldName] = allowedValues;
230
+ setNested(innerMutatedInputData, inputDataFieldName, allowedValues, { removeNestedFieldEscapeSign: true });
231
+ }
232
+ }
233
+ }
234
+ // 3. Input data whitelist
235
+ // WARNING: In an expressjs v5+ environment, this will only work properly if the query is mutable
236
+ if (allowedInputData && Object.keys(allowedInputData).length) {
237
+ const values = IAMAuthorizationService.matchInputValues(innerMutatedInputData, allowedInputData);
238
+ for (const key in values) {
239
+ innerInputDataToBeMutated[key] = values[key];
240
+ setNested(innerMutatedInputData, key, values[key], { removeNestedFieldEscapeSign: true });
241
+ }
242
+ }
243
+ // 4. Input data blacklist
244
+ if (forbiddenInputData && Object.keys(forbiddenInputData).length) {
245
+ const values = IAMAuthorizationService.matchInputValues(innerMutatedInputData, forbiddenInputData);
246
+ for (const key in values) {
247
+ innerInputDataToBeMutated[key] = undefined;
248
+ setNested(innerMutatedInputData, key, undefined, { removeNestedFieldEscapeSign: true });
249
+ }
250
+ }
251
+ inputDataToBeMutated = ld.merge(inputDataToBeMutated, innerInputDataToBeMutated);
252
+ usedAuthorizationPoints[apId] = apData;
253
+ break;
254
+ }
255
+ const returnData: AuthorizationStaticCheckAccessResult = {
256
+ authorizationPoints: usedAuthorizationPoints,
257
+ hasAccess,
258
+ inputDataToBeMutated,
259
+ noMatchForResource
260
+ };
261
+ if (!hasAccess) {
262
+ if (authorizationPointsForDifferentModules === authorizationPointsCount) {
263
+ returnData.errorCode = AuthorizationCheckErrorCode.RBACNoAccessToModule;
264
+ } else if (authorizationPointsForDifferentContexts === authorizationPointsCount) {
265
+ returnData.errorCode = AuthorizationCheckErrorCode.RBACNoAccessToResource;
266
+ } else {
267
+ returnData.errorCode = AuthorizationCheckErrorCode.FGANoAccessToModule;
268
+ }
269
+ }
270
+ return returnData;
271
+ }
272
+
273
+ static getValuesForTesting(valueToTest: unknown): unknown[] {
274
+ const values = [
275
+ valueToTest, // the value as-is
276
+ parseInt(valueToTest as string, 10), // the int equivalent of the value
277
+ parseFloat(valueToTest as string) // the float equivalent of the value
278
+ ];
279
+ // the boolean equivalent of the values
280
+ if (valueToTest === 'true') {
281
+ values.push(true);
282
+ } else if (valueToTest === 'false') {
283
+ values.push(false);
284
+ }
285
+ return values;
286
+ }
287
+
288
+ static matchInputValues(input: GenericObject, values: GenericObject): GenericObject {
289
+ const matchedValues: GenericObject = {};
290
+ for (const fieldName in values) {
291
+ const { paths: valuePaths, values: foundValues } = getNested(input, fieldName, {
292
+ removeNestedFieldEscapeSign: true
293
+ });
294
+ const allowedValue = values[fieldName];
295
+ const allowedValues = allowedValue instanceof Array ? allowedValue : [allowedValue];
296
+ const valuesToSet: unknown[] = [];
297
+ valuePaths.forEach((valuePath, valuePathIndex) => {
298
+ const valueAtIndex = foundValues[valuePathIndex];
299
+ let valueIsArray = false;
300
+ let valuesToCheck: unknown[] = [];
301
+ if (valueAtIndex instanceof Array) {
302
+ valuesToCheck = valueAtIndex;
303
+ valueIsArray = true;
304
+ } else {
305
+ valuesToCheck.push(valueAtIndex);
306
+ }
307
+ valuesToCheck.forEach(valueToCheck => {
308
+ for (const j in allowedValues) {
309
+ if (IAMAuthorizationService.testValue(valueToCheck, allowedValues[j])) {
310
+ valuesToSet.push(valueToCheck);
311
+ break;
312
+ }
313
+ }
314
+ });
315
+ if (!valuesToSet.length) {
316
+ matchedValues[valuePath] = undefined;
317
+ return;
318
+ }
319
+ matchedValues[valuePath] = valueIsArray ? valuesToSet : valuesToSet[0];
320
+ });
321
+ }
322
+ return matchedValues;
323
+ }
324
+
325
+ static processOutputData(
326
+ authorizationPoints: { [id: number]: BaseAuthorizationPoint<unknown> },
327
+ outputData: GenericObject
328
+ ): {
329
+ outputDataToBeMutated: GenericObject;
330
+ } {
331
+ const mutatedOutputData = ld.cloneDeep(outputData);
332
+ let outputDataToBeMutated: GenericObject = {};
333
+ for (const apId in authorizationPoints) {
334
+ const apData = authorizationPoints[apId];
335
+ const { allowedOutputData, forbiddenOutputData } = apData;
336
+ const innerMutatedOutputData = ld.cloneDeep(mutatedOutputData);
337
+ const innerOutputDataToBeMutated: GenericObject = {};
338
+ if (allowedOutputData && Object.keys(allowedOutputData).length) {
339
+ const values = IAMAuthorizationService.matchInputValues(innerMutatedOutputData, allowedOutputData);
340
+ for (const key in values) {
341
+ innerOutputDataToBeMutated[key] = values[key];
342
+ setNested(innerMutatedOutputData, key, values[key], { removeNestedFieldEscapeSign: true });
343
+ }
344
+ }
345
+ if (forbiddenOutputData && Object.keys(forbiddenOutputData).length) {
346
+ const values = IAMAuthorizationService.matchInputValues(innerMutatedOutputData, forbiddenOutputData);
347
+ for (const key in values) {
348
+ innerOutputDataToBeMutated[key] = undefined;
349
+ setNested(innerMutatedOutputData, key, undefined, { removeNestedFieldEscapeSign: true });
350
+ }
351
+ }
352
+ outputDataToBeMutated = ld.merge(outputDataToBeMutated, innerOutputDataToBeMutated);
353
+ }
354
+ return { outputDataToBeMutated };
355
+ }
356
+
357
+ static testValue(valueToTest: unknown, valueToTestAgainst: unknown): boolean {
358
+ if (
359
+ typeof valueToTestAgainst === 'string' &&
360
+ valueToTestAgainst.charAt(0) === '/' &&
361
+ valueToTestAgainst.charAt(valueToTestAgainst.length - 1) === '/'
362
+ ) {
363
+ const regex = new RegExp(valueToTestAgainst.substring(1, valueToTestAgainst.length - 1));
364
+ if (typeof valueToTest === 'undefined') {
365
+ return false;
366
+ }
367
+ return regex.test(typeof valueToTest === 'string' ? valueToTest : JSON.stringify(valueToTest));
368
+ }
369
+ if (
370
+ typeof valueToTest === 'object' &&
371
+ valueToTest !== null &&
372
+ typeof valueToTestAgainst === 'object' &&
373
+ valueToTestAgainst !== null
374
+ ) {
375
+ return JSON.stringify(valueToTest) === JSON.stringify(valueToTestAgainst);
376
+ }
377
+ const possibleValidValues = IAMAuthorizationService.getValuesForTesting(valueToTest);
378
+ let hasMatch = false;
379
+ for (const i in possibleValidValues) {
380
+ if (possibleValidValues[i] === valueToTestAgainst) {
381
+ hasMatch = true;
382
+ break;
383
+ }
384
+ }
385
+ return hasMatch;
386
+ }
387
+ }
@@ -0,0 +1,2 @@
1
+ export * from './iam.authorization.definitions';
2
+ export * from './iam.authorization.service';
@@ -0,0 +1,7 @@
1
+ export * from './authentication';
2
+ export * from './authenticationOAuth2';
3
+ export * from './authenticationUserLocal';
4
+ export * from './authorization';
5
+ export * from './mfa';
6
+ export * from './tokenManager';
7
+ export * from './userManager';
@@ -0,0 +1,28 @@
1
+ export interface IAMMFACompleteData {
2
+ type?: IAMMFAType;
3
+ }
4
+
5
+ export interface IAMMFACompleteOptions<Context> {
6
+ context: Context;
7
+ }
8
+
9
+ export enum IAMMFAType {
10
+ // eslint-disable-next-line no-unused-vars
11
+ Local = 'local'
12
+ }
13
+
14
+ export interface IAMMFACompleteResult {
15
+ valid: boolean;
16
+ }
17
+
18
+ export interface IAMMFAInitiateData {
19
+ type?: IAMMFAType;
20
+ }
21
+
22
+ export interface IAMMFAInitiateOptions<Context> {
23
+ context: Context;
24
+ }
25
+
26
+ export interface IAMMFAInitiateResult {
27
+ valid: boolean;
28
+ }