@appweaver/data 0.0.0-stage → 2.0.0

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 (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -2
  3. package/config.d.ts +6 -0
  4. package/config.js +22 -0
  5. package/database/create-client.d.ts +11 -0
  6. package/database/create-client.js +145 -0
  7. package/database/database-config.d.ts +14 -0
  8. package/database/database-config.js +40 -0
  9. package/database/database-error.d.ts +49 -0
  10. package/database/database-error.js +216 -0
  11. package/database/index.d.ts +3 -0
  12. package/database/index.js +19 -0
  13. package/database/prisma-database.d.ts +33 -0
  14. package/database/prisma-database.js +178 -0
  15. package/database/run-transaction.d.ts +21 -0
  16. package/database/run-transaction.js +15 -0
  17. package/export/export-config.d.ts +7 -0
  18. package/export/export-config.js +17 -0
  19. package/export/export-service.d.ts +22 -0
  20. package/export/export-service.js +188 -0
  21. package/export/index.d.ts +1 -0
  22. package/export/index.js +17 -0
  23. package/factory/create-model.d.ts +11 -0
  24. package/factory/create-model.js +479 -0
  25. package/factory/create-policy.d.ts +12 -0
  26. package/factory/create-policy.js +20 -0
  27. package/factory/create-service.d.ts +14 -0
  28. package/factory/create-service.js +116 -0
  29. package/index.d.ts +22 -0
  30. package/index.js +38 -0
  31. package/internal.d.ts +2 -0
  32. package/internal.js +6 -0
  33. package/module.d.ts +3 -0
  34. package/module.js +81 -0
  35. package/package.json +67 -3
  36. package/prisma/client/browser.d.ts +39 -0
  37. package/prisma/client/browser.js +56 -0
  38. package/prisma/client/client.d.ts +58 -0
  39. package/prisma/client/client.js +70 -0
  40. package/prisma/client/commonInputTypes.d.ts +345 -0
  41. package/prisma/client/commonInputTypes.js +11 -0
  42. package/prisma/client/enums.d.ts +1 -0
  43. package/prisma/client/enums.js +11 -0
  44. package/prisma/client/internal/class.d.ts +198 -0
  45. package/prisma/client/internal/class.js +85 -0
  46. package/prisma/client/internal/prismaNamespace.d.ts +1125 -0
  47. package/prisma/client/internal/prismaNamespace.js +204 -0
  48. package/prisma/client/internal/prismaNamespaceBrowser.d.ts +137 -0
  49. package/prisma/client/internal/prismaNamespaceBrowser.js +175 -0
  50. package/prisma/client/models/ApiKey.d.ts +1167 -0
  51. package/prisma/client/models/ApiKey.js +2 -0
  52. package/prisma/client/models/ConnectedAccount.d.ts +1094 -0
  53. package/prisma/client/models/ConnectedAccount.js +2 -0
  54. package/prisma/client/models/File.d.ts +1291 -0
  55. package/prisma/client/models/File.js +2 -0
  56. package/prisma/client/models/OneTimeToken.d.ts +1055 -0
  57. package/prisma/client/models/OneTimeToken.js +2 -0
  58. package/prisma/client/models/Permission.d.ts +1209 -0
  59. package/prisma/client/models/Permission.js +2 -0
  60. package/prisma/client/models/Role.d.ts +1209 -0
  61. package/prisma/client/models/Role.js +2 -0
  62. package/prisma/client/models/Seeder.d.ts +1023 -0
  63. package/prisma/client/models/Seeder.js +2 -0
  64. package/prisma/client/models.d.ts +8 -0
  65. package/prisma/client/models.js +2 -0
  66. package/resource/load-data-resources.d.ts +13 -0
  67. package/resource/load-data-resources.js +37 -0
  68. package/resource/model-schema.d.ts +34 -0
  69. package/resource/model-schema.js +54 -0
  70. package/resource/resource-config.d.ts +16 -0
  71. package/resource/resource-config.js +62 -0
  72. package/resource/resource-loader.d.ts +25 -0
  73. package/resource/resource-loader.js +138 -0
  74. package/resource/resource-service.d.ts +462 -0
  75. package/resource/resource-service.js +1042 -0
  76. package/resource/schemas/index.d.ts +3 -0
  77. package/resource/schemas/index.js +19 -0
  78. package/resource/schemas/resource-aggregate-schema.d.ts +53 -0
  79. package/resource/schemas/resource-aggregate-schema.js +130 -0
  80. package/resource/schemas/resource-filter-schema.d.ts +39 -0
  81. package/resource/schemas/resource-filter-schema.js +153 -0
  82. package/resource/schemas/resource-sort-schema.d.ts +38 -0
  83. package/resource/schemas/resource-sort-schema.js +113 -0
  84. package/resource/utils/aggregate-util.d.ts +113 -0
  85. package/resource/utils/aggregate-util.js +322 -0
  86. package/resource/utils/cursor-util.d.ts +60 -0
  87. package/resource/utils/cursor-util.js +116 -0
  88. package/resource/utils/date-util.d.ts +22 -0
  89. package/resource/utils/date-util.js +112 -0
  90. package/resource/utils/delete-util.d.ts +131 -0
  91. package/resource/utils/delete-util.js +385 -0
  92. package/resource/utils/filter-util.d.ts +24 -0
  93. package/resource/utils/filter-util.js +289 -0
  94. package/resource/utils/prune-util.d.ts +11 -0
  95. package/resource/utils/prune-util.js +56 -0
  96. package/resource/utils/relation-util.d.ts +75 -0
  97. package/resource/utils/relation-util.js +400 -0
  98. package/resource/utils/sort-util.d.ts +31 -0
  99. package/resource/utils/sort-util.js +235 -0
  100. package/resource/utils/text-search-util.d.ts +14 -0
  101. package/resource/utils/text-search-util.js +39 -0
  102. package/resources.d.ts +10 -0
  103. package/resources.js +26 -0
  104. package/security/auth-config.d.ts +23 -0
  105. package/security/auth-config.js +43 -0
  106. package/security/create-auth-resources.d.ts +28 -0
  107. package/security/create-auth-resources.js +163 -0
  108. package/security/helper.d.ts +109 -0
  109. package/security/helper.js +298 -0
  110. package/security/resources/api-key/model.d.ts +2 -0
  111. package/security/resources/api-key/model.js +67 -0
  112. package/security/resources/api-key/policy.d.ts +2 -0
  113. package/security/resources/api-key/policy.js +27 -0
  114. package/security/resources/api-key/service.d.ts +3 -0
  115. package/security/resources/api-key/service.js +34 -0
  116. package/security/resources/connected-account/model.d.ts +2 -0
  117. package/security/resources/connected-account/model.js +57 -0
  118. package/security/resources/connected-account/service.d.ts +2 -0
  119. package/security/resources/connected-account/service.js +7 -0
  120. package/security/resources/one-time-token/model.d.ts +2 -0
  121. package/security/resources/one-time-token/model.js +36 -0
  122. package/security/resources/permission/model.d.ts +2 -0
  123. package/security/resources/permission/model.js +16 -0
  124. package/security/resources/role/model.d.ts +2 -0
  125. package/security/resources/role/model.js +22 -0
  126. package/security/store/database-security-store.d.ts +5 -0
  127. package/security/store/database-security-store.js +54 -0
  128. package/seeder/create-seeder.d.ts +29 -0
  129. package/seeder/create-seeder.js +28 -0
  130. package/seeder/index.d.ts +2 -0
  131. package/seeder/index.js +18 -0
  132. package/seeder/resources/seeder/model.d.ts +2 -0
  133. package/seeder/resources/seeder/model.js +38 -0
  134. package/seeder/seeder.d.ts +29 -0
  135. package/seeder/seeder.js +197 -0
  136. package/storage/file-service.d.ts +133 -0
  137. package/storage/file-service.js +586 -0
  138. package/storage/image-config.d.ts +3 -0
  139. package/storage/image-config.js +11 -0
  140. package/storage/resources/file/model.d.ts +2 -0
  141. package/storage/resources/file/model.js +80 -0
  142. package/types/auth.d.ts +23 -0
  143. package/types/auth.js +2 -0
  144. package/types/generated.d.ts +366 -0
  145. package/types/generated.js +3 -0
  146. package/types/storage.d.ts +35 -0
  147. package/types/storage.js +2 -0
  148. package/utils/export-util.d.ts +8 -0
  149. package/utils/export-util.js +26 -0
  150. package/utils/file-util.d.ts +106 -0
  151. package/utils/file-util.js +254 -0
  152. package/utils/model-util.d.ts +36 -0
  153. package/utils/model-util.js +154 -0
  154. package/utils/policy-util.d.ts +18 -0
  155. package/utils/policy-util.js +28 -0
  156. package/utils/virtual-util.d.ts +11 -0
  157. package/utils/virtual-util.js +53 -0
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resourceAuthModel = resourceAuthModel;
4
+ exports.resourceAuthService = resourceAuthService;
5
+ exports.hashPassword = hashPassword;
6
+ exports.checkPassword = checkPassword;
7
+ exports.needsRehash = needsRehash;
8
+ exports.updatePasswordHash = updatePasswordHash;
9
+ exports.validatePasswordComplexity = validatePasswordComplexity;
10
+ exports.isOAuth2Enabled = isOAuth2Enabled;
11
+ exports.hasRole = hasRole;
12
+ exports.hasRoles = hasRoles;
13
+ exports.hasPermission = hasPermission;
14
+ exports.hasPermissions = hasPermissions;
15
+ const node_crypto_1 = require("node:crypto");
16
+ const common_1 = require("@appweaver/common");
17
+ const auth_config_1 = require("./auth-config");
18
+ const internal_1 = require("@appweaver/core/internal");
19
+ const core_1 = require("@appweaver/core");
20
+ /** The Argon2id cost of new password hashes (OWASP minimum): m KiB of memory, t passes, p lanes. Each hash stores its
21
+ * own, and {@link needsRehash} flags the ones made with other parameters */
22
+ const ARGON2_PARAMS = { m: 19456, t: 2, p: 1 };
23
+ const ARGON2_VERSION = 19;
24
+ const ARGON2_SALT_BYTES = 16;
25
+ const ARGON2_KEY_BYTES = 32;
26
+ /** A stored hash in the PHC string format, i.e. `$argon2id$v=19$m=19456,t=2,p=1$<salt>$<key>` with unpadded base64
27
+ * salt and key */
28
+ const ARGON2_HASH_PATTERN = /^\$argon2id\$v=(\d+)\$m=(\d{1,7}),t=(\d{1,3}),p=(\d{1,3})\$([A-Za-z0-9+/]{11,})\$([A-Za-z0-9+/]{6,})$/;
29
+ /**
30
+ * Evaluates and retrieves the resource authentication model from the available resource models.
31
+ *
32
+ * The function iterates through the resource models in the given context and returns the first model
33
+ * that satisfies the `isResourceAuthModel` condition. If no such model is found, it returns undefined.
34
+ *
35
+ * @return {ResourceModel | undefined} The resource authentication model if present, otherwise undefined.
36
+ */
37
+ function resourceAuthModel() {
38
+ for (const model of internal_1.context.resource.models.values()) {
39
+ if ((0, common_1.isResourceAuthModel)(model)) {
40
+ return model;
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Provides authentication service for a resource by identifying and injecting
46
+ * the appropriate resource authentication service if available.
47
+ *
48
+ * @return {IResourceService<AuthUser, AuthUser> | undefined} The resource authentication service
49
+ * instance if found, otherwise undefined.
50
+ */
51
+ function resourceAuthService() {
52
+ for (const service of internal_1.context.resource.services.values()) {
53
+ if ((0, common_1.isResourceAuthService)(service)) {
54
+ return (0, core_1.injectService)(service[common_1.RESOURCE_NAME]);
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Hashes a plain text password with the Argon2id function of the runtime and a random salt. The hash holds the
60
+ * Argon2 parameters and the salt, in the PHC string format `$argon2id$v=19$m=19456,t=2,p=1$<salt>$<key>`.
61
+ *
62
+ * @param {string} password - The plain text password to be hashed.
63
+ * @return {Promise<string>} A promise that resolves to the hashed password.
64
+ */
65
+ async function hashPassword(password) {
66
+ const salt = (0, node_crypto_1.randomBytes)(ARGON2_SALT_BYTES);
67
+ const key = await deriveKey(password, salt, ARGON2_PARAMS, ARGON2_KEY_BYTES);
68
+ const { m, t, p } = ARGON2_PARAMS;
69
+ return `$argon2id$v=${ARGON2_VERSION}$m=${m},t=${t},p=${p}$${toPhcBase64(salt)}$${toPhcBase64(key)}`;
70
+ }
71
+ /**
72
+ * Validates a plain text password against a hashed password, using the parameters stored in the hash. A hash not in
73
+ * the form {@link hashPassword} produces never matches.
74
+ *
75
+ * @param {string} password - The plain text password to validate.
76
+ * @param {string} passwordHash - The hashed password to compare against.
77
+ * @return {Promise<boolean>} Returns a promise that resolves to `true` if the password matches the hash,
78
+ * otherwise `false`.
79
+ */
80
+ async function checkPassword(password, passwordHash) {
81
+ const parsed = parseHash(passwordHash);
82
+ if (!parsed) {
83
+ return false;
84
+ }
85
+ const key = await deriveKey(password, parsed.salt, parsed.params, parsed.key.length);
86
+ return (0, node_crypto_1.timingSafeEqual)(key, parsed.key);
87
+ }
88
+ /**
89
+ * Tells whether a password hash was made with other parameters than {@link hashPassword} uses now, so it should be
90
+ * replaced with a new hash of the password, e.g. on the next successful login.
91
+ *
92
+ * @param {string} passwordHash - The stored password hash.
93
+ * @return {boolean} `true` if the hash is not a current Argon2id hash, otherwise `false`.
94
+ */
95
+ function needsRehash(passwordHash) {
96
+ const parsed = parseHash(passwordHash);
97
+ return (!parsed ||
98
+ parsed.key.length !== ARGON2_KEY_BYTES ||
99
+ parsed.params.m !== ARGON2_PARAMS.m ||
100
+ parsed.params.t !== ARGON2_PARAMS.t ||
101
+ parsed.params.p !== ARGON2_PARAMS.p);
102
+ }
103
+ /**
104
+ * Updates the password hash for a given user and optionally logs the user out by setting a logout timestamp.
105
+ *
106
+ * @param {Partial<AuthUser>} authUser - The user object containing authentication details. Only partial fields are
107
+ * expected.
108
+ * @param {string} [password] - The new password to hash and set for the user. If not provided, no password update
109
+ * occurs.
110
+ * @param {boolean} [logout=false] - Indicates whether to log the user out by setting the logout timestamp.
111
+ * @return Resolves when the password hash is updated and optional logout processing is complete.
112
+ * @throws {AuthError} `AUTH_PASSWORD_WEAK` if the provided password does not satisfy password complexity requirements.
113
+ */
114
+ async function updatePasswordHash(authUser, password, logout = false) {
115
+ if (password) {
116
+ if (!auth_config_1.passwordConfig.ENABLED) {
117
+ throw new common_1.AuthError(common_1.ErrorCode.AuthPasswordDisabled, 'Password creation is not supported');
118
+ }
119
+ const result = validatePasswordComplexity(password);
120
+ if (!result.valid) {
121
+ throw new common_1.AuthError(common_1.ErrorCode.AuthPasswordWeak, result.message, {
122
+ reason: result.message
123
+ });
124
+ }
125
+ authUser.passwordHash = await hashPassword(password);
126
+ if (logout) {
127
+ authUser.logoutAt = new Date();
128
+ }
129
+ }
130
+ delete authUser['password'];
131
+ }
132
+ /**
133
+ * Validates the complexity of a given password based on the configured rules.
134
+ * @param {string} password - The password to be validated.
135
+ * @return {ValidationResult} An object with the validation result and message.
136
+ */
137
+ function validatePasswordComplexity(password) {
138
+ const minLength = auth_config_1.passwordConfig.MIN_LENGTH;
139
+ if (password.length < minLength) {
140
+ return {
141
+ valid: false,
142
+ message: `Password must be at least ${minLength} characters long`
143
+ };
144
+ }
145
+ const maxLength = auth_config_1.passwordConfig.MAX_LENGTH;
146
+ if (password.length > maxLength) {
147
+ return {
148
+ valid: false,
149
+ message: `Password must not be longer than ${maxLength} characters`
150
+ };
151
+ }
152
+ if (!/[A-Z]/.test(password) && auth_config_1.passwordConfig.UPPERCASE) {
153
+ return {
154
+ valid: false,
155
+ message: `Password must have at least one uppercase character`
156
+ };
157
+ }
158
+ if (!/[a-z]/.test(password) && auth_config_1.passwordConfig.LOWERCASE) {
159
+ return {
160
+ valid: false,
161
+ message: `Password must have at least one lowercase character`
162
+ };
163
+ }
164
+ if (!/[0-9]/.test(password) && auth_config_1.passwordConfig.NUMERIC) {
165
+ return {
166
+ valid: false,
167
+ message: `Password must have at least one numeric character`
168
+ };
169
+ }
170
+ if (!/[^A-Za-z0-9]/.test(password) && auth_config_1.passwordConfig.SPECIAL) {
171
+ return {
172
+ valid: false,
173
+ message: `Password must have at least one special character`
174
+ };
175
+ }
176
+ return { valid: true, message: 'OK' };
177
+ }
178
+ /**
179
+ * Checks whether at least one OAuth2 provider is enabled, by resolving each OAuth2 auth source to its
180
+ * `SECURITY_OAUTH2_<PROVIDER>_ENABLED` config flag. New providers are picked up automatically as long as they follow
181
+ * that naming convention.
182
+ *
183
+ * @return {boolean} True when any OAuth2 provider is enabled.
184
+ */
185
+ function isOAuth2Enabled() {
186
+ // Read by the flattened keys, since the OAuth2 config belongs to the web module
187
+ return Object.values(common_1.AuthSource)
188
+ .filter((source) => source.startsWith('oauth2'))
189
+ .some((source) => common_1.config.bool(`SECURITY_OAUTH2_${source.replace('oauth2', '').toUpperCase()}_ENABLED`, false));
190
+ }
191
+ /**
192
+ * Checks if the given user has a specific role.
193
+ *
194
+ * @param {AuthUser} authUser - The authenticated user object containing role information.
195
+ * @param {string} role - The name of the role to check for.
196
+ * @return {boolean} True if the user has the specified role; otherwise, false.
197
+ */
198
+ function hasRole(authUser, role) {
199
+ return authUser.roles.some((r) => r.name === role);
200
+ }
201
+ /**
202
+ * Checks if the authenticated user has the required roles based on the specified operator.
203
+ *
204
+ * @param {AuthUser} authUser - The authenticated user whose roles are being checked.
205
+ * @param {Array<string> | undefined} roles - The list of roles to check against. If undefined, the method returns
206
+ * true`.
207
+ * @param {'or' | 'and'} [operator='or'] - The logical operator for role matching. Use 'or' to check if the user has at
208
+ * least one role, and 'and' to check if the user has all roles.
209
+ * @return {boolean} Returns `true` if the user satisfies the role check based on the specified operator, otherwise
210
+ * `false`.
211
+ */
212
+ function hasRoles(authUser, roles, operator = 'or') {
213
+ if (!roles) {
214
+ return true;
215
+ }
216
+ const predicate = (role) => hasRole(authUser, role);
217
+ return operator === 'and' ? roles.every(predicate) : roles.some(predicate);
218
+ }
219
+ /**
220
+ * Determines if the authenticated user has the specified permission.
221
+ *
222
+ * @param {AuthUser} authUser - The authenticated user object containing roles and permissions.
223
+ * @param {string} permission - The name of the permission to check for.
224
+ * @return {boolean} Returns true if the user has the specified permission, otherwise false.
225
+ */
226
+ function hasPermission(authUser, permission) {
227
+ return authUser.roles
228
+ .flatMap((r) => r.permissions)
229
+ .some((p) => p.name === permission);
230
+ }
231
+ /**
232
+ * Checks if the authenticated user has the specified permissions based on the given operator.
233
+ *
234
+ * @param {AuthUser} authUser - The authenticated user object.
235
+ * @param {string[] | undefined} permissions - The list of permissions to check. If undefined, it defaults to granting
236
+ * access.
237
+ * @param {'or' | 'and'} [operator='or'] - Determines whether all permissions (and) or at least one permission (or) must
238
+ * be satisfied.
239
+ * @return {boolean} Returns true if the user satisfies the permission requirements, false otherwise.
240
+ */
241
+ function hasPermissions(authUser, permissions, operator = 'or') {
242
+ if (!permissions) {
243
+ return true;
244
+ }
245
+ const predicate = (perm) => hasPermission(authUser, perm);
246
+ return operator === 'and'
247
+ ? permissions.every(predicate)
248
+ : permissions.some(predicate);
249
+ }
250
+ /**
251
+ * Parses a PHC Argon2id hash, rejecting the parameters out of bounds, so a tampered hash cannot make the check exhaust
252
+ * the memory or CPU.
253
+ */
254
+ function parseHash(passwordHash) {
255
+ const match = ARGON2_HASH_PATTERN.exec(passwordHash);
256
+ if (!match || Number(match[1]) !== ARGON2_VERSION) {
257
+ return null;
258
+ }
259
+ const params = {
260
+ m: Number(match[2]),
261
+ t: Number(match[3]),
262
+ p: Number(match[4])
263
+ };
264
+ const bounded = params.p >= 1 &&
265
+ params.p <= 16 &&
266
+ params.m >= 8 * params.p &&
267
+ params.m <= 262144 &&
268
+ params.t >= 1 &&
269
+ params.t <= 10;
270
+ if (!bounded) {
271
+ return null;
272
+ }
273
+ return {
274
+ params,
275
+ salt: Buffer.from(match[5], 'base64'),
276
+ key: Buffer.from(match[6], 'base64')
277
+ };
278
+ }
279
+ /**
280
+ * Derives the Argon2id key of a password.
281
+ */
282
+ function deriveKey(password, salt, params, keyBytes) {
283
+ const parameters = {
284
+ message: password,
285
+ nonce: salt,
286
+ memory: params.m,
287
+ passes: params.t,
288
+ parallelism: params.p,
289
+ tagLength: keyBytes
290
+ };
291
+ return new Promise((resolve, reject) => {
292
+ (0, node_crypto_1.argon2)('argon2id', parameters, (error, key) => error ? reject(error) : resolve(key));
293
+ });
294
+ }
295
+ /** Encodes bytes in the unpadded base64 of the PHC string format. */
296
+ function toPhcBase64(bytes) {
297
+ return bytes.toString('base64').replace(/=+$/, '');
298
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").ResourceModel | undefined;
2
+ export default _default;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@appweaver/common");
4
+ const auth_config_1 = require("../../auth-config");
5
+ const create_model_1 = require("../../../factory/create-model");
6
+ const helper_1 = require("../../helper");
7
+ const authModel = (0, helper_1.resourceAuthModel)();
8
+ const shouldCreateModel = auth_config_1.apiKeyConfig.ENABLED || auth_config_1.apiKeyConfig.KEEP_DATABASE_TABLE;
9
+ exports.default = shouldCreateModel
10
+ ? (0, create_model_1.createModel)({
11
+ name: 'ApiKey',
12
+ // Cascades from the auth model, so it is soft deleted together with it
13
+ softDelete: !!authModel?.config.softDelete,
14
+ scalars: {
15
+ key: {
16
+ type: 'string'
17
+ },
18
+ keyHash: {
19
+ type: 'string',
20
+ unique: true,
21
+ hidden: true
22
+ },
23
+ name: {
24
+ type: 'string',
25
+ required: false,
26
+ maxLength: 255
27
+ },
28
+ description: {
29
+ type: 'string',
30
+ required: false
31
+ },
32
+ enabled: {
33
+ type: 'boolean',
34
+ default: true
35
+ },
36
+ expiresAt: {
37
+ type: 'dateTime',
38
+ required: false
39
+ }
40
+ },
41
+ ...(authModel
42
+ ? {
43
+ relations: {
44
+ [(0, common_1.uncapitalize)(authModel.name)]: {
45
+ model: authModel.name,
46
+ type: 'manyToOne',
47
+ mappedBy: 'apiKeys',
48
+ // API keys are meaningless without their user
49
+ onDelete: 'cascade',
50
+ input: {
51
+ type: 'none'
52
+ },
53
+ output: {
54
+ type: 'none'
55
+ }
56
+ }
57
+ }
58
+ }
59
+ : {}),
60
+ create: {
61
+ omit: ['key']
62
+ },
63
+ update: {
64
+ omit: ['key', 'expiresAt']
65
+ }
66
+ })
67
+ : undefined;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").ResourcePolicyConfig<Record<string, any>> | undefined;
2
+ export default _default;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@appweaver/common");
4
+ const auth_config_1 = require("../../auth-config");
5
+ const helper_1 = require("../../helper");
6
+ const create_policy_1 = require("../../../factory/create-policy");
7
+ function authModelField() {
8
+ return (0, common_1.uncapitalize)((0, helper_1.resourceAuthModel)().name);
9
+ }
10
+ exports.default = auth_config_1.apiKeyConfig.ENABLED
11
+ ? (0, create_policy_1.createPolicy)({
12
+ modelName: 'ApiKey',
13
+ checkAccess: (user, resource, action) => {
14
+ const authFieldName = authModelField();
15
+ const currentAuthId = user.id;
16
+ if (action === 'create') {
17
+ return resource[authFieldName].id === currentAuthId;
18
+ }
19
+ else {
20
+ return resource[`${authFieldName}Id`] === currentAuthId;
21
+ }
22
+ },
23
+ readRestrictions: (user) => {
24
+ return { [authModelField()]: { id: user.id } };
25
+ }
26
+ })
27
+ : undefined;
@@ -0,0 +1,3 @@
1
+ import { ApiKey } from '../../../types/generated';
2
+ declare const _default: import("@appweaver/common").Ctor<import("../../..").ResourceService<ApiKey, ApiKey, ApiKey, any, import("@appweaver/common").QueryFilter<ApiKey>, ApiKey>> | undefined;
3
+ export default _default;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@appweaver/common");
4
+ const auth_config_1 = require("../../auth-config");
5
+ const create_service_1 = require("../../../factory/create-service");
6
+ const helper_1 = require("../../helper");
7
+ const core_1 = require("@appweaver/core");
8
+ exports.default = auth_config_1.apiKeyConfig.ENABLED
9
+ ? (0, create_service_1.createService)({
10
+ modelName: 'ApiKey',
11
+ beforeCreate: (data) => {
12
+ data.key = (0, common_1.randomString)(64, { special: false });
13
+ data.keyHash = (0, common_1.makeHash)(data.key);
14
+ // Add relation to the current user when creating a new API Key
15
+ data[(0, common_1.uncapitalize)((0, helper_1.resourceAuthModel)().name)] = {
16
+ id: (0, core_1.currentAuthUser)().id
17
+ };
18
+ // Enforces max API key duration based on config value
19
+ const maxDuration = auth_config_1.apiKeyConfig.MAX_DURATION;
20
+ if (maxDuration) {
21
+ const maxExpiryDate = new Date(Date.now() + maxDuration * 1000);
22
+ if (!data.expiresAt ||
23
+ new Date(data.expiresAt).getTime() > maxExpiryDate.getTime()) {
24
+ data.expiresAt = maxExpiryDate;
25
+ }
26
+ }
27
+ },
28
+ afterCreate: async (data) => {
29
+ const service = (0, core_1.injectService)('ApiKey');
30
+ await service.update(data.id, { key: `...${data.key.slice(-6)}` });
31
+ data.key = `${data.id}${auth_config_1.apiKeyConfig.DELIMITER}${data.key}`;
32
+ }
33
+ })
34
+ : undefined;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").ResourceModel | undefined;
2
+ export default _default;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@appweaver/common");
4
+ const auth_config_1 = require("../../auth-config");
5
+ const create_model_1 = require("../../../factory/create-model");
6
+ const helper_1 = require("../../helper");
7
+ const authModel = (0, helper_1.resourceAuthModel)();
8
+ const shouldCreateModel = (0, helper_1.isOAuth2Enabled)() ||
9
+ auth_config_1.connectedAccountConfig.CONNECTED_ACCOUNTS_KEEP_DATABASE_TABLE;
10
+ exports.default = shouldCreateModel
11
+ ? (0, create_model_1.createModel)({
12
+ name: 'ConnectedAccount',
13
+ // Cascades from the auth model, so it is soft deleted together with it
14
+ softDelete: !!authModel?.config.softDelete,
15
+ audit: {
16
+ createdById: false
17
+ },
18
+ scalars: {
19
+ provider: {
20
+ type: 'string',
21
+ maxLength: 255
22
+ },
23
+ providerAccountId: {
24
+ type: 'string',
25
+ maxLength: 255
26
+ },
27
+ scope: {
28
+ type: 'string',
29
+ required: false
30
+ },
31
+ lastLoginAt: {
32
+ type: 'dateTime'
33
+ }
34
+ },
35
+ ...(authModel
36
+ ? {
37
+ relations: {
38
+ [(0, common_1.uncapitalize)(authModel.name)]: {
39
+ model: authModel.name,
40
+ type: 'manyToOne',
41
+ mappedBy: 'connectedAccounts',
42
+ // Connected accounts are meaningless without their user
43
+ onDelete: 'cascade',
44
+ input: {
45
+ type: 'none'
46
+ },
47
+ output: {
48
+ type: 'none'
49
+ }
50
+ }
51
+ }
52
+ }
53
+ : {}),
54
+ // A provider account may only ever be linked to a single user
55
+ unique: [['provider', 'providerAccountId']]
56
+ })
57
+ : undefined;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").Ctor<import("../../..").ResourceService<any, any, any, any, import("@appweaver/common").QueryFilter<any>, any>> | undefined;
2
+ export default _default;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const create_service_1 = require("../../../factory/create-service");
4
+ const helper_1 = require("../../helper");
5
+ exports.default = (0, helper_1.isOAuth2Enabled)()
6
+ ? (0, create_service_1.createService)({ modelName: 'ConnectedAccount' })
7
+ : undefined;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").ResourceModel | undefined;
2
+ export default _default;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@appweaver/core");
4
+ const create_model_1 = require("../../../factory/create-model");
5
+ // The database store is the one `auto` selects with a database
6
+ const shouldCreateModel = ['auto', 'database'].includes(core_1.securityStoreConfig.PROVIDER) ||
7
+ core_1.securityStoreConfig.KEEP_DATABASE_TABLE;
8
+ exports.default = shouldCreateModel
9
+ ? (0, create_model_1.createModel)({
10
+ name: 'OneTimeToken',
11
+ // Tokens are only created and deleted, never updated
12
+ audit: {
13
+ updatedAt: false,
14
+ createdById: false
15
+ },
16
+ scalars: {
17
+ tokenHash: {
18
+ type: 'string',
19
+ maxLength: 128,
20
+ unique: true
21
+ },
22
+ purpose: {
23
+ type: 'string',
24
+ maxLength: 64
25
+ },
26
+ data: {
27
+ type: 'json'
28
+ },
29
+ expiresAt: {
30
+ type: 'dateTime'
31
+ }
32
+ },
33
+ // Deleting the expired tokens filters by the expiration
34
+ index: [['expiresAt']]
35
+ })
36
+ : undefined;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").ResourceModel;
2
+ export default _default;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const create_model_1 = require("../../../factory/create-model");
4
+ exports.default = (0, create_model_1.createModel)({
5
+ name: 'Permission',
6
+ scalars: {
7
+ name: {
8
+ type: 'string',
9
+ unique: true,
10
+ maxLength: 255
11
+ }
12
+ },
13
+ read: {
14
+ omit: ['updatedAt', 'createdAt', 'createdById']
15
+ }
16
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@appweaver/common").ResourceModel;
2
+ export default _default;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const create_model_1 = require("../../../factory/create-model");
4
+ exports.default = (0, create_model_1.createModel)({
5
+ name: 'Role',
6
+ scalars: {
7
+ name: {
8
+ type: 'string',
9
+ unique: true,
10
+ maxLength: 255
11
+ }
12
+ },
13
+ relations: {
14
+ permissions: {
15
+ model: 'Permission',
16
+ type: 'manyToMany'
17
+ }
18
+ },
19
+ read: {
20
+ omit: ['updatedAt', 'createdAt', 'createdById']
21
+ }
22
+ });
@@ -0,0 +1,5 @@
1
+ import { SecurityStore, ValidationResult } from '@appweaver/common';
2
+ export declare class DatabaseSecurityStore extends SecurityStore {
3
+ generateOneTimeToken<T = any>(purpose: string, data: T, ttl: number): Promise<string>;
4
+ useOneTimeToken<T = any>(token: string, purpose: string, validateContent?: (value: T) => ValidationResult): Promise<T>;
5
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatabaseSecurityStore = void 0;
4
+ const common_1 = require("@appweaver/common");
5
+ const core_1 = require("@appweaver/core");
6
+ class DatabaseSecurityStore extends common_1.SecurityStore {
7
+ /** @internal */
8
+ _db = (0, core_1.inject)(common_1.Database);
9
+ async generateOneTimeToken(purpose, data, ttl) {
10
+ const token = (0, common_1.generateToken)('bytes', 64);
11
+ const oneTimeTokens = this._db.client().oneTimeToken;
12
+ // Expired tokens are removed here, so they need no scheduled cleanup
13
+ await oneTimeTokens.deleteMany({
14
+ where: { expiresAt: { lt: new Date() } }
15
+ });
16
+ await oneTimeTokens.create({
17
+ data: {
18
+ tokenHash: (0, common_1.makeHash)(token),
19
+ purpose,
20
+ data: data,
21
+ expiresAt: new Date(Date.now() + ttl)
22
+ }
23
+ });
24
+ return token;
25
+ }
26
+ async useOneTimeToken(token, purpose, validateContent) {
27
+ const oneTimeTokens = this._db.client().oneTimeToken;
28
+ const oneTimeToken = await oneTimeTokens.findUnique({
29
+ where: { tokenHash: (0, common_1.makeHash)(token) }
30
+ });
31
+ if (oneTimeToken === null ||
32
+ oneTimeToken.purpose !== purpose ||
33
+ oneTimeToken.expiresAt.getTime() < Date.now()) {
34
+ throw new common_1.AuthError(common_1.ErrorCode.AuthInvalidToken, 'Invalid or expired token provided');
35
+ }
36
+ const data = oneTimeToken.data;
37
+ // A token failing the validation is kept, so it can be used again
38
+ if (validateContent) {
39
+ const result = validateContent(data);
40
+ if (!result.valid) {
41
+ throw new common_1.AuthError(common_1.ErrorCode.AuthInvalidToken, result.message);
42
+ }
43
+ }
44
+ // Only the one of concurrent uses that removes the token gets its data
45
+ const { count } = await oneTimeTokens.deleteMany({
46
+ where: { id: oneTimeToken.id }
47
+ });
48
+ if (count === 0) {
49
+ throw new common_1.AuthError(common_1.ErrorCode.AuthInvalidToken, 'Invalid or expired token provided');
50
+ }
51
+ return data;
52
+ }
53
+ }
54
+ exports.DatabaseSecurityStore = DatabaseSecurityStore;