prow-authority 3.6.3

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 (168) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/.dockerignore +11 -0
  3. package/.github/workflows/_deploy.yml +191 -0
  4. package/.github/workflows/deploy-dev.yml +30 -0
  5. package/.github/workflows/deploy-prod.yml +34 -0
  6. package/CLAUDE.md +114 -0
  7. package/Dockerfile +61 -0
  8. package/Dockerfile.dev +24 -0
  9. package/README.md +1 -0
  10. package/package.json +61 -0
  11. package/src/App.ts +40 -0
  12. package/src/helpers/api-error.ts +21 -0
  13. package/src/helpers/authority.ts +216 -0
  14. package/src/helpers/config.ts +4 -0
  15. package/src/helpers/holded.ts +105 -0
  16. package/src/helpers/mysql.ts +15 -0
  17. package/src/helpers/notifications.ts +55 -0
  18. package/src/helpers/random.ts +18 -0
  19. package/src/helpers/s3.ts +43 -0
  20. package/src/helpers/stripe.ts +8 -0
  21. package/src/index.ts +47 -0
  22. package/src/v1/common-types.ts +12 -0
  23. package/src/v1/controllers/admin/accounting-seat-controller.ts +148 -0
  24. package/src/v1/controllers/admin/billing-cycle-controller.ts +171 -0
  25. package/src/v1/controllers/admin/invoice-controller.ts +222 -0
  26. package/src/v1/controllers/admin/license-controller.ts +190 -0
  27. package/src/v1/controllers/admin/license-limit-controller.ts +160 -0
  28. package/src/v1/controllers/admin/notification-controller.ts +115 -0
  29. package/src/v1/controllers/admin/organization-controller.ts +210 -0
  30. package/src/v1/controllers/admin/organization-license-controller.ts +224 -0
  31. package/src/v1/controllers/admin/organization-license-limit-controller.ts +196 -0
  32. package/src/v1/controllers/admin/organization-usage-controller.ts +167 -0
  33. package/src/v1/controllers/admin/payment-method-controller.ts +173 -0
  34. package/src/v1/controllers/admin/policy-controller.ts +138 -0
  35. package/src/v1/controllers/admin/policy-version-controller.ts +143 -0
  36. package/src/v1/controllers/admin/schedule-controller.ts +181 -0
  37. package/src/v1/controllers/admin/service-controller.ts +206 -0
  38. package/src/v1/controllers/admin/service-notification-controller.ts +166 -0
  39. package/src/v1/controllers/admin/user-controller.ts +282 -0
  40. package/src/v1/controllers/admin/user-organization-controller.ts +172 -0
  41. package/src/v1/controllers/admin/user-policy-controller.ts +164 -0
  42. package/src/v1/controllers/auth/@deprecated oauth-controller.ts +191 -0
  43. package/src/v1/controllers/auth/identity-token-controller.ts +286 -0
  44. package/src/v1/controllers/auth/organization-token-controller.ts +277 -0
  45. package/src/v1/controllers/auth/temp-code-controller.ts +60 -0
  46. package/src/v1/controllers/billing/billing-controller.ts +805 -0
  47. package/src/v1/controllers/organizations/public-organization-controller.ts +1796 -0
  48. package/src/v1/controllers/policies/public-policy-controller.ts +68 -0
  49. package/src/v1/controllers/services/public-service-controller.ts +177 -0
  50. package/src/v1/controllers/services/service-controller.ts +474 -0
  51. package/src/v1/controllers/users/public-user-controller.ts +471 -0
  52. package/src/v1/daos/@deprecated auth-dao.ts +173 -0
  53. package/src/v1/daos/accounting-seat-dao.ts +111 -0
  54. package/src/v1/daos/billing-cycle-dao.ts +107 -0
  55. package/src/v1/daos/invoice-dao.ts +131 -0
  56. package/src/v1/daos/license-dao.ts +152 -0
  57. package/src/v1/daos/license-limit-dao.ts +120 -0
  58. package/src/v1/daos/organization-dao.ts +109 -0
  59. package/src/v1/daos/organization-license-dao.ts +117 -0
  60. package/src/v1/daos/organization-license-limit-dao.ts +157 -0
  61. package/src/v1/daos/organization-usage-dao.ts +115 -0
  62. package/src/v1/daos/payment-method-dao.ts +110 -0
  63. package/src/v1/daos/policy-dao.ts +105 -0
  64. package/src/v1/daos/policy-version-dao.ts +112 -0
  65. package/src/v1/daos/redis-dao.ts +38 -0
  66. package/src/v1/daos/schema.sql +462 -0
  67. package/src/v1/daos/service-dao.ts +118 -0
  68. package/src/v1/daos/service-notification-dao.ts +120 -0
  69. package/src/v1/daos/user-dao.ts +127 -0
  70. package/src/v1/daos/user-organization-dao.ts +123 -0
  71. package/src/v1/daos/user-policy-dao.ts +109 -0
  72. package/src/v1/middlewares/auth/identity-token-middleware.ts +51 -0
  73. package/src/v1/middlewares/auth/organization-token-middleware.ts +73 -0
  74. package/src/v1/middlewares/auth/service-token-middleware.ts +167 -0
  75. package/src/v1/middlewares/authority-middleware.ts +97 -0
  76. package/src/v1/middlewares/organizations/organization-middleware.ts +94 -0
  77. package/src/v1/middlewares/static/static-middleware.ts +41 -0
  78. package/src/v1/middlewares/users/user-middleware.ts +41 -0
  79. package/src/v1/routes/admin/accounting-seat-router.ts +89 -0
  80. package/src/v1/routes/admin/admin-router.ts +61 -0
  81. package/src/v1/routes/admin/billing-cycle-router.ts +89 -0
  82. package/src/v1/routes/admin/invoice-router.ts +89 -0
  83. package/src/v1/routes/admin/license-limit-router.ts +89 -0
  84. package/src/v1/routes/admin/license-router.ts +89 -0
  85. package/src/v1/routes/admin/notification-router.ts +61 -0
  86. package/src/v1/routes/admin/organization-license-limit-router.ts +89 -0
  87. package/src/v1/routes/admin/organization-license-router.ts +89 -0
  88. package/src/v1/routes/admin/organization-router.ts +89 -0
  89. package/src/v1/routes/admin/payment-method-router.ts +89 -0
  90. package/src/v1/routes/admin/policy-router.ts +89 -0
  91. package/src/v1/routes/admin/policy-version-router.ts +89 -0
  92. package/src/v1/routes/admin/schedule-router.ts +75 -0
  93. package/src/v1/routes/admin/service-notification-router.ts +98 -0
  94. package/src/v1/routes/admin/service-router.ts +89 -0
  95. package/src/v1/routes/admin/user-organization-router.ts +89 -0
  96. package/src/v1/routes/admin/user-policy-router.ts +89 -0
  97. package/src/v1/routes/admin/user-router.ts +89 -0
  98. package/src/v1/routes/auth/@deprecated auth-router.ts +199 -0
  99. package/src/v1/routes/auth/identity-token-router.ts +147 -0
  100. package/src/v1/routes/auth/organization-token-router.ts +70 -0
  101. package/src/v1/routes/billing/billing-router.ts +227 -0
  102. package/src/v1/routes/organizations/invoices-router.ts +48 -0
  103. package/src/v1/routes/organizations/payments-router.ts +104 -0
  104. package/src/v1/routes/organizations/public-organization-router.ts +319 -0
  105. package/src/v1/routes/organizations/users-router.ts +139 -0
  106. package/src/v1/routes/policies/public-policy-router.ts +50 -0
  107. package/src/v1/routes/services/public-service-router.ts +89 -0
  108. package/src/v1/routes/services/service-router.ts +197 -0
  109. package/src/v1/routes/users/public-user-router.ts +230 -0
  110. package/src/v1/routes/v1-router.ts +50 -0
  111. package/src/v1/schemas/admin/accounting-seat-schemas.ts +22 -0
  112. package/src/v1/schemas/admin/billing-cycle-schemas.ts +20 -0
  113. package/src/v1/schemas/admin/invoice-schemas.ts +41 -0
  114. package/src/v1/schemas/admin/license-limit-schemas.ts +25 -0
  115. package/src/v1/schemas/admin/license-schemas.ts +30 -0
  116. package/src/v1/schemas/admin/organization-license-limit-schemas.ts +29 -0
  117. package/src/v1/schemas/admin/organization-license-schemas.ts +43 -0
  118. package/src/v1/schemas/admin/organization-schemas.ts +67 -0
  119. package/src/v1/schemas/admin/organization-usage-schemas.ts +15 -0
  120. package/src/v1/schemas/admin/payment-method-schemas.ts +40 -0
  121. package/src/v1/schemas/admin/policy-schemas.ts +31 -0
  122. package/src/v1/schemas/admin/service-notification-schemas.ts +23 -0
  123. package/src/v1/schemas/admin/service-schemas.ts +56 -0
  124. package/src/v1/schemas/admin/user-organization-schemas.ts +24 -0
  125. package/src/v1/schemas/admin/user-policy-schemas.ts +17 -0
  126. package/src/v1/schemas/admin/user-schemas.ts +52 -0
  127. package/src/v1/schemas/auth/auth-schemas.ts +55 -0
  128. package/src/v1/schemas/billing/billing-schemas.ts +32 -0
  129. package/src/v1/schemas/organizations/public-organization-schemas.ts +77 -0
  130. package/src/v1/schemas/payments/payment-method-schemas.ts +0 -0
  131. package/src/v1/schemas/services/public-service-schemas.ts +5 -0
  132. package/src/v1/schemas/services/service-schemas.ts +27 -0
  133. package/src/v1/schemas/users/public-user-schemas.ts +73 -0
  134. package/src/v1/types/admin/accounting-seat-types.ts +16 -0
  135. package/src/v1/types/admin/billing-cycle-types.ts +14 -0
  136. package/src/v1/types/admin/invoice-types.ts +39 -0
  137. package/src/v1/types/admin/license-limit-types.ts +16 -0
  138. package/src/v1/types/admin/license-types.ts +29 -0
  139. package/src/v1/types/admin/organization-license-limit-types.ts +13 -0
  140. package/src/v1/types/admin/organization-license-types.ts +21 -0
  141. package/src/v1/types/admin/organization-types.ts +60 -0
  142. package/src/v1/types/admin/organization-usage-types.ts +17 -0
  143. package/src/v1/types/admin/payment-method-types.ts +30 -0
  144. package/src/v1/types/admin/policy-types.ts +9 -0
  145. package/src/v1/types/admin/policy-version-types.ts +12 -0
  146. package/src/v1/types/admin/service-notification-types.ts +20 -0
  147. package/src/v1/types/admin/service-types.ts +36 -0
  148. package/src/v1/types/admin/user-organization-types.ts +31 -0
  149. package/src/v1/types/admin/user-types.ts +44 -0
  150. package/src/v1/types/auth/@deprecated auth-types.ts +26 -0
  151. package/src/v1/types/auth/common-types.ts +7 -0
  152. package/src/v1/types/auth/confirmation-types.ts +5 -0
  153. package/src/v1/types/auth/identity-token-types.ts +8 -0
  154. package/src/v1/types/auth/organization-token-types.ts +23 -0
  155. package/src/v1/types/auth/otp-types.ts +12 -0
  156. package/src/v1/types/billing/billing-types.ts +37 -0
  157. package/src/v1/types/billing/ue-types.ts +29 -0
  158. package/src/v1/types/organizations/public-organization-types.ts +76 -0
  159. package/src/v1/types/policies/public-policy-types.ts +7 -0
  160. package/src/v1/types/services/public-service-types.ts +20 -0
  161. package/src/v1/types/services/service-types.ts +43 -0
  162. package/src/v1/types/users/public-user-types.ts +15 -0
  163. package/src/v1/types/users/user-policy-types.ts +11 -0
  164. package/static/error.html +55 -0
  165. package/static/grant.html +117 -0
  166. package/static/login.html +54 -0
  167. package/static/oauth-client-logo.webp +0 -0
  168. package/tsconfig.json +20 -0
