wabe 0.6.9 → 0.6.11

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 (162) hide show
  1. package/README.md +156 -50
  2. package/bucket/b.txt +1 -0
  3. package/dev/index.ts +215 -0
  4. package/dist/authentication/Session.d.ts +4 -1
  5. package/dist/authentication/interface.d.ts +16 -0
  6. package/dist/cron/index.d.ts +0 -1
  7. package/dist/database/DatabaseController.d.ts +41 -13
  8. package/dist/database/interface.d.ts +1 -0
  9. package/dist/email/DevAdapter.d.ts +0 -1
  10. package/dist/email/interface.d.ts +1 -1
  11. package/dist/graphql/resolvers.d.ts +4 -2
  12. package/dist/hooks/index.d.ts +8 -2
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.js +32144 -32058
  15. package/dist/schema/Schema.d.ts +2 -1
  16. package/dist/server/index.d.ts +4 -2
  17. package/dist/utils/crypto.d.ts +7 -0
  18. package/dist/utils/helper.d.ts +5 -1
  19. package/generated/schema.graphql +22 -14
  20. package/generated/wabe.ts +4 -4
  21. package/package.json +23 -23
  22. package/src/authentication/OTP.test.ts +69 -0
  23. package/src/authentication/OTP.ts +64 -0
  24. package/src/authentication/Session.test.ts +629 -0
  25. package/src/authentication/Session.ts +493 -0
  26. package/src/authentication/defaultAuthentication.ts +209 -0
  27. package/src/authentication/index.ts +3 -0
  28. package/src/authentication/interface.ts +155 -0
  29. package/src/authentication/oauth/GitHub.test.ts +91 -0
  30. package/src/authentication/oauth/GitHub.ts +121 -0
  31. package/src/authentication/oauth/Google.test.ts +91 -0
  32. package/src/authentication/oauth/Google.ts +101 -0
  33. package/src/authentication/oauth/Oauth2Client.test.ts +219 -0
  34. package/src/authentication/oauth/Oauth2Client.ts +135 -0
  35. package/src/authentication/oauth/index.ts +2 -0
  36. package/src/authentication/oauth/utils.test.ts +33 -0
  37. package/src/authentication/oauth/utils.ts +27 -0
  38. package/src/authentication/providers/EmailOTP.test.ts +127 -0
  39. package/src/authentication/providers/EmailOTP.ts +84 -0
  40. package/src/authentication/providers/EmailPassword.test.ts +176 -0
  41. package/src/authentication/providers/EmailPassword.ts +116 -0
  42. package/src/authentication/providers/EmailPasswordSRP.test.ts +208 -0
  43. package/src/authentication/providers/EmailPasswordSRP.ts +179 -0
  44. package/src/authentication/providers/GitHub.ts +24 -0
  45. package/src/authentication/providers/Google.ts +24 -0
  46. package/src/authentication/providers/OAuth.test.ts +185 -0
  47. package/src/authentication/providers/OAuth.ts +106 -0
  48. package/src/authentication/providers/PhonePassword.test.ts +176 -0
  49. package/src/authentication/providers/PhonePassword.ts +115 -0
  50. package/src/authentication/providers/QRCodeOTP.test.ts +77 -0
  51. package/src/authentication/providers/QRCodeOTP.ts +58 -0
  52. package/src/authentication/providers/index.ts +6 -0
  53. package/src/authentication/resolvers/refreshResolver.test.ts +30 -0
  54. package/src/authentication/resolvers/refreshResolver.ts +19 -0
  55. package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
  56. package/src/authentication/resolvers/signInWithResolver.test.ts +293 -0
  57. package/src/authentication/resolvers/signInWithResolver.ts +92 -0
  58. package/src/authentication/resolvers/signOutResolver.test.ts +38 -0
  59. package/src/authentication/resolvers/signOutResolver.ts +18 -0
  60. package/src/authentication/resolvers/signUpWithResolver.test.ts +180 -0
  61. package/src/authentication/resolvers/signUpWithResolver.ts +65 -0
  62. package/src/authentication/resolvers/verifyChallenge.test.ts +133 -0
  63. package/src/authentication/resolvers/verifyChallenge.ts +62 -0
  64. package/src/authentication/roles.test.ts +49 -0
  65. package/src/authentication/roles.ts +40 -0
  66. package/src/authentication/utils.test.ts +97 -0
  67. package/src/authentication/utils.ts +39 -0
  68. package/src/cache/InMemoryCache.test.ts +62 -0
  69. package/src/cache/InMemoryCache.ts +45 -0
  70. package/src/cron/index.test.ts +17 -0
  71. package/src/cron/index.ts +43 -0
  72. package/src/database/DatabaseController.test.ts +613 -0
  73. package/src/database/DatabaseController.ts +1007 -0
  74. package/src/database/index.test.ts +1372 -0
  75. package/src/database/index.ts +9 -0
  76. package/src/database/interface.ts +302 -0
  77. package/src/email/DevAdapter.ts +7 -0
  78. package/src/email/EmailController.test.ts +29 -0
  79. package/src/email/EmailController.ts +13 -0
  80. package/src/email/index.ts +2 -0
  81. package/src/email/interface.ts +36 -0
  82. package/src/email/templates/sendOtpCode.ts +120 -0
  83. package/src/file/FileController.ts +28 -0
  84. package/src/file/FileDevAdapter.ts +51 -0
  85. package/src/file/hookDeleteFile.ts +25 -0
  86. package/src/file/hookReadFile.ts +66 -0
  87. package/src/file/hookUploadFile.ts +50 -0
  88. package/src/file/index.test.ts +932 -0
  89. package/src/file/index.ts +2 -0
  90. package/src/file/interface.ts +39 -0
  91. package/src/graphql/GraphQLSchema.test.ts +4408 -0
  92. package/src/graphql/GraphQLSchema.ts +880 -0
  93. package/src/graphql/index.ts +2 -0
  94. package/src/graphql/parseGraphqlSchema.ts +85 -0
  95. package/src/graphql/parser.test.ts +203 -0
  96. package/src/graphql/parser.ts +542 -0
  97. package/src/graphql/pointerAndRelationFunction.ts +191 -0
  98. package/src/graphql/resolvers.ts +442 -0
  99. package/src/graphql/tests/aggregation.test.ts +1115 -0
  100. package/src/graphql/tests/e2e.test.ts +590 -0
  101. package/src/graphql/tests/scalars.test.ts +250 -0
  102. package/src/graphql/types.ts +227 -0
  103. package/src/hooks/HookObject.test.ts +122 -0
  104. package/src/hooks/HookObject.ts +165 -0
  105. package/src/hooks/authentication.ts +67 -0
  106. package/src/hooks/createUser.test.ts +77 -0
  107. package/src/hooks/createUser.ts +10 -0
  108. package/src/hooks/defaultFields.test.ts +176 -0
  109. package/src/hooks/defaultFields.ts +32 -0
  110. package/src/hooks/deleteSession.test.ts +181 -0
  111. package/src/hooks/deleteSession.ts +20 -0
  112. package/src/hooks/hashFieldHook.test.ts +152 -0
  113. package/src/hooks/hashFieldHook.ts +89 -0
  114. package/src/hooks/index.test.ts +258 -0
  115. package/src/hooks/index.ts +414 -0
  116. package/src/hooks/permissions.test.ts +412 -0
  117. package/src/hooks/permissions.ts +93 -0
  118. package/src/hooks/protected.test.ts +551 -0
  119. package/src/hooks/protected.ts +60 -0
  120. package/src/hooks/searchableFields.test.ts +147 -0
  121. package/src/hooks/searchableFields.ts +86 -0
  122. package/src/hooks/session.test.ts +134 -0
  123. package/src/hooks/session.ts +76 -0
  124. package/src/hooks/setEmail.test.ts +216 -0
  125. package/src/hooks/setEmail.ts +33 -0
  126. package/src/hooks/setupAcl.test.ts +618 -0
  127. package/src/hooks/setupAcl.ts +25 -0
  128. package/src/index.ts +9 -0
  129. package/src/schema/Schema.test.ts +482 -0
  130. package/src/schema/Schema.ts +757 -0
  131. package/src/schema/defaultResolvers.ts +93 -0
  132. package/src/schema/index.ts +1 -0
  133. package/src/schema/resolvers/meResolver.test.ts +62 -0
  134. package/src/schema/resolvers/meResolver.ts +10 -0
  135. package/src/schema/resolvers/resetPassword.test.ts +341 -0
  136. package/src/schema/resolvers/resetPassword.ts +63 -0
  137. package/src/schema/resolvers/sendEmail.test.ts +118 -0
  138. package/src/schema/resolvers/sendEmail.ts +21 -0
  139. package/src/schema/resolvers/sendOtpCode.test.ts +141 -0
  140. package/src/schema/resolvers/sendOtpCode.ts +52 -0
  141. package/src/security.test.ts +3434 -0
  142. package/src/server/defaultSessionHandler.test.ts +62 -0
  143. package/src/server/defaultSessionHandler.ts +105 -0
  144. package/src/server/generateCodegen.ts +433 -0
  145. package/src/server/index.test.ts +532 -0
  146. package/src/server/index.ts +334 -0
  147. package/src/server/interface.ts +11 -0
  148. package/src/server/routes/authHandler.ts +169 -0
  149. package/src/server/routes/index.ts +39 -0
  150. package/src/utils/crypto.test.ts +41 -0
  151. package/src/utils/crypto.ts +105 -0
  152. package/src/utils/export.ts +11 -0
  153. package/src/utils/helper.ts +204 -0
  154. package/src/utils/index.test.ts +11 -0
  155. package/src/utils/index.ts +189 -0
  156. package/src/utils/preload.ts +8 -0
  157. package/src/utils/testHelper.ts +116 -0
  158. package/tsconfig.json +32 -0
  159. package/bunfig.toml +0 -4
  160. package/dist/ai/index.d.ts +0 -1
  161. package/dist/ai/interface.d.ts +0 -9
  162. /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