@@ -0,0 +1,286 @@
1
+ import { newUUIDv4 } from '@beseif-solutions/utility-functions/dist/utils/string-utils';
2
+ import { Validator } from '@beseif-solutions/utility-functions/dist/utils/validation-utils';
3
+ import APIError, { StatusCodes } from '../../../helpers/api-error';
4
+ import nconf from '../../../helpers/config';
5
+ import RedisDao from '../../daos/redis-dao';
6
+ import { identityAdminSchema, identityTokenSchema, newOTPSchema, refreshSchema, validateOTPSchema, validateRecaptchaSchema } from '../../schemas/auth/auth-schemas';
7
+ import { User, UserStatus } from '../../types/admin/user-types';
8
+ import { BearerAuthorization } from '../../types/auth/common-types';
9
+ import { IdentityAccessInformation, IdentityRefreshInformation } from '../../types/auth/identity-token-types';
10
+ import userController from '../admin/user-controller';
11
+ import notificationController from '../admin/notification-controller';
12
+ import tempCodeController from './temp-code-controller';
13
+ import { TempCodeType } from '../../types/auth/otp-types';
14
+ import { Requester } from '@beseif-solutions/utility-functions/dist/utils/http-utils';
15
+ import FormData from 'form-data';
16
+
17
+ class IdentityTokenController {
18
+
19
+ private readonly tokenDao = new RedisDao<IdentityAccessInformation | IdentityRefreshInformation>();
20
+
21
+ private readonly key = (type: `access_token` | `refresh_token` | `*`, user: number | `*`, pair: string, token: string) =>
22
+ `identity:${type}:${user}:${pair}:${token}`;
23
+
24
+ public readonly admin = async (data: Pick<User, `email`>): Promise<BearerAuthorization> => {
25
+ try {
26
+ const { email } = Validator.validate(identityAdminSchema, data);
27
+ const user = (await userController.list({ email: email }, { password: false }))[0];
28
+
29
+ if (!user) {
30
+ throw new APIError({
31
+ code: StatusCodes.NOT_FOUND,
32
+ detail: `No user found with provided email and password.`,
33
+ });
34
+ }
35
+
36
+ return await this.newBearerAuthorization(user);
37
+ } catch (e) { throw e; }
38
+ };
39
+
40
+ public readonly token = async (data: Pick<User, `email` | `password`>): Promise<BearerAuthorization> => {
41
+ try {
42
+ const { email, password } = Validator.validate(identityTokenSchema, data);
43
+ const user = (await userController.list({ email: email }, { password: true }))[0];
44
+
45
+ if (!user || userController.hashPassword(password) !== user.password) {
46
+ throw new APIError({
47
+ code: StatusCodes.NOT_FOUND,
48
+ detail: `No user found with provided email and password.`,
49
+ });
50
+ }
51
+
52
+ if (user.status === UserStatus.Blocked) {
53
+ throw new APIError({
54
+ code: StatusCodes.FORBIDDEN,
55
+ detail: `This user has been blocked. Pease contact with your support representative to fix this issue.`,
56
+ });
57
+ }
58
+
59
+ // activate user after expired login
60
+ if (user.status === UserStatus.Expired) {
61
+ await userController.activate(user.id, { password: false });
62
+ }
63
+
64
+ return await this.newBearerAuthorization(user);
65
+ } catch (e) { throw e; }
66
+ };
67
+
68
+ public readonly refresh = async (access_token: string, data: Pick<BearerAuthorization, `refresh_token`>): Promise<BearerAuthorization> => {
69
+ try {
70
+ const { refresh_token } = Validator.validate(refreshSchema, data);
71
+ const refreshInfo = await this.tokenDao.get(this.key(`refresh_token`, `*`, `*`, refresh_token)) as IdentityRefreshInformation;
72
+
73
+ if (!refreshInfo || refreshInfo.access_token !== access_token) {
74
+ throw new APIError({
75
+ code: StatusCodes.FORBIDDEN,
76
+ detail: `Invalid refresh token provided.`,
77
+ });
78
+ }
79
+
80
+ // get user
81
+ const user = await this.validate(refreshInfo);
82
+
83
+ // clear old tokens
84
+ await this.tokenDao.delete(this.key(`*`, user.id, refreshInfo.pair, `*`));
85
+
86
+ return await identityTokenController.newBearerAuthorization(user);
87
+ } catch (e) { throw e; }
88
+ };
89
+
90
+ public readonly delete = async (user: User, access_token: string): Promise<void> => {
91
+ try {
92
+ const accessInfo = await this.tokenDao.get(this.key(`access_token`, user.id, `*`, access_token)) as IdentityAccessInformation;
93
+ if (!accessInfo) {
94
+ throw new APIError({
95
+ code: StatusCodes.FORBIDDEN,
96
+ detail: `Invalid token provided.`,
97
+ });
98
+ }
99
+
100
+ await this.tokenDao.delete(this.key(`*`, user.id, accessInfo.pair, `*`));
101
+ } catch (e) { throw e; }
102
+ };
103
+
104
+ public readonly deleteAll = async (user: User): Promise<void> => {
105
+ try {
106
+ await this.tokenDao.delete(this.key(`*`, user.id, `*`, `*`));
107
+ } catch (e) { throw e; }
108
+ };
109
+
110
+ public readonly check = async (token: string): Promise<User> => {
111
+ try {
112
+ const accessInfo: IdentityAccessInformation = await this.tokenDao.get(this.key(`access_token`, `*`, `*`, token));
113
+ if (!accessInfo) {
114
+ throw new APIError({
115
+ code: StatusCodes.FORBIDDEN,
116
+ detail: `Invalid token provided.`,
117
+ });
118
+ }
119
+
120
+ // get user
121
+ const user = await this.validate(accessInfo);
122
+ return user;
123
+ } catch (e) { throw e; }
124
+ };
125
+
126
+ // otp access (+ password recover)
127
+ public readonly requestOTP = async (data: Pick<User, `email`> & { flow: TempCodeType.OTP | TempCodeType.Password }): Promise<void> => {
128
+ try {
129
+ const { email, flow } = Validator.validate(newOTPSchema, data);
130
+
131
+ const user = (await userController.list({ email: email }, { password: false }))[0];
132
+ if (!user) { return; }
133
+
134
+ if (user.status === UserStatus.Blocked) {
135
+ throw new APIError({
136
+ code: StatusCodes.FORBIDDEN,
137
+ detail: `This user has been blocked. Pease contact with your support representative to fix this issue.`,
138
+ });
139
+ }
140
+
141
+ await notificationController[flow](user.id);
142
+ } catch (e) { throw e; }
143
+ };
144
+
145
+ public readonly validateOTP = async (data: Pick<User, `email`> & { code: string, flow: TempCodeType.OTP | TempCodeType.Password }): Promise<BearerAuthorization> => {
146
+ try {
147
+ const { email, code, flow } = Validator.validate(validateOTPSchema, data);
148
+
149
+ const user = (await userController.list({ email: email }, { password: false }))[0];
150
+ if (!user) { return; }
151
+
152
+ if (user.status === UserStatus.Blocked) {
153
+ throw new APIError({
154
+ code: StatusCodes.FORBIDDEN,
155
+ detail: `This user has been blocked. Pease contact with your support representative to fix this issue.`,
156
+ });
157
+ }
158
+
159
+ await tempCodeController.validate(flow, user, code);
160
+
161
+ return await this.newBearerAuthorization(user);
162
+ } catch (e) { throw e; }
163
+ };
164
+
165
+ public readonly validateRecaptcha = async (data: { token: string }): Promise<void> => {
166
+ try {
167
+ const recaptchaValue = nconf.get(`SERVICES:AUTH:RECAPTCHA:VALUE`);
168
+
169
+ if (recaptchaValue > 0) {
170
+ const { token } = Validator.validate(validateRecaptchaSchema, data);
171
+ const recaptchaSecret = nconf.get(`SERVICES:AUTH:RECAPTCHA:SECRETKEY`);
172
+
173
+ const form = new FormData();
174
+ form.append(`secret`, recaptchaSecret);
175
+ form.append(`response`, token);
176
+
177
+ const response = await Requester.post(`https://www.google.com/recaptcha/api/siteverify`,
178
+ form,
179
+ {
180
+ headers: {
181
+ ...form.getHeaders(),
182
+ },
183
+ }
184
+ );
185
+
186
+ if (response.score < recaptchaValue) {
187
+ throw new APIError({
188
+ code: StatusCodes.UNAUTHORIZED,
189
+ detail: `Recaptcha validation failed: score too low (${response.score})`,
190
+ });
191
+ }
192
+ }
193
+
194
+ } catch (e) { throw e; }
195
+ };
196
+
197
+ public readonly socialGoogle = async (data: { code: string }): Promise<BearerAuthorization> => {
198
+ try {
199
+ const auth = await Requester.post(`https://oauth2.googleapis.com/token`, {
200
+ code: data.code,
201
+ client_id: nconf.get(`SERVICES:AUTH:SOCIAL:GOOGLE:CLIENT_ID`),
202
+ client_secret: nconf.get(`SERVICES:AUTH:SOCIAL:GOOGLE:CLIENT_SECRET`),
203
+ redirect_uri: nconf.get(`SERVICES:AUTH:SOCIAL:GOOGLE:REDIRECT_URI`),
204
+ grant_type: `authorization_code`,
205
+ });
206
+ if (!auth.access_token) {
207
+ throw new APIError({
208
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
209
+ detail: `Could not recover access token from Google.`,
210
+ });
211
+ }
212
+
213
+ const profile = await Requester.get(`https://www.googleapis.com/oauth2/v3/userinfo`, {
214
+ headers: {
215
+ Authorization: `Bearer ${auth.access_token}`,
216
+ },
217
+ });
218
+ if (!profile.email) {
219
+ throw new APIError({
220
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
221
+ detail: `Could not recover email from Google profile.`,
222
+ });
223
+ }
224
+
225
+ let user = (await userController.list({ email: profile.email }, { password: false }))[0];
226
+ if (!user) {
227
+ // create user with social profile data
228
+ user = (await userController.add({
229
+ email: profile.email,
230
+ password: null,
231
+ properties: {
232
+ name: profile.given_name,
233
+ surnames: profile.family_name,
234
+ image: profile.picture,
235
+ },
236
+ }, { password: false }))[0];
237
+
238
+ // activate user
239
+ await userController.activate(user.id, { password: false });
240
+ }
241
+
242
+ return await this.newBearerAuthorization(user);
243
+ } catch (e) { throw e; }
244
+ };
245
+
246
+ private readonly validate = async (accessInfo: IdentityAccessInformation): Promise<User> => {
247
+ try {
248
+ // get user
249
+ const user = (await userController.get(accessInfo.id, { password: false, timestamp: true }))[0];
250
+ if (!user) {
251
+ throw new APIError({
252
+ code: StatusCodes.NOT_FOUND,
253
+ detail: `No user found.`,
254
+ });
255
+ }
256
+
257
+ return user;
258
+ } catch (e) { throw e; }
259
+ };
260
+
261
+ private readonly newBearerAuthorization = async (user: User): Promise<BearerAuthorization> => {
262
+ try {
263
+ const pair = newUUIDv4();
264
+
265
+ const access_token = `id-${newUUIDv4()}`;
266
+ const aInfo: IdentityAccessInformation = { id: user.id, pair: pair };
267
+ await this.tokenDao.add(this.key(`access_token`, user.id, pair, access_token), aInfo, nconf.get(`SERVICES:AUTH:BEARER:TOKEN_EXPIRATION`));
268
+
269
+ const refresh_token = `id-${newUUIDv4()}`;
270
+ const rInfo: IdentityRefreshInformation = { ...aInfo, access_token: access_token };
271
+ await this.tokenDao.add(this.key(`refresh_token`, user.id, pair, refresh_token), rInfo, nconf.get(`SERVICES:AUTH:BEARER:REFRESH_EXPIRATION`));
272
+
273
+ return {
274
+ token_type: `Bearer`,
275
+ access_token: access_token,
276
+ refresh_token: refresh_token,
277
+ expires_in: nconf.get(`SERVICES:AUTH:BEARER:TOKEN_EXPIRATION`),
278
+ };
279
+ } catch (e) { throw e; }
280
+ };
281
+
282
+
283
+ }
284
+
285
+ const identityTokenController = new IdentityTokenController();
286
+ export default identityTokenController;
@@ -0,0 +1,277 @@
1
+ import { newUUIDv4 } from '@beseif-solutions/utility-functions/dist/utils/string-utils';
2
+ import { Validator } from '@beseif-solutions/utility-functions/dist/utils/validation-utils';
3
+ import APIError, { StatusCodes } from '../../../helpers/api-error';
4
+ import nconf from '../../../helpers/config';
5
+ import RedisDao from '../../daos/redis-dao';
6
+ import { organizationTokenSchema, refreshSchema } from '../../schemas/auth/auth-schemas';
7
+ import { LicenseType } from '../../types/admin/license-types';
8
+ import { Organization, OrganizationStatus } from '../../types/admin/organization-types';
9
+ import { UserOrganization, UserOrganizationRole } from '../../types/admin/user-organization-types';
10
+ import { User, UserStatus } from '../../types/admin/user-types';
11
+ import { BearerAuthorization } from '../../types/auth/common-types';
12
+ import { Environment, OrganizationAccessInformation, OrganizationRefreshInformation } from '../../types/auth/organization-token-types';
13
+ import organizationController from '../admin/organization-controller';
14
+ import organizationLicenseController from '../admin/organization-license-controller';
15
+ import serviceController from '../admin/service-controller';
16
+ import userController from '../admin/user-controller';
17
+ import userOrganizationController from '../admin/user-organization-controller';
18
+ import authority, { omnipotentAccess } from '../../../helpers/authority';
19
+
20
+ class OrganizationTokenController {
21
+
22
+ private readonly tokenDao = new RedisDao<OrganizationAccessInformation | OrganizationRefreshInformation>();
23
+
24
+ private readonly key = (type: `access_token` | `refresh_token` | `*`, organization: number | `*`, pair: string, token: string) =>
25
+ `organization:${type}:${organization}:${pair}:${token}`;
26
+
27
+
28
+ public readonly token = async (user: User, data: { organization: string }): Promise<BearerAuthorization> => {
29
+ try {
30
+ Validator.validate(organizationTokenSchema, data);
31
+
32
+ const organization = (await organizationController.list({ uuid: data.organization, status: { is: OrganizationStatus.Blocked, inverse: true } }, { timestamp: true }))[0];
33
+ if (!organization) {
34
+ throw new APIError({
35
+ code: StatusCodes.NOT_FOUND,
36
+ detail: `No organization ${data.organization} found.`,
37
+ });
38
+ }
39
+
40
+ let access: UserOrganization = (await userOrganizationController.list({
41
+ user: user.id,
42
+ organization: organization.id,
43
+ enabled: true,
44
+ }))[0];
45
+ if (!access || access.invite) {
46
+ if (!user.omnipotent) {
47
+ throw new APIError({
48
+ code: StatusCodes.FORBIDDEN,
49
+ detail: `Forbidden access to organization ${data.organization}`,
50
+ });
51
+ }
52
+
53
+ access = omnipotentAccess(user, organization);
54
+ }
55
+
56
+ const service = (await serviceController.list({ uuid: authority.configuration.uuid }))[0];
57
+ if (!service) {
58
+ throw new APIError({
59
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
60
+ detail: `Could not find service.`,
61
+ });
62
+ }
63
+
64
+ const license = (await organizationLicenseController.list({
65
+ organization: organization.id,
66
+ service: service.id,
67
+ type: LicenseType.Base,
68
+ enabled: true,
69
+ }))[0];
70
+ if (!license) {
71
+ throw new APIError({
72
+ code: StatusCodes.FORBIDDEN,
73
+ detail: `No license found for organization ${data.organization}`,
74
+ });
75
+ }
76
+
77
+ return await this.newBearerAuthorization({ user: user, access: access, organization: organization, license: license });
78
+ } catch (e) { throw e; }
79
+ };
80
+
81
+ public readonly refresh = async (access_token: string, data: Pick<BearerAuthorization, `refresh_token`>): Promise<BearerAuthorization> => {
82
+ try {
83
+ const { refresh_token } = Validator.validate(refreshSchema, data);
84
+ const refreshInfo = await this.tokenDao.get(this.key(`refresh_token`, `*`, `*`, refresh_token)) as OrganizationRefreshInformation;
85
+
86
+ if (!refreshInfo || refreshInfo.access_token !== access_token) {
87
+ throw new APIError({
88
+ code: StatusCodes.FORBIDDEN,
89
+ detail: `Invalid refresh token provided.`,
90
+ });
91
+ }
92
+
93
+ const env = await this.validate(refreshInfo);
94
+
95
+ // clear old tokens
96
+ await this.tokenDao.delete(this.key(`*`, env.organization.id, refreshInfo.pair, `*`));
97
+
98
+ return await organizationTokenController.newBearerAuthorization(env);
99
+ } catch (e) { throw e; }
100
+ };
101
+
102
+ public readonly authorize = async (organization: Organization): Promise<BearerAuthorization> => {
103
+ try {
104
+ // get organization owner
105
+ const access = (await userOrganizationController.list({
106
+ organization: organization.id,
107
+ role: UserOrganizationRole.Owner,
108
+ enabled: true,
109
+ }))[0];
110
+ if (!access) {
111
+ throw new APIError({
112
+ code: StatusCodes.FORBIDDEN,
113
+ detail: `Forbidden access to organization ${organization}`,
114
+ });
115
+ }
116
+
117
+ const user = (await userController.list({ id: access.user, status: UserStatus.Active }, { password: false }))[0];
118
+ if (!user) {
119
+ throw new APIError({
120
+ code: StatusCodes.NOT_FOUND,
121
+ detail: `No user found.`,
122
+ });
123
+ }
124
+
125
+ // get organization license
126
+ const service = (await serviceController.list({ uuid: authority.configuration.uuid }))[0];
127
+ if (!service) {
128
+ throw new APIError({
129
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
130
+ detail: `Could not find service.`,
131
+ });
132
+ }
133
+
134
+ const license = (await organizationLicenseController.list({
135
+ service: service.id,
136
+ organization: organization.id,
137
+ type: LicenseType.Base,
138
+ enabled: true,
139
+ }))[0];
140
+ if (!license) {
141
+ throw new APIError({
142
+ code: StatusCodes.FORBIDDEN,
143
+ detail: `No license found for organization.`,
144
+ });
145
+ }
146
+
147
+ // generate auth token
148
+ return await this.newBearerAuthorization({ user: user, access: access, organization: organization, license: license }, false);
149
+ } catch (e) { throw e; }
150
+ };
151
+
152
+ public readonly delete = async (env: Environment, access_token: string): Promise<void> => {
153
+ try {
154
+ const accessInfo = await this.tokenDao.get(this.key(`access_token`, `*`, `*`, access_token)) as OrganizationAccessInformation;
155
+ if (!accessInfo) {
156
+ throw new APIError({
157
+ code: StatusCodes.FORBIDDEN,
158
+ detail: `Invalid token provided.`,
159
+ });
160
+ }
161
+
162
+ await this.tokenDao.delete(this.key(`*`, env.organization.id, accessInfo.pair, `*`));
163
+ } catch (e) { throw e; }
164
+ };
165
+
166
+ public readonly deleteAll = async (env: Environment): Promise<void> => {
167
+ try {
168
+ await this.tokenDao.delete(this.key(`*`, env.organization.id, `*`, `*`));
169
+ } catch (e) { throw e; }
170
+ };
171
+
172
+ public readonly check = async (access_token: string): Promise<Environment> => {
173
+ try {
174
+ const accessInfo = await this.tokenDao.get(this.key(`access_token`, `*`, `*`, access_token)) as OrganizationAccessInformation;
175
+ if (!accessInfo) {
176
+ throw new APIError({
177
+ code: StatusCodes.FORBIDDEN,
178
+ detail: `Invalid token provided.`,
179
+ });
180
+ }
181
+
182
+ return await this.validate(accessInfo);
183
+ } catch (e) { throw e; }
184
+ };
185
+
186
+ private readonly validate = async (accessInfo: OrganizationAccessInformation): Promise<Environment> => {
187
+ try {
188
+ // validate user
189
+ const user = (await userController.list({ id: accessInfo.user, status: UserStatus.Active }, { password: false, timestamp: true }))[0];
190
+ if (!user) {
191
+ throw new APIError({
192
+ code: StatusCodes.NOT_FOUND,
193
+ detail: `No user found.`,
194
+ });
195
+ }
196
+
197
+ // validate organization
198
+ const organization = (await organizationController.list({ id: accessInfo.organization, status: { is: OrganizationStatus.Blocked, inverse: true } }, { timestamp: true }))[0];
199
+ if (!organization) {
200
+ throw new APIError({
201
+ code: StatusCodes.NOT_FOUND,
202
+ detail: `No organization found.`,
203
+ });
204
+ }
205
+
206
+ // validate access
207
+ let access: UserOrganization = (await userOrganizationController.list({
208
+ id: accessInfo.access,
209
+ user: user.id,
210
+ organization: organization.id,
211
+ enabled: true,
212
+ }))[0];
213
+ if (!access) {
214
+ if (!user.omnipotent) {
215
+ throw new APIError({
216
+ code: StatusCodes.FORBIDDEN,
217
+ detail: `Forbidden access to organization.`,
218
+ });
219
+ }
220
+
221
+ access = omnipotentAccess(user, organization);
222
+ }
223
+
224
+ // validate license
225
+ const service = (await serviceController.list({ uuid: authority.configuration.uuid }))[0];
226
+ if (!service) {
227
+ throw new APIError({
228
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
229
+ detail: `Could not find service.`,
230
+ });
231
+ }
232
+
233
+ const license = (await organizationLicenseController.list({
234
+ id: accessInfo.license,
235
+ organization: organization.id,
236
+ service: service.id,
237
+ enabled: true,
238
+ }))[0];
239
+ if (!license) {
240
+ throw new APIError({
241
+ code: StatusCodes.FORBIDDEN,
242
+ detail: `No license found for organization.`,
243
+ });
244
+ }
245
+
246
+ return { user: user, access: access, organization: organization, license: license };
247
+ } catch (e) { throw e; }
248
+ };
249
+
250
+ private readonly newBearerAuthorization = async (env: Environment, refresh = true): Promise<BearerAuthorization> => {
251
+ try {
252
+ const pair = newUUIDv4();
253
+
254
+ const access_token = `org-${newUUIDv4()}`;
255
+ const aInfo: OrganizationAccessInformation = { user: env.user.id, access: env.access.id, organization: env.organization.id, license: env.license.id, pair: pair };
256
+ await this.tokenDao.add(this.key(`access_token`, env.organization.id, pair, access_token), aInfo, nconf.get(`SERVICES:AUTH:BEARER:TOKEN_EXPIRATION`));
257
+
258
+ const refresh_token = `org-${newUUIDv4()}`;
259
+ if (refresh) {
260
+ const rInfo: OrganizationRefreshInformation = { ...aInfo, access_token: access_token };
261
+ await this.tokenDao.add(this.key(`refresh_token`, env.organization.id, pair, refresh_token), rInfo, nconf.get(`SERVICES:AUTH:BEARER:REFRESH_EXPIRATION`));
262
+ }
263
+
264
+ return {
265
+ token_type: `Bearer`,
266
+ access_token: access_token,
267
+ refresh_token: refresh ? refresh_token : undefined,
268
+ expires_in: nconf.get(`SERVICES:AUTH:BEARER:TOKEN_EXPIRATION`),
269
+ };
270
+ } catch (e) { throw e; }
271
+ };
272
+
273
+
274
+ }
275
+
276
+ const organizationTokenController = new OrganizationTokenController();
277
+ export default organizationTokenController;
@@ -0,0 +1,60 @@
1
+ import { StatusCodes } from "http-status-codes";
2
+ import APIError from "../../../helpers/api-error";
3
+ import nconf from "../../../helpers/config";
4
+ import { generateRandomString, NUMERIC_CHARSET } from "../../../helpers/random";
5
+ import RedisDao from "../../daos/redis-dao";
6
+ import { User } from "../../types/admin/user-types";
7
+ import { TempCode, TempCodeType } from "../../types/auth/otp-types";
8
+
9
+ class TempCodeController {
10
+
11
+ private readonly tempCodeDao = new RedisDao<TempCode>();
12
+
13
+ private readonly key = (type: TempCodeType, user: number | `*`) =>
14
+ `temp:${type}:${user}`;
15
+
16
+ public readonly generate = async (type: TempCodeType, user: User): Promise<TempCode> => {
17
+ try {
18
+ // delete previous one time password code
19
+ await this.tempCodeDao.delete(this.key(type, user.id));
20
+
21
+ // store a new one
22
+ const otp: TempCode = {
23
+ code: generateRandomString(NUMERIC_CHARSET, nconf.get(`SERVICES:AUTH:TEMP:LENGTH`)),
24
+ attempts: 0,
25
+ expiration: nconf.get(`SERVICES:AUTH:TEMP:EXPIRATION`),
26
+ };
27
+
28
+ await this.tempCodeDao.add(this.key(type, user.id), otp, nconf.get(`SERVICES:AUTH:TEMP:EXPIRATION`));
29
+
30
+ return otp;
31
+ } catch (e) { throw e; }
32
+ };
33
+
34
+ public readonly validate = async (type: TempCodeType, user: User, code: string): Promise<void> => {
35
+ try {
36
+ const record = await this.tempCodeDao.get(this.key(type, user.id));
37
+ if (!record || record.code !== code) {
38
+ // delete if max attempts reached
39
+ if (record) {
40
+ if (record.attempts >= nconf.get(`SERVICES:AUTH:TEMP:ATTEMPTS`)) {
41
+ await this.tempCodeDao.delete(this.key(type, user.id));
42
+ } else {
43
+ await this.tempCodeDao.add(this.key(type, user.id), { ...record, attempts: record.attempts + 1 });
44
+ }
45
+ }
46
+
47
+ throw new APIError({
48
+ code: StatusCodes.BAD_REQUEST,
49
+ detail: `OTP is expired or code is invalid.`,
50
+ });
51
+ }
52
+
53
+ await this.tempCodeDao.delete(this.key(type, user.id));
54
+ } catch (e) { throw e; }
55
+ };
56
+
57
+ }
58
+
59
+ const tempCodeController = new TempCodeController();
60
+ export default tempCodeController;