@@ -0,0 +1,757 @@
1
+ import { AuthenticationProvider, SecondaryFactor } from '../authentication'
2
+ import { refreshResolver } from '../authentication/resolvers/refreshResolver'
3
+ import { signOutResolver } from '../authentication/resolvers/signOutResolver'
4
+ import { verifyChallengeResolver } from '../authentication/resolvers/verifyChallenge'
5
+ import type { WabeConfig, WabeTypes } from '../server'
6
+ import { defaultMutations, defaultQueries } from './defaultResolvers'
7
+ import type { HookObject } from '../hooks/HookObject'
8
+ import { signUpWithResolver } from '../authentication/resolvers/signUpWithResolver'
9
+ import { signInWithResolver } from '../authentication/resolvers/signInWithResolver'
10
+
11
+ export const defaultPrivateFields = ['acl']
12
+
13
+ export type WabePrimaryTypes =
14
+ | 'String'
15
+ | 'Int'
16
+ | 'Float'
17
+ | 'Boolean'
18
+ | 'Email'
19
+ | 'Phone'
20
+ | 'Date'
21
+ | 'File'
22
+ | 'Hash'
23
+
24
+ export type WabeCustomTypes = 'Array' | 'Object'
25
+
26
+ export type WabeRelationTypes = 'Pointer' | 'Relation'
27
+
28
+ export type WabeFieldTypes = WabeCustomTypes | WabePrimaryTypes | WabeRelationTypes
29
+
30
+ export type WabeObject<T extends WabeTypes> = {
31
+ name: string
32
+ fields: SchemaFields<T>
33
+ description?: string
34
+ required?: boolean
35
+ }
36
+
37
+ type FieldBase<T extends WabeTypes> = {
38
+ required?: boolean
39
+ description?: string
40
+ protected?: {
41
+ authorizedRoles: Array<T['enums']['RoleEnum'] | 'rootOnly'>
42
+ protectedOperations: Array<'create' | 'read' | 'update'>
43
+ }
44
+ }
45
+
46
+ type TypeFieldBase<U, K extends WabeFieldTypes> = {
47
+ type: K
48
+ defaultValue?: U
49
+ }
50
+
51
+ type TypeFieldArray<T extends WabeTypes> = {
52
+ type: 'Array'
53
+ requiredValue?: boolean
54
+ defaultValue?: any[]
55
+ } & (
56
+ | {
57
+ // For the moment we only keep object and not array because we don't
58
+ // support array of array
59
+ typeValue: WabePrimaryTypes
60
+ }
61
+ | { typeValue: 'Object'; object: WabeObject<T> }
62
+ )
63
+
64
+ type TypeFieldObject<T extends WabeTypes> = {
65
+ type: 'Object'
66
+ object: WabeObject<T>
67
+ defaultValue?: any
68
+ }
69
+
70
+ type TypeFieldPointer<T extends WabeTypes> = {
71
+ type: 'Pointer'
72
+ class: keyof T['types']
73
+ }
74
+
75
+ type TypeFieldRelation<T extends WabeTypes> = {
76
+ type: 'Relation'
77
+ class: keyof T['types']
78
+ }
79
+
80
+ type TypeFieldFile = {
81
+ type: 'File'
82
+ }
83
+
84
+ type TypeFieldCustomScalars<T extends WabeTypes> = {
85
+ type: T['scalars'] extends '' ? never : T['scalars']
86
+ required?: boolean
87
+ defaultValue?: any
88
+ }
89
+
90
+ type TypeFieldCustomEnums<T extends WabeTypes> = {
91
+ type: keyof T['enums']
92
+ defaultValue?: any
93
+ }
94
+
95
+ export type TypeField<T extends WabeTypes> = (
96
+ | TypeFieldBase<string, 'String'>
97
+ | TypeFieldBase<number, 'Int'>
98
+ | TypeFieldBase<number, 'Float'>
99
+ | TypeFieldBase<boolean, 'Boolean'>
100
+ | TypeFieldBase<Date, 'Date'>
101
+ | TypeFieldBase<string, 'Email'>
102
+ | TypeFieldBase<string, 'Phone'>
103
+ | TypeFieldBase<string, 'Hash'>
104
+ | TypeFieldArray<T>
105
+ | TypeFieldObject<T>
106
+ | TypeFieldPointer<T>
107
+ | TypeFieldRelation<T>
108
+ | TypeFieldFile
109
+ | TypeFieldCustomScalars<T>
110
+ | TypeFieldCustomEnums<T>
111
+ ) &
112
+ FieldBase<T>
113
+
114
+ export type SchemaFields<T extends WabeTypes> = Record<string, TypeField<T>>
115
+
116
+ export type ResolverType<T extends WabeTypes> = {
117
+ required?: boolean
118
+ description?: string
119
+ resolve: (...args: any) => any
120
+ } & (
121
+ | { type: WabePrimaryTypes | T['enums'] | T['scalars'] }
122
+ | { type: 'Object'; outputObject: ClassInterface<T> }
123
+ | {
124
+ type: 'Array'
125
+ typeValue: WabePrimaryTypes
126
+ typeValueRequired?: boolean
127
+ }
128
+ | {
129
+ type: 'Array'
130
+ typeValue: 'Object'
131
+ outputObject: ClassInterface<T>
132
+ typeValueRequired?: boolean
133
+ }
134
+ )
135
+
136
+ export type QueryResolver<T extends WabeTypes> = {
137
+ args?: SchemaFields<T>
138
+ } & ResolverType<T>
139
+
140
+ export type MutationResolver<T extends WabeTypes> = {
141
+ args?: { input: SchemaFields<T> }
142
+ } & ResolverType<T>
143
+
144
+ export type TypeResolver<T extends WabeTypes> = {
145
+ queries?: {
146
+ [key: string]: QueryResolver<T>
147
+ }
148
+ mutations?: {
149
+ [key: string]: MutationResolver<T>
150
+ }
151
+ }
152
+
153
+ export type PermissionsOperations = 'create' | 'read' | 'update' | 'delete'
154
+
155
+ export interface PermissionProperties<T extends WabeTypes> {
156
+ requireAuthentication?: boolean
157
+ /**
158
+ * An empty array means that none role is authorized (except root client)
159
+ */
160
+ authorizedRoles?: Array<T['enums']['RoleEnum'] | 'everyone'>
161
+ }
162
+
163
+ /**
164
+ * ACL properties
165
+ * Callback to define the ACL object before insert of the object in the database
166
+ * Can be done with a beforeCreate hook but for simplicity we can define it here
167
+ */
168
+ export type ACLProperties = (hookObject: HookObject<any, any>) => void | Promise<void>
169
+
170
+ export type ClassPermissions<T extends WabeTypes> = Partial<
171
+ Record<PermissionsOperations, PermissionProperties<T>> & {
172
+ acl: ACLProperties
173
+ }
174
+ >
175
+
176
+ export type SearchableFields = Array<string>
177
+
178
+ export type ClassIndexes = Array<{
179
+ field: string
180
+ order: 'ASC' | 'DESC'
181
+ unique?: boolean
182
+ }>
183
+
184
+ export interface ClassInterface<T extends WabeTypes> {
185
+ name: string
186
+ fields: SchemaFields<T>
187
+ description?: string
188
+ permissions?: ClassPermissions<T>
189
+ searchableFields?: SearchableFields
190
+ indexes?: ClassIndexes
191
+ }
192
+
193
+ export interface ScalarInterface {
194
+ name: string
195
+ description?: string
196
+ parseValue?: (value: any) => any
197
+ serialize?: (value: any) => any
198
+ parseLiteral?: (ast: any) => any
199
+ }
200
+
201
+ export interface EnumInterface {
202
+ name: string
203
+ values: Record<string, string>
204
+ description?: string
205
+ }
206
+
207
+ export interface SchemaInterface<T extends WabeTypes> {
208
+ classes?: ClassInterface<T>[]
209
+ scalars?: ScalarInterface[]
210
+ enums?: EnumInterface[]
211
+ resolvers?: TypeResolver<T>
212
+ }
213
+
214
+ export class Schema<T extends WabeTypes> {
215
+ public schema: SchemaInterface<T>
216
+ private config: WabeConfig<T>
217
+
218
+ constructor(config: WabeConfig<T>) {
219
+ this.config = config
220
+ // TODO : Add default scalars here
221
+ this.schema = {
222
+ ...config.schema,
223
+ classes: this.defaultClass(config.schema),
224
+ enums: [...(config.schema?.enums || []), ...this.defaultEnum()],
225
+ resolvers: this.mergeResolvers(this.defaultResolvers()),
226
+ }
227
+ }
228
+
229
+ defaultEnum(): EnumInterface[] {
230
+ return [
231
+ {
232
+ name: 'AuthenticationProvider',
233
+ values: Object.fromEntries(Object.values(AuthenticationProvider).map((key) => [key, key])),
234
+ },
235
+ {
236
+ name: 'SecondaryFactor',
237
+ values: Object.fromEntries(Object.values(SecondaryFactor).map((key) => [key, key])),
238
+ },
239
+ ]
240
+ }
241
+
242
+ mergeResolvers(defaultResolvers: TypeResolver<T>): TypeResolver<T> {
243
+ return {
244
+ mutations: {
245
+ ...this.config.schema?.resolvers?.mutations,
246
+ ...defaultResolvers.mutations,
247
+ },
248
+ queries: {
249
+ ...this.config.schema?.resolvers?.queries,
250
+ ...defaultResolvers.queries,
251
+ },
252
+ }
253
+ }
254
+
255
+ defaultResolvers(): TypeResolver<T> {
256
+ const customAuthenticationConfig = this.config.authentication?.customAuthenticationMethods || []
257
+
258
+ const allPrimaryAuthenticationMethodsInput = customAuthenticationConfig
259
+ .filter((authenticationMethod) => !authenticationMethod.isSecondaryFactor)
260
+ .reduce((acc, authenticationMethod) => {
261
+ acc[authenticationMethod.name] = {
262
+ type: 'Object',
263
+ object: {
264
+ name: authenticationMethod.name,
265
+ fields: authenticationMethod.input,
266
+ },
267
+ }
268
+
269
+ return acc
270
+ }, {} as SchemaFields<T>)
271
+
272
+ const allSecondaryFactorAuthenticationMethodsInput = customAuthenticationConfig
273
+ .filter((authenticationMethod) => authenticationMethod.isSecondaryFactor)
274
+ .reduce((acc, authenticationMethod) => {
275
+ acc[authenticationMethod.name] = {
276
+ type: 'Object',
277
+ object: {
278
+ name: authenticationMethod.name,
279
+ fields: authenticationMethod.input,
280
+ },
281
+ }
282
+
283
+ return acc
284
+ }, {} as SchemaFields<T>)
285
+
286
+ const authenticationInputObject: TypeFieldObject<T> = {
287
+ type: 'Object',
288
+ object: {
289
+ name: 'Authentication',
290
+ fields: allPrimaryAuthenticationMethodsInput,
291
+ },
292
+ }
293
+
294
+ const secondaryFactorAuthenticationInputObject: TypeFieldObject<T> = {
295
+ type: 'Object',
296
+ object: {
297
+ name: 'SecondaryFactorAuthentication',
298
+ fields: allSecondaryFactorAuthenticationMethodsInput,
299
+ },
300
+ }
301
+
302
+ const authenticationInput: TypeField<T> = {
303
+ type: 'Object',
304
+ object: {
305
+ name: 'Authentication',
306
+ fields: authenticationInputObject.object.fields,
307
+ required: true,
308
+ },
309
+ required: true,
310
+ }
311
+
312
+ return {
313
+ queries: defaultQueries,
314
+ mutations: {
315
+ ...defaultMutations,
316
+ ...(customAuthenticationConfig.length > 0
317
+ ? {
318
+ signInWith: {
319
+ type: 'Object',
320
+ outputObject: {
321
+ name: 'SignInWithOutput',
322
+ fields: {
323
+ user: {
324
+ type: 'Pointer',
325
+ class: 'User',
326
+ },
327
+ accessToken: {
328
+ type: 'String',
329
+ },
330
+ refreshToken: {
331
+ type: 'String',
332
+ },
333
+
334
+ srp: {
335
+ type: 'Object',
336
+ object: {
337
+ name: 'SRPOutputSignInWith',
338
+ fields: {
339
+ salt: {
340
+ type: 'String',
341
+ },
342
+ serverPublic: {
343
+ type: 'String',
344
+ },
345
+ },
346
+ },
347
+ },
348
+ },
349
+ },
350
+ args: {
351
+ input: {
352
+ authentication: authenticationInput,
353
+ },
354
+ },
355
+ resolve: signInWithResolver,
356
+ },
357
+ signUpWith: {
358
+ type: 'Object',
359
+ outputObject: {
360
+ name: 'SignUpWithOutput',
361
+ fields: {
362
+ id: { type: 'String' },
363
+ accessToken: {
364
+ type: 'String',
365
+ required: true,
366
+ },
367
+ refreshToken: {
368
+ type: 'String',
369
+ required: true,
370
+ },
371
+ },
372
+ },
373
+ args: {
374
+ input: {
375
+ authentication: authenticationInput,
376
+ },
377
+ },
378
+ resolve: signUpWithResolver,
379
+ },
380
+ signOut: {
381
+ type: 'Boolean',
382
+ resolve: signOutResolver,
383
+ },
384
+ refresh: {
385
+ type: 'Object',
386
+ args: {
387
+ input: {
388
+ accessToken: {
389
+ type: 'String',
390
+ required: true,
391
+ },
392
+ refreshToken: {
393
+ type: 'String',
394
+ required: true,
395
+ },
396
+ },
397
+ },
398
+ outputObject: {
399
+ name: 'RefreshSessionOutput',
400
+ fields: {
401
+ accessToken: {
402
+ type: 'String',
403
+ required: true,
404
+ },
405
+ refreshToken: {
406
+ type: 'String',
407
+ required: true,
408
+ },
409
+ },
410
+ },
411
+ resolve: refreshResolver,
412
+ },
413
+ verifyChallenge: {
414
+ type: 'Object',
415
+ outputObject: {
416
+ name: 'VerifyChallengeOutput',
417
+ fields: {
418
+ accessToken: {
419
+ type: 'String',
420
+ },
421
+ srp: {
422
+ type: 'Object',
423
+ object: {
424
+ name: 'SRPOutputVerifyChallenge',
425
+ fields: {
426
+ serverSessionProof: {
427
+ type: 'String',
428
+ required: true,
429
+ },
430
+ },
431
+ },
432
+ },
433
+ },
434
+ },
435
+ args: {
436
+ input: {
437
+ secondFA: secondaryFactorAuthenticationInputObject,
438
+ },
439
+ },
440
+ resolve: verifyChallengeResolver,
441
+ },
442
+ }
443
+ : {}),
444
+ },
445
+ }
446
+ }
447
+
448
+ sessionClass(): ClassInterface<T> {
449
+ return {
450
+ name: '_Session',
451
+ fields: {
452
+ user: {
453
+ type: 'Pointer',
454
+ required: true,
455
+ class: 'User',
456
+ },
457
+ accessTokenEncrypted: {
458
+ type: 'String',
459
+ required: true,
460
+ },
461
+ accessTokenExpiresAt: {
462
+ type: 'Date',
463
+ required: true,
464
+ },
465
+ refreshTokenEncrypted: {
466
+ type: 'String',
467
+ required: true,
468
+ },
469
+ refreshTokenExpiresAt: {
470
+ type: 'Date',
471
+ required: true,
472
+ },
473
+ },
474
+ }
475
+ }
476
+
477
+ roleClass(): ClassInterface<T> {
478
+ return {
479
+ name: 'Role',
480
+ fields: {
481
+ name: {
482
+ type: 'String',
483
+ required: true,
484
+ },
485
+ users: {
486
+ type: 'Relation',
487
+ class: 'User',
488
+ },
489
+ },
490
+ permissions: {
491
+ create: {
492
+ authorizedRoles: [],
493
+ requireAuthentication: true,
494
+ },
495
+ read: {
496
+ authorizedRoles: ['everyone'],
497
+ requireAuthentication: true,
498
+ },
499
+ update: {
500
+ authorizedRoles: [],
501
+ requireAuthentication: true,
502
+ },
503
+ delete: {
504
+ authorizedRoles: [],
505
+ requireAuthentication: true,
506
+ },
507
+ },
508
+ }
509
+ }
510
+
511
+ internalConfigClass(): ClassInterface<T> {
512
+ return {
513
+ name: '_InternalConfig',
514
+ fields: {
515
+ configKey: {
516
+ type: 'String',
517
+ required: true,
518
+ },
519
+ configValue: {
520
+ type: 'String',
521
+ required: true,
522
+ },
523
+ description: {
524
+ type: 'String',
525
+ },
526
+ },
527
+ // Only root key
528
+ permissions: {
529
+ create: {
530
+ authorizedRoles: [],
531
+ requireAuthentication: true,
532
+ },
533
+ read: {
534
+ authorizedRoles: [],
535
+ requireAuthentication: true,
536
+ },
537
+ update: {
538
+ authorizedRoles: [],
539
+ requireAuthentication: true,
540
+ },
541
+ delete: {
542
+ authorizedRoles: [],
543
+ requireAuthentication: true,
544
+ },
545
+ },
546
+ }
547
+ }
548
+
549
+ userClass(): ClassInterface<T> {
550
+ const customAuthenticationConfig = this.config.authentication?.customAuthenticationMethods || []
551
+
552
+ const allAuthenticationDataToStoreObject = customAuthenticationConfig
553
+ .filter(
554
+ (authenticationMethod) =>
555
+ authenticationMethod.dataToStore && !authenticationMethod.isSecondaryFactor,
556
+ )
557
+ .reduce((acc, authenticationMethod) => {
558
+ if (authenticationMethod.dataToStore)
559
+ acc[authenticationMethod.name] = {
560
+ type: 'Object',
561
+ object: {
562
+ name: authenticationMethod.name,
563
+ fields: authenticationMethod.dataToStore,
564
+ },
565
+ }
566
+
567
+ return acc
568
+ }, {} as SchemaFields<T>)
569
+
570
+ const authenticationObject: TypeFieldObject<T> = {
571
+ type: 'Object',
572
+ object: {
573
+ name: 'Authentication',
574
+ fields: allAuthenticationDataToStoreObject,
575
+ },
576
+ }
577
+
578
+ const fields: SchemaFields<T> = {
579
+ ...(customAuthenticationConfig.length > 0 ? { authentication: authenticationObject } : {}),
580
+ provider: {
581
+ type: 'AuthenticationProvider',
582
+ },
583
+ isOauth: {
584
+ type: 'Boolean',
585
+ },
586
+ email: {
587
+ type: 'Email',
588
+ },
589
+ verifiedEmail: {
590
+ type: 'Boolean',
591
+ },
592
+ role: {
593
+ type: 'Pointer',
594
+ class: 'Role',
595
+ protected: {
596
+ authorizedRoles: ['rootOnly'],
597
+ protectedOperations: ['create', 'update'],
598
+ },
599
+ },
600
+ sessions: {
601
+ type: 'Relation',
602
+ class: '_Session',
603
+ protected: {
604
+ authorizedRoles: ['rootOnly'],
605
+ protectedOperations: ['create', 'update'],
606
+ },
607
+ },
608
+ secondFA: {
609
+ type: 'Object',
610
+ object: {
611
+ name: 'SecondFA',
612
+ fields: {
613
+ enabled: {
614
+ type: 'Boolean',
615
+ required: true,
616
+ },
617
+ provider: {
618
+ type: 'SecondaryFactor',
619
+ required: true,
620
+ },
621
+ },
622
+ },
623
+ },
624
+ }
625
+
626
+ return {
627
+ name: 'User',
628
+ fields,
629
+ permissions: {
630
+ delete: {
631
+ authorizedRoles: [],
632
+ requireAuthentication: true,
633
+ },
634
+ update: {
635
+ authorizedRoles: [],
636
+ requireAuthentication: true,
637
+ },
638
+ create: {
639
+ requireAuthentication: false,
640
+ },
641
+ read: {
642
+ authorizedRoles: [],
643
+ requireAuthentication: true,
644
+ },
645
+ },
646
+ }
647
+ }
648
+
649
+ defaultFields(): SchemaFields<T> {
650
+ return {
651
+ acl: {
652
+ type: 'Object',
653
+ object: {
654
+ name: 'ACLObject',
655
+ fields: {
656
+ users: {
657
+ type: 'Array',
658
+ typeValue: 'Object',
659
+ object: {
660
+ name: 'UsersACL',
661
+ fields: {
662
+ userId: {
663
+ type: 'String',
664
+ required: true,
665
+ },
666
+ read: {
667
+ type: 'Boolean',
668
+ required: true,
669
+ },
670
+ write: {
671
+ type: 'Boolean',
672
+ required: true,
673
+ },
674
+ },
675
+ },
676
+ },
677
+ roles: {
678
+ type: 'Array',
679
+ typeValue: 'Object',
680
+ object: {
681
+ name: 'RolesACL',
682
+ fields: {
683
+ roleId: {
684
+ type: 'String',
685
+ required: true,
686
+ },
687
+ read: {
688
+ type: 'Boolean',
689
+ required: true,
690
+ },
691
+ write: {
692
+ type: 'Boolean',
693
+ required: true,
694
+ },
695
+ },
696
+ },
697
+ },
698
+ },
699
+ },
700
+ protected: {
701
+ authorizedRoles: ['rootOnly'],
702
+ protectedOperations: ['create', 'update'],
703
+ },
704
+ },
705
+ createdAt: {
706
+ type: 'Date',
707
+ },
708
+ updatedAt: {
709
+ type: 'Date',
710
+ },
711
+ search: {
712
+ type: 'Array',
713
+ typeValue: 'String',
714
+ },
715
+ }
716
+ }
717
+
718
+ mergeClass(newClass: ClassInterface<T>[]): ClassInterface<T>[] {
719
+ const allUniqueClassName = [...new Set(newClass.map((classItem) => classItem.name))]
720
+
721
+ return allUniqueClassName.map((uniqueClass) => {
722
+ const allClassWithSameName = newClass.filter((localClass) => localClass.name === uniqueClass)
723
+
724
+ return allClassWithSameName.reduce((acc, classItem) => {
725
+ return {
726
+ ...acc,
727
+ ...classItem,
728
+ fields: {
729
+ // We merge fields that have the same name and then we add the new fields
730
+ ...acc.fields,
731
+ ...classItem.fields,
732
+ ...this.defaultFields(),
733
+ },
734
+ permissions:
735
+ classItem.permissions || acc.permissions
736
+ ? {
737
+ // Order is important because we put the provided schema before so we always consider
738
+ // the provided schema as the source of truth
739
+ ...classItem.permissions,
740
+ ...acc.permissions,
741
+ }
742
+ : undefined,
743
+ }
744
+ }, allClassWithSameName[0] as ClassInterface<T>)
745
+ })
746
+ }
747
+
748
+ defaultClass(schema?: SchemaInterface<T>): ClassInterface<T>[] {
749
+ return this.mergeClass([
750
+ ...(schema?.classes || []),
751
+ this.userClass(),
752
+ this.sessionClass(),
753
+ this.roleClass(),
754
+ this.internalConfigClass(),
755
+ ])
756
+ }
757
+ }