wabe 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authentication/OTP.d.ts +10 -6
- package/dist/authentication/interface.d.ts +3 -5
- package/dist/authentication/security.d.ts +1 -2
- package/dist/database/DatabaseController.d.ts +11 -2
- package/dist/database/interface.d.ts +2 -0
- package/dist/index.js +455 -367
- package/dist/server/generateCodegen.d.ts +17 -0
- package/dist/server/index.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17231,14 +17231,14 @@ var require_plugin_crypto = __commonJS((exports) => {
|
|
|
17231
17231
|
function _interopDefault(ex) {
|
|
17232
17232
|
return ex && typeof ex === "object" && "default" in ex ? ex["default"] : ex;
|
|
17233
17233
|
}
|
|
17234
|
-
var
|
|
17234
|
+
var crypto2 = _interopDefault(__require("crypto"));
|
|
17235
17235
|
var createDigest = (algorithm, hmacKey, counter) => {
|
|
17236
|
-
const hmac =
|
|
17236
|
+
const hmac = crypto2.createHmac(algorithm, Buffer.from(hmacKey, "hex"));
|
|
17237
17237
|
const digest = hmac.update(Buffer.from(counter, "hex")).digest();
|
|
17238
17238
|
return digest.toString("hex");
|
|
17239
17239
|
};
|
|
17240
17240
|
var createRandomBytes = (size, encoding) => {
|
|
17241
|
-
return
|
|
17241
|
+
return crypto2.randomBytes(size).toString(encoding);
|
|
17242
17242
|
};
|
|
17243
17243
|
exports.createDigest = createDigest;
|
|
17244
17244
|
exports.createRandomBytes = createRandomBytes;
|
|
@@ -28905,13 +28905,13 @@ var SecondaryFactor;
|
|
|
28905
28905
|
SecondaryFactor2["QRCodeOTP"] = "qrcodeOTP";
|
|
28906
28906
|
})(SecondaryFactor ||= {});
|
|
28907
28907
|
// ../wabe/src/authentication/oauth/utils.ts
|
|
28908
|
-
import
|
|
28908
|
+
import crypto from "node:crypto";
|
|
28909
28909
|
var base64URLencode = (content) => {
|
|
28910
|
-
const hasher =
|
|
28910
|
+
const hasher = crypto.createHash("sha256").update(content);
|
|
28911
28911
|
const result = hasher.digest("base64");
|
|
28912
28912
|
return result.split("=")[0].replaceAll("+", "-").replaceAll("/", "_");
|
|
28913
28913
|
};
|
|
28914
|
-
var generateRandomValues = () =>
|
|
28914
|
+
var generateRandomValues = () => crypto.randomBytes(60).toString("base64url");
|
|
28915
28915
|
|
|
28916
28916
|
// ../wabe/src/authentication/oauth/Oauth2Client.ts
|
|
28917
28917
|
class OAuth2Client {
|
|
@@ -29048,7 +29048,7 @@ class Google {
|
|
|
29048
29048
|
}
|
|
29049
29049
|
// ../wabe/src/authentication/OTP.ts
|
|
29050
29050
|
var import_otplib = __toESM(require_otplib(), 1);
|
|
29051
|
-
import { createHash } from "node:crypto";
|
|
29051
|
+
import { createHash, randomBytes as randomBytes2 } from "node:crypto";
|
|
29052
29052
|
|
|
29053
29053
|
// ../wabe/src/utils/index.ts
|
|
29054
29054
|
import { timingSafeEqual } from "node:crypto";
|
|
@@ -29149,10 +29149,14 @@ var getNestedProperty = (obj, path) => {
|
|
|
29149
29149
|
};
|
|
29150
29150
|
var firstLetterInUpperCase = (str) => {
|
|
29151
29151
|
const indexOfFirstLetter = str.search(/[a-z]/i);
|
|
29152
|
+
if (indexOfFirstLetter < 0)
|
|
29153
|
+
return str;
|
|
29152
29154
|
return str.slice(0, indexOfFirstLetter) + str[indexOfFirstLetter]?.toUpperCase() + str.slice(indexOfFirstLetter + 1);
|
|
29153
29155
|
};
|
|
29154
29156
|
var firstLetterInLowerCase = (str) => {
|
|
29155
29157
|
const indexOfFirstLetter = str.search(/[a-z]/i);
|
|
29158
|
+
if (indexOfFirstLetter < 0)
|
|
29159
|
+
return str;
|
|
29156
29160
|
return str.slice(0, indexOfFirstLetter) + str[indexOfFirstLetter]?.toLowerCase() + str.slice(indexOfFirstLetter + 1);
|
|
29157
29161
|
};
|
|
29158
29162
|
var getClassFromClassName = (className, config) => {
|
|
@@ -29186,56 +29190,15 @@ var tokenize = (value) => {
|
|
|
29186
29190
|
return tmpValue.replace(/[\W_]+/g, " ").trim();
|
|
29187
29191
|
};
|
|
29188
29192
|
|
|
29189
|
-
// ../wabe/src/authentication/OTP.ts
|
|
29190
|
-
var ONE_WINDOW = 1;
|
|
29191
|
-
|
|
29192
|
-
class OTP {
|
|
29193
|
-
secret;
|
|
29194
|
-
internalTotp;
|
|
29195
|
-
constructor(rootKey) {
|
|
29196
|
-
this.secret = rootKey;
|
|
29197
|
-
this.internalTotp = import_otplib.totp.clone({
|
|
29198
|
-
window: [ONE_WINDOW, 0]
|
|
29199
|
-
});
|
|
29200
|
-
}
|
|
29201
|
-
deriveSecret(userId) {
|
|
29202
|
-
const hash = createHash("sha256").update(`${this.secret}:${userId}`).digest();
|
|
29203
|
-
return base32Encode(hash, "RFC4648", { padding: false });
|
|
29204
|
-
}
|
|
29205
|
-
generate(userId) {
|
|
29206
|
-
const secret = this.deriveSecret(userId);
|
|
29207
|
-
return this.internalTotp.generate(secret);
|
|
29208
|
-
}
|
|
29209
|
-
verify(otp, userId) {
|
|
29210
|
-
const secret = this.deriveSecret(userId);
|
|
29211
|
-
return this.internalTotp.verify({ secret, token: otp });
|
|
29212
|
-
}
|
|
29213
|
-
authenticatorGenerate(userId) {
|
|
29214
|
-
const secret = this.deriveSecret(userId);
|
|
29215
|
-
return import_otplib.authenticator.generate(secret);
|
|
29216
|
-
}
|
|
29217
|
-
authenticatorVerify(otp, userId) {
|
|
29218
|
-
const secret = this.deriveSecret(userId);
|
|
29219
|
-
return import_otplib.authenticator.verify({
|
|
29220
|
-
secret,
|
|
29221
|
-
token: otp
|
|
29222
|
-
});
|
|
29223
|
-
}
|
|
29224
|
-
generateKeyuri({
|
|
29225
|
-
userId,
|
|
29226
|
-
emailOrUsername,
|
|
29227
|
-
applicationName
|
|
29228
|
-
}) {
|
|
29229
|
-
const secret = this.deriveSecret(userId);
|
|
29230
|
-
return import_otplib.authenticator.keyuri(emailOrUsername, applicationName, secret);
|
|
29231
|
-
}
|
|
29232
|
-
}
|
|
29233
|
-
// ../wabe/src/authentication/Session.ts
|
|
29234
|
-
var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
|
|
29235
|
-
import crypto4 from "node:crypto";
|
|
29236
|
-
|
|
29237
29193
|
// ../wabe/src/utils/crypto.ts
|
|
29238
|
-
import {
|
|
29194
|
+
import {
|
|
29195
|
+
randomBytes,
|
|
29196
|
+
createCipheriv,
|
|
29197
|
+
createDecipheriv,
|
|
29198
|
+
createHmac,
|
|
29199
|
+
timingSafeEqual as timingSafeEqual2
|
|
29200
|
+
} from "node:crypto";
|
|
29201
|
+
import * as nodeCrypto from "node:crypto";
|
|
29239
29202
|
import { promisify } from "node:util";
|
|
29240
29203
|
var params = {
|
|
29241
29204
|
parallelism: 1,
|
|
@@ -29243,12 +29206,18 @@ var params = {
|
|
|
29243
29206
|
memory: 65536,
|
|
29244
29207
|
passes: 2
|
|
29245
29208
|
};
|
|
29209
|
+
var getNodeArgon2 = () => {
|
|
29210
|
+
const argon22 = nodeCrypto.argon2;
|
|
29211
|
+
if (!argon22)
|
|
29212
|
+
throw new Error("Argon2 is not supported in this runtime");
|
|
29213
|
+
return argon22;
|
|
29214
|
+
};
|
|
29246
29215
|
var hashArgon2 = async (text) => {
|
|
29247
29216
|
if (process.versions.bun)
|
|
29248
29217
|
return Bun.password.hash(text, { algorithm: "argon2id" });
|
|
29249
|
-
const
|
|
29218
|
+
const argon22 = promisify(getNodeArgon2());
|
|
29250
29219
|
const nonce = randomBytes(16);
|
|
29251
|
-
const result = await
|
|
29220
|
+
const result = await argon22("argon2id", {
|
|
29252
29221
|
message: text,
|
|
29253
29222
|
nonce,
|
|
29254
29223
|
...params
|
|
@@ -29259,6 +29228,8 @@ var verifyArgon2 = async (password, hash) => {
|
|
|
29259
29228
|
if (process.versions.bun)
|
|
29260
29229
|
return Bun.password.verify(password, hash, "argon2id");
|
|
29261
29230
|
const [, algorithm, , paramString, nonceHex, storedHashHex] = hash.split("$");
|
|
29231
|
+
if (!algorithm || !paramString || !nonceHex || !storedHashHex)
|
|
29232
|
+
return false;
|
|
29262
29233
|
const kvPairs = paramString?.split(",");
|
|
29263
29234
|
const parsedParams = Object.fromEntries(kvPairs?.map((pair) => {
|
|
29264
29235
|
const [key, value] = pair.split("=");
|
|
@@ -29267,15 +29238,18 @@ var verifyArgon2 = async (password, hash) => {
|
|
|
29267
29238
|
const memory = parsedParams.m;
|
|
29268
29239
|
const passes = parsedParams.t;
|
|
29269
29240
|
const parallelism = parsedParams.p;
|
|
29270
|
-
|
|
29271
|
-
|
|
29241
|
+
if ([memory, passes, parallelism].some((value) => Number.isNaN(value)))
|
|
29242
|
+
return false;
|
|
29243
|
+
const argon2Async = promisify(getNodeArgon2());
|
|
29244
|
+
const newDerived = await argon2Async(algorithm, {
|
|
29245
|
+
nonce: Buffer.from(nonceHex, "base64"),
|
|
29272
29246
|
parallelism,
|
|
29273
|
-
tagLength:
|
|
29247
|
+
tagLength: params.tagLength,
|
|
29274
29248
|
memory,
|
|
29275
29249
|
passes,
|
|
29276
29250
|
message: password
|
|
29277
29251
|
});
|
|
29278
|
-
const isMatch =
|
|
29252
|
+
const isMatch = timingSafeEqual2(Buffer.from(newDerived), Buffer.from(storedHashHex, "base64"));
|
|
29279
29253
|
return isMatch;
|
|
29280
29254
|
};
|
|
29281
29255
|
var isArgon2Hash = (value) => typeof value === "string" && value.startsWith("$argon2");
|
|
@@ -29319,7 +29293,76 @@ var contextWithRoot = (context) => ({
|
|
|
29319
29293
|
});
|
|
29320
29294
|
var notEmpty = (value) => value !== null && value !== undefined;
|
|
29321
29295
|
|
|
29296
|
+
// ../wabe/src/authentication/OTP.ts
|
|
29297
|
+
var ONE_WINDOW = 1;
|
|
29298
|
+
var generateOtpSalt = () => randomBytes2(32).toString("hex");
|
|
29299
|
+
var getOrCreateOtpSalt = async (context, userId) => {
|
|
29300
|
+
const rootContext = contextWithRoot(context);
|
|
29301
|
+
const user = await context.wabe.controllers.database.getObject({
|
|
29302
|
+
className: "User",
|
|
29303
|
+
id: userId,
|
|
29304
|
+
context: rootContext,
|
|
29305
|
+
select: { otpSalt: true }
|
|
29306
|
+
});
|
|
29307
|
+
if (user?.otpSalt)
|
|
29308
|
+
return user.otpSalt;
|
|
29309
|
+
const salt = generateOtpSalt();
|
|
29310
|
+
await context.wabe.controllers.database.updateObject({
|
|
29311
|
+
className: "User",
|
|
29312
|
+
id: userId,
|
|
29313
|
+
context: rootContext,
|
|
29314
|
+
data: { otpSalt: salt },
|
|
29315
|
+
select: {}
|
|
29316
|
+
});
|
|
29317
|
+
return salt;
|
|
29318
|
+
};
|
|
29319
|
+
|
|
29320
|
+
class OTP {
|
|
29321
|
+
secret;
|
|
29322
|
+
internalTotp;
|
|
29323
|
+
constructor(rootKey) {
|
|
29324
|
+
this.secret = rootKey;
|
|
29325
|
+
this.internalTotp = import_otplib.totp.clone({
|
|
29326
|
+
window: [ONE_WINDOW, 0]
|
|
29327
|
+
});
|
|
29328
|
+
}
|
|
29329
|
+
deriveSecret(userId, salt) {
|
|
29330
|
+
const material = salt ? `${this.secret}:${userId}:${salt}` : `${this.secret}:${userId}`;
|
|
29331
|
+
const hash = createHash("sha256").update(material).digest();
|
|
29332
|
+
return base32Encode(hash, "RFC4648", { padding: false });
|
|
29333
|
+
}
|
|
29334
|
+
generate(userId, salt) {
|
|
29335
|
+
const secret = this.deriveSecret(userId, salt);
|
|
29336
|
+
return this.internalTotp.generate(secret);
|
|
29337
|
+
}
|
|
29338
|
+
verify(otp, userId, salt) {
|
|
29339
|
+
const secret = this.deriveSecret(userId, salt);
|
|
29340
|
+
return this.internalTotp.verify({ secret, token: otp });
|
|
29341
|
+
}
|
|
29342
|
+
authenticatorGenerate(userId, salt) {
|
|
29343
|
+
const secret = this.deriveSecret(userId, salt);
|
|
29344
|
+
return import_otplib.authenticator.generate(secret);
|
|
29345
|
+
}
|
|
29346
|
+
authenticatorVerify(otp, userId, salt) {
|
|
29347
|
+
const secret = this.deriveSecret(userId, salt);
|
|
29348
|
+
return import_otplib.authenticator.verify({
|
|
29349
|
+
secret,
|
|
29350
|
+
token: otp
|
|
29351
|
+
});
|
|
29352
|
+
}
|
|
29353
|
+
generateKeyuri({
|
|
29354
|
+
userId,
|
|
29355
|
+
emailOrUsername,
|
|
29356
|
+
applicationName,
|
|
29357
|
+
salt
|
|
29358
|
+
}) {
|
|
29359
|
+
const secret = this.deriveSecret(userId, salt);
|
|
29360
|
+
return import_otplib.authenticator.keyuri(emailOrUsername, applicationName, secret);
|
|
29361
|
+
}
|
|
29362
|
+
}
|
|
29322
29363
|
// ../wabe/src/authentication/Session.ts
|
|
29364
|
+
var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
|
|
29365
|
+
import crypto3 from "node:crypto";
|
|
29323
29366
|
var getJwtSecret = (context) => {
|
|
29324
29367
|
const secret = context.wabe.config.authentication?.session?.jwtSecret;
|
|
29325
29368
|
if (!secret)
|
|
@@ -29338,7 +29381,7 @@ var safeVerify = (token, secret, options = {}) => {
|
|
|
29338
29381
|
}
|
|
29339
29382
|
};
|
|
29340
29383
|
var getTokenSecret = (context) => context.wabe.config.authentication?.session?.tokenSecret ?? getJwtSecret(context);
|
|
29341
|
-
var getTokenEncryptionKey = (context) =>
|
|
29384
|
+
var getTokenEncryptionKey = (context) => crypto3.createHash("sha256").update(getTokenSecret(context)).digest();
|
|
29342
29385
|
var getJwtVerifyOptions = (context) => {
|
|
29343
29386
|
const opts = {};
|
|
29344
29387
|
const audience = context.wabe.config.authentication?.session?.jwtAudience;
|
|
@@ -29434,7 +29477,7 @@ class Session {
|
|
|
29434
29477
|
const currentSessionId = session.id;
|
|
29435
29478
|
const message = `${currentSessionId.length}!${currentSessionId}!${receivedRandomValue?.length}!${receivedRandomValue}`;
|
|
29436
29479
|
const csrfSecret = context.wabe.config.authentication?.session?.csrfSecret || getJwtSecret(context);
|
|
29437
|
-
const expectedHmac =
|
|
29480
|
+
const expectedHmac = crypto3.createHmac("sha256", csrfSecret).update(message).digest("hex");
|
|
29438
29481
|
const isHex = /^[0-9a-f]+$/i;
|
|
29439
29482
|
if (!isHex.test(receivedHmacHex) || receivedHmacHex.length !== expectedHmac.length)
|
|
29440
29483
|
return {
|
|
@@ -29443,7 +29486,7 @@ class Session {
|
|
|
29443
29486
|
accessToken: null,
|
|
29444
29487
|
refreshToken: null
|
|
29445
29488
|
};
|
|
29446
|
-
const isValid =
|
|
29489
|
+
const isValid = crypto3.timingSafeEqual(Buffer.from(receivedHmacHex, "hex"), Buffer.from(expectedHmac, "hex"));
|
|
29447
29490
|
if (!isValid)
|
|
29448
29491
|
return {
|
|
29449
29492
|
sessionId: null,
|
|
@@ -29502,7 +29545,7 @@ class Session {
|
|
|
29502
29545
|
}) : undefined;
|
|
29503
29546
|
const secretKey = getJwtSecret(context);
|
|
29504
29547
|
const signOptions = {
|
|
29505
|
-
jwtid:
|
|
29548
|
+
jwtid: crypto3.randomUUID(),
|
|
29506
29549
|
algorithm: JWT_ALGORITHM
|
|
29507
29550
|
};
|
|
29508
29551
|
const audience = context.wabe.config.authentication?.session?.jwtAudience;
|
|
@@ -29544,10 +29587,10 @@ class Session {
|
|
|
29544
29587
|
if (!res)
|
|
29545
29588
|
throw new Error("Session not created");
|
|
29546
29589
|
const sessionId = res.id;
|
|
29547
|
-
const randomValue =
|
|
29590
|
+
const randomValue = crypto3.randomBytes(16).toString("hex");
|
|
29548
29591
|
const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;
|
|
29549
29592
|
const csrfSecret = context.wabe.config.authentication?.session?.csrfSecret || secretKey;
|
|
29550
|
-
const hmac =
|
|
29593
|
+
const hmac = crypto3.createHmac("sha256", csrfSecret).update(message).digest("hex");
|
|
29551
29594
|
const csrfToken = `${hmac}.${randomValue}`;
|
|
29552
29595
|
return {
|
|
29553
29596
|
accessToken: this.accessToken,
|
|
@@ -29626,7 +29669,7 @@ class Session {
|
|
|
29626
29669
|
}) : undefined;
|
|
29627
29670
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
29628
29671
|
const signOptions = {
|
|
29629
|
-
jwtid:
|
|
29672
|
+
jwtid: crypto3.randomUUID(),
|
|
29630
29673
|
algorithm: JWT_ALGORITHM
|
|
29631
29674
|
};
|
|
29632
29675
|
const audience = context.wabe.config.authentication?.session?.jwtAudience;
|
|
@@ -29699,6 +29742,7 @@ var signOutResolver = async (_, __, context) => {
|
|
|
29699
29742
|
if (context.wabe.config.authentication?.session?.cookieSession) {
|
|
29700
29743
|
context.response?.deleteCookie("accessToken");
|
|
29701
29744
|
context.response?.deleteCookie("refreshToken");
|
|
29745
|
+
context.response?.deleteCookie("csrfToken");
|
|
29702
29746
|
}
|
|
29703
29747
|
return true;
|
|
29704
29748
|
};
|
|
@@ -29713,7 +29757,7 @@ var getSessionCookieSameSite = (config) => {
|
|
|
29713
29757
|
};
|
|
29714
29758
|
|
|
29715
29759
|
// ../wabe/src/authentication/security.ts
|
|
29716
|
-
import
|
|
29760
|
+
import crypto4 from "node:crypto";
|
|
29717
29761
|
var DEFAULT_SIGN_IN_RATE_LIMIT = {
|
|
29718
29762
|
maxAttempts: 10,
|
|
29719
29763
|
windowMs: 10 * 60 * 1000,
|
|
@@ -29729,6 +29773,16 @@ var DEFAULT_VERIFY_CHALLENGE_RATE_LIMIT = {
|
|
|
29729
29773
|
windowMs: 10 * 60 * 1000,
|
|
29730
29774
|
blockDurationMs: 15 * 60 * 1000
|
|
29731
29775
|
};
|
|
29776
|
+
var DEFAULT_SEND_OTP_CODE_RATE_LIMIT = {
|
|
29777
|
+
maxAttempts: 5,
|
|
29778
|
+
windowMs: 10 * 60 * 1000,
|
|
29779
|
+
blockDurationMs: 30 * 60 * 1000
|
|
29780
|
+
};
|
|
29781
|
+
var DEFAULT_RESET_PASSWORD_RATE_LIMIT = {
|
|
29782
|
+
maxAttempts: 10,
|
|
29783
|
+
windowMs: 10 * 60 * 1000,
|
|
29784
|
+
blockDurationMs: 15 * 60 * 1000
|
|
29785
|
+
};
|
|
29732
29786
|
var DEFAULT_MFA_CHALLENGE_TTL_MS = 5 * 60 * 1000;
|
|
29733
29787
|
var rateLimitStorage = new Map;
|
|
29734
29788
|
var getRateLimitOptions = (context, scope) => {
|
|
@@ -29737,17 +29791,21 @@ var getRateLimitOptions = (context, scope) => {
|
|
|
29737
29791
|
const scopeConfigMap = {
|
|
29738
29792
|
signIn: securityConfig?.signInRateLimit,
|
|
29739
29793
|
signUp: securityConfig?.signUpRateLimit,
|
|
29740
|
-
verifyChallenge: securityConfig?.verifyChallengeRateLimit
|
|
29794
|
+
verifyChallenge: securityConfig?.verifyChallengeRateLimit,
|
|
29795
|
+
sendOtpCode: securityConfig?.sendOtpCodeRateLimit,
|
|
29796
|
+
resetPassword: securityConfig?.resetPasswordRateLimit
|
|
29741
29797
|
};
|
|
29742
29798
|
const defaultsMap = {
|
|
29743
29799
|
signIn: DEFAULT_SIGN_IN_RATE_LIMIT,
|
|
29744
29800
|
signUp: DEFAULT_SIGN_UP_RATE_LIMIT,
|
|
29745
|
-
verifyChallenge: DEFAULT_VERIFY_CHALLENGE_RATE_LIMIT
|
|
29801
|
+
verifyChallenge: DEFAULT_VERIFY_CHALLENGE_RATE_LIMIT,
|
|
29802
|
+
sendOtpCode: DEFAULT_SEND_OTP_CODE_RATE_LIMIT,
|
|
29803
|
+
resetPassword: DEFAULT_RESET_PASSWORD_RATE_LIMIT
|
|
29746
29804
|
};
|
|
29747
29805
|
const scopeConfig = scopeConfigMap[scope];
|
|
29748
29806
|
const defaults = defaultsMap[scope];
|
|
29749
29807
|
return {
|
|
29750
|
-
enabled: scopeConfig?.enabled ??
|
|
29808
|
+
enabled: scopeConfig?.enabled ?? true,
|
|
29751
29809
|
maxAttempts: scopeConfig?.maxAttempts ?? defaults.maxAttempts,
|
|
29752
29810
|
windowMs: scopeConfig?.windowMs ?? defaults.windowMs,
|
|
29753
29811
|
blockDurationMs: scopeConfig?.blockDurationMs ?? defaults.blockDurationMs
|
|
@@ -29826,19 +29884,15 @@ var pruneExpiredChallenges = (challenges) => {
|
|
|
29826
29884
|
return challenges.filter((challenge) => challenge.expiresAt > now);
|
|
29827
29885
|
};
|
|
29828
29886
|
var getUserPendingChallenges = async (context, userId) => {
|
|
29829
|
-
|
|
29830
|
-
|
|
29831
|
-
|
|
29832
|
-
|
|
29833
|
-
|
|
29834
|
-
|
|
29835
|
-
|
|
29836
|
-
|
|
29837
|
-
|
|
29838
|
-
return parsePendingChallenges(user?.pendingChallenges);
|
|
29839
|
-
} catch {
|
|
29840
|
-
return null;
|
|
29841
|
-
}
|
|
29887
|
+
const user = await getDatabaseController(context).getObject({
|
|
29888
|
+
className: "User",
|
|
29889
|
+
id: userId,
|
|
29890
|
+
context: contextWithRoot(context),
|
|
29891
|
+
select: {
|
|
29892
|
+
pendingChallenges: true
|
|
29893
|
+
}
|
|
29894
|
+
});
|
|
29895
|
+
return parsePendingChallenges(user?.pendingChallenges);
|
|
29842
29896
|
};
|
|
29843
29897
|
var saveUserPendingChallenges = async (context, userId, challenges) => getDatabaseController(context).updateObject({
|
|
29844
29898
|
className: "User",
|
|
@@ -29854,9 +29908,9 @@ var saveUserPendingChallenges = async (context, userId, challenges) => getDataba
|
|
|
29854
29908
|
select: {}
|
|
29855
29909
|
});
|
|
29856
29910
|
var createMfaChallenge = async (context, { userId, provider }) => {
|
|
29857
|
-
const token =
|
|
29911
|
+
const token = crypto4.randomUUID();
|
|
29858
29912
|
const expiresAt = Date.now() + getMfaChallengeTTL(context);
|
|
29859
|
-
const currentChallenges = await getUserPendingChallenges(context, userId)
|
|
29913
|
+
const currentChallenges = await getUserPendingChallenges(context, userId);
|
|
29860
29914
|
const nextChallenges = [
|
|
29861
29915
|
...pruneExpiredChallenges(currentChallenges),
|
|
29862
29916
|
{
|
|
@@ -29868,24 +29922,35 @@ var createMfaChallenge = async (context, { userId, provider }) => {
|
|
|
29868
29922
|
await saveUserPendingChallenges(context, userId, nextChallenges);
|
|
29869
29923
|
return token;
|
|
29870
29924
|
};
|
|
29925
|
+
var mfaLocks = new Map;
|
|
29871
29926
|
var consumeMfaChallenge = async (context, {
|
|
29872
29927
|
challengeToken,
|
|
29873
29928
|
userId,
|
|
29874
29929
|
provider
|
|
29875
29930
|
}) => {
|
|
29876
|
-
const
|
|
29877
|
-
|
|
29878
|
-
|
|
29879
|
-
|
|
29880
|
-
|
|
29881
|
-
|
|
29882
|
-
|
|
29883
|
-
|
|
29884
|
-
|
|
29885
|
-
|
|
29886
|
-
|
|
29931
|
+
const lockKey = `mfa:${userId}`;
|
|
29932
|
+
const previous = mfaLocks.get(lockKey) ?? Promise.resolve(false);
|
|
29933
|
+
const current = previous.catch(() => false).then(async () => {
|
|
29934
|
+
const currentChallenges = await getUserPendingChallenges(context, userId);
|
|
29935
|
+
if (!currentChallenges)
|
|
29936
|
+
return false;
|
|
29937
|
+
const activeChallenges = pruneExpiredChallenges(currentChallenges);
|
|
29938
|
+
const normalizedProvider = provider.toLowerCase();
|
|
29939
|
+
const isValid = activeChallenges.some((challenge) => challenge.token === challengeToken && challenge.provider === normalizedProvider);
|
|
29940
|
+
const remainingChallenges = activeChallenges.filter((challenge) => !(challenge.token === challengeToken && challenge.provider === normalizedProvider));
|
|
29941
|
+
if (remainingChallenges.length !== currentChallenges.length || isValid) {
|
|
29942
|
+
await saveUserPendingChallenges(context, userId, remainingChallenges);
|
|
29943
|
+
}
|
|
29944
|
+
return isValid;
|
|
29945
|
+
});
|
|
29946
|
+
mfaLocks.set(lockKey, current);
|
|
29947
|
+
try {
|
|
29948
|
+
return await current;
|
|
29949
|
+
} finally {
|
|
29950
|
+
if (mfaLocks.get(lockKey) === current)
|
|
29951
|
+
mfaLocks.delete(lockKey);
|
|
29952
|
+
}
|
|
29887
29953
|
};
|
|
29888
|
-
var shouldRequireMfaChallenge = (context) => context.wabe.config?.authentication?.security?.requireMfaChallengeInProduction ?? !!context.wabe.config?.isProduction;
|
|
29889
29954
|
|
|
29890
29955
|
// ../wabe/src/authentication/utils.ts
|
|
29891
29956
|
var getAuthenticationMethod = (listOfMethods, context) => {
|
|
@@ -29918,18 +29983,16 @@ var verifyChallengeResolver = async (_, {
|
|
|
29918
29983
|
});
|
|
29919
29984
|
if (!result?.userId)
|
|
29920
29985
|
throw new Error("Invalid challenge");
|
|
29921
|
-
|
|
29922
|
-
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
|
|
29930
|
-
|
|
29931
|
-
throw new Error("Invalid challenge");
|
|
29932
|
-
}
|
|
29986
|
+
const challengeToken = input.challengeToken;
|
|
29987
|
+
if (!challengeToken)
|
|
29988
|
+
throw new Error("Invalid challenge");
|
|
29989
|
+
const isValidChallenge = await consumeMfaChallenge(context, {
|
|
29990
|
+
challengeToken,
|
|
29991
|
+
userId: result.userId,
|
|
29992
|
+
provider: name
|
|
29993
|
+
});
|
|
29994
|
+
if (!isValidChallenge)
|
|
29995
|
+
throw new Error("Invalid challenge");
|
|
29933
29996
|
const session = new Session;
|
|
29934
29997
|
const { accessToken, refreshToken } = await session.create(result.userId, context);
|
|
29935
29998
|
if (context.wabe.config.authentication?.session?.cookieSession) {
|
|
@@ -29966,14 +30029,26 @@ var meResolver = (_, __, context) => {
|
|
|
29966
30029
|
// ../wabe/src/schema/resolvers/resetPassword.ts
|
|
29967
30030
|
var DUMMY_USER_ID = "00000000-0000-0000-0000-000000000000";
|
|
29968
30031
|
var resetPasswordResolver = async (_, { input: { email, phone, password, otp } }, context) => {
|
|
29969
|
-
|
|
30032
|
+
const normalizedEmail = email?.trim().toLowerCase();
|
|
30033
|
+
const normalizedPhone = phone?.trim();
|
|
30034
|
+
if (!normalizedEmail && !normalizedPhone)
|
|
30035
|
+
throw new Error("Email or phone is required");
|
|
30036
|
+
if (normalizedEmail && normalizedPhone)
|
|
29970
30037
|
throw new Error("Email or phone is required");
|
|
30038
|
+
const identifier = normalizedEmail || normalizedPhone;
|
|
30039
|
+
if (!identifier)
|
|
30040
|
+
throw new Error("Email or phone is required");
|
|
30041
|
+
const rateLimitKey = `resetPassword:${identifier}`;
|
|
30042
|
+
if (isRateLimited(context, "resetPassword", rateLimitKey))
|
|
30043
|
+
throw new Error("Too many attempts. Please try again later.");
|
|
29971
30044
|
const users = await context.wabe.controllers.database.getObjects({
|
|
29972
30045
|
className: "User",
|
|
29973
30046
|
where: {
|
|
29974
|
-
...
|
|
29975
|
-
...
|
|
29976
|
-
authentication: {
|
|
30047
|
+
...normalizedEmail && { email: { equalTo: normalizedEmail } },
|
|
30048
|
+
...normalizedPhone && {
|
|
30049
|
+
authentication: {
|
|
30050
|
+
phonePassword: { phone: { equalTo: normalizedPhone } }
|
|
30051
|
+
}
|
|
29977
30052
|
}
|
|
29978
30053
|
},
|
|
29979
30054
|
select: { id: true, authentication: true },
|
|
@@ -29983,21 +30058,24 @@ var resetPasswordResolver = async (_, { input: { email, phone, password, otp } }
|
|
|
29983
30058
|
const realUser = users.length > 0 ? users[0] : null;
|
|
29984
30059
|
const userId = realUser?.id ?? DUMMY_USER_ID;
|
|
29985
30060
|
const otpClass = new OTP(context.wabe.config.rootKey);
|
|
29986
|
-
const
|
|
30061
|
+
const salt = realUser ? await getOrCreateOtpSalt(context, userId) : undefined;
|
|
30062
|
+
const isOtpValid = otpClass.verify(otp, userId, salt);
|
|
29987
30063
|
if (realUser) {
|
|
29988
|
-
if (!isOtpValid)
|
|
30064
|
+
if (!isOtpValid) {
|
|
30065
|
+
registerRateLimitFailure(context, "resetPassword", rateLimitKey);
|
|
29989
30066
|
throw new Error("Invalid OTP code");
|
|
29990
|
-
|
|
30067
|
+
}
|
|
30068
|
+
const providerKey = normalizedPhone ? "phonePassword" : "emailPassword";
|
|
29991
30069
|
await context.wabe.controllers.database.updateObject({
|
|
29992
30070
|
className: "User",
|
|
29993
30071
|
id: realUser.id,
|
|
29994
30072
|
data: {
|
|
29995
30073
|
authentication: {
|
|
29996
30074
|
[providerKey]: {
|
|
29997
|
-
...
|
|
30075
|
+
...normalizedPhone && {
|
|
29998
30076
|
phone: realUser.authentication?.phonePassword?.phone
|
|
29999
30077
|
},
|
|
30000
|
-
...
|
|
30078
|
+
...normalizedEmail && { email: normalizedEmail },
|
|
30001
30079
|
password
|
|
30002
30080
|
}
|
|
30003
30081
|
}
|
|
@@ -30013,6 +30091,9 @@ var resetPasswordResolver = async (_, { input: { email, phone, password, otp } }
|
|
|
30013
30091
|
select: {},
|
|
30014
30092
|
context: contextWithRoot(context)
|
|
30015
30093
|
});
|
|
30094
|
+
clearRateLimit(context, "resetPassword", rateLimitKey);
|
|
30095
|
+
} else {
|
|
30096
|
+
registerRateLimitFailure(context, "resetPassword", rateLimitKey);
|
|
30016
30097
|
}
|
|
30017
30098
|
return true;
|
|
30018
30099
|
};
|
|
@@ -30144,11 +30225,17 @@ var sendOtpCodeResolver = async (_, { input }, context) => {
|
|
|
30144
30225
|
const emailController = context.wabe.controllers.email;
|
|
30145
30226
|
if (!emailController)
|
|
30146
30227
|
throw new Error("Email adapter not defined");
|
|
30228
|
+
const normalizedEmail = input.email.trim().toLowerCase();
|
|
30229
|
+
if (!normalizedEmail)
|
|
30230
|
+
return true;
|
|
30231
|
+
const rateLimitKey = `sendOtpCode:${normalizedEmail}`;
|
|
30232
|
+
if (isRateLimited(context, "sendOtpCode", rateLimitKey))
|
|
30233
|
+
return true;
|
|
30147
30234
|
const user = await context.wabe.controllers.database.getObjects({
|
|
30148
30235
|
className: "User",
|
|
30149
30236
|
where: {
|
|
30150
30237
|
email: {
|
|
30151
|
-
equalTo:
|
|
30238
|
+
equalTo: normalizedEmail
|
|
30152
30239
|
}
|
|
30153
30240
|
},
|
|
30154
30241
|
select: { id: true },
|
|
@@ -30161,15 +30248,17 @@ var sendOtpCodeResolver = async (_, { input }, context) => {
|
|
|
30161
30248
|
if (!userId)
|
|
30162
30249
|
return false;
|
|
30163
30250
|
const otpClass = new OTP(context.wabe.config.rootKey);
|
|
30164
|
-
const
|
|
30251
|
+
const salt = await getOrCreateOtpSalt(context, userId);
|
|
30252
|
+
const otp = otpClass.generate(userId, salt);
|
|
30165
30253
|
const mainEmail = context.wabe.config.email?.mainEmail || "noreply@wabe.com";
|
|
30166
30254
|
const template = context.wabe.config.email?.htmlTemplates?.sendOTPCode;
|
|
30167
30255
|
await emailController.send({
|
|
30168
30256
|
from: mainEmail,
|
|
30169
|
-
to: [
|
|
30257
|
+
to: [normalizedEmail],
|
|
30170
30258
|
subject: template?.subject || "Your OTP code",
|
|
30171
30259
|
html: template?.fn ? await template.fn({ otp }) : sendOtpCodeTemplate(otp)
|
|
30172
30260
|
});
|
|
30261
|
+
registerRateLimitFailure(context, "sendOtpCode", rateLimitKey);
|
|
30173
30262
|
return true;
|
|
30174
30263
|
};
|
|
30175
30264
|
|
|
@@ -30307,12 +30396,16 @@ var signInWithResolver = async (_, {
|
|
|
30307
30396
|
};
|
|
30308
30397
|
}
|
|
30309
30398
|
if (name === "emailPasswordSRP") {
|
|
30399
|
+
const challengeToken = await createMfaChallenge(context, {
|
|
30400
|
+
userId,
|
|
30401
|
+
provider: "emailPasswordSRPChallenge"
|
|
30402
|
+
});
|
|
30310
30403
|
return {
|
|
30311
30404
|
accessToken: null,
|
|
30312
30405
|
refreshToken: null,
|
|
30313
30406
|
user,
|
|
30314
30407
|
srp: srp || null,
|
|
30315
|
-
challengeToken
|
|
30408
|
+
challengeToken
|
|
30316
30409
|
};
|
|
30317
30410
|
}
|
|
30318
30411
|
const session = new Session;
|
|
@@ -30731,6 +30824,13 @@ class Schema {
|
|
|
30731
30824
|
}
|
|
30732
30825
|
}
|
|
30733
30826
|
},
|
|
30827
|
+
otpSalt: {
|
|
30828
|
+
type: "String",
|
|
30829
|
+
protected: {
|
|
30830
|
+
authorizedRoles: ["rootOnly"],
|
|
30831
|
+
protectedOperations: ["create", "read", "update"]
|
|
30832
|
+
}
|
|
30833
|
+
},
|
|
30734
30834
|
pendingChallenges: {
|
|
30735
30835
|
type: "Array",
|
|
30736
30836
|
typeValue: "Object",
|
|
@@ -30918,7 +31018,7 @@ var getFile = async (hookObject) => {
|
|
|
30918
31018
|
const schema = hookObject.context.wabe.config.schema?.classes?.find((currentClass) => currentClass.name === hookObject.className);
|
|
30919
31019
|
if (!schema)
|
|
30920
31020
|
return;
|
|
30921
|
-
const urlCacheInSeconds = hookObject.context.wabe.config.file?.urlCacheInSeconds
|
|
31021
|
+
const urlCacheInSeconds = hookObject.context.wabe.config.file?.urlCacheInSeconds ?? 3600 * 24;
|
|
30922
31022
|
await Promise.all(Object.entries(schema.fields).filter(([_, value]) => value.type === "File").map(async ([fieldName]) => {
|
|
30923
31023
|
const fileInfo = hookObject.object?.[fieldName];
|
|
30924
31024
|
if (!fileInfo)
|
|
@@ -30932,17 +31032,21 @@ var getFile = async (hookObject) => {
|
|
|
30932
31032
|
if (!hookObject.context.wabe.controllers.file)
|
|
30933
31033
|
throw new Error("No file adapter found");
|
|
30934
31034
|
const fileUrlFromBucket = await hookObject.context.wabe.controllers.file?.readFile(fileName);
|
|
30935
|
-
|
|
31035
|
+
if (!fileUrlFromBucket)
|
|
31036
|
+
return;
|
|
31037
|
+
const newUrl = fileUrlFromBucket;
|
|
30936
31038
|
const newUrlGeneratedAt = new Date;
|
|
30937
31039
|
hookObject.object[fieldName] = {
|
|
30938
31040
|
...fileInfo,
|
|
30939
31041
|
urlGeneratedAt: newUrlGeneratedAt,
|
|
30940
31042
|
url: newUrl
|
|
30941
31043
|
};
|
|
31044
|
+
if (!hookObject.object?.id)
|
|
31045
|
+
return;
|
|
30942
31046
|
return hookObject.context.wabe.controllers.database.updateObject({
|
|
30943
31047
|
className: hookObject.className,
|
|
30944
31048
|
context: hookObject.context,
|
|
30945
|
-
id: hookObject.object
|
|
31049
|
+
id: hookObject.object.id,
|
|
30946
31050
|
data: {
|
|
30947
31051
|
[fieldName]: {
|
|
30948
31052
|
...fileInfo,
|
|
@@ -30957,7 +31061,7 @@ var getFile = async (hookObject) => {
|
|
|
30957
31061
|
var defaultAfterReadFile = (hookObject) => getFile(hookObject);
|
|
30958
31062
|
|
|
30959
31063
|
// ../wabe/src/file/security.ts
|
|
30960
|
-
import
|
|
31064
|
+
import crypto5 from "node:crypto";
|
|
30961
31065
|
import path from "node:path";
|
|
30962
31066
|
var DEFAULT_MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
|
|
30963
31067
|
var DEFAULT_ALLOWED_MIME_TYPES = [
|
|
@@ -31018,7 +31122,7 @@ var detectMimeTypeFromContent = async (file) => {
|
|
|
31018
31122
|
};
|
|
31019
31123
|
var getUploadSecurityConfig = (context) => {
|
|
31020
31124
|
const security = context.wabe.config.file?.security;
|
|
31021
|
-
const enabled = security?.enabled ??
|
|
31125
|
+
const enabled = security?.enabled ?? true;
|
|
31022
31126
|
const maxFileSizeBytes = security?.maxFileSizeBytes ?? DEFAULT_MAX_FILE_SIZE_BYTES;
|
|
31023
31127
|
const allowedMimeTypes = (security?.allowedMimeTypes || DEFAULT_ALLOWED_MIME_TYPES).map(normalizeMimeType);
|
|
31024
31128
|
const allowedExtensions = (security?.allowedExtensions || DEFAULT_ALLOWED_EXTENSIONS).map((value) => value.trim().toLowerCase());
|
|
@@ -31032,7 +31136,7 @@ var getUploadSecurityConfig = (context) => {
|
|
|
31032
31136
|
};
|
|
31033
31137
|
};
|
|
31034
31138
|
var createRandomizedFile = async (file, extension) => {
|
|
31035
|
-
const uniqueName = `${
|
|
31139
|
+
const uniqueName = `${crypto5.randomUUID()}.${extension}`;
|
|
31036
31140
|
const content = await file.arrayBuffer();
|
|
31037
31141
|
return new File([content], uniqueName, {
|
|
31038
31142
|
type: file.type,
|
|
@@ -31052,6 +31156,9 @@ var secureUploadedFile = async (file, context) => {
|
|
|
31052
31156
|
if (!mimeType || !allowedMimeTypes.includes(mimeType))
|
|
31053
31157
|
throw new Error("File MIME type is not allowed");
|
|
31054
31158
|
const detectedMimeType = await detectMimeTypeFromContent(file);
|
|
31159
|
+
const mimeTypeHasKnownSignature = MIME_SIGNATURES.some((signature) => signature.mimeType === mimeType);
|
|
31160
|
+
if (mimeTypeHasKnownSignature && detectedMimeType !== mimeType)
|
|
31161
|
+
throw new Error("File content does not match MIME type");
|
|
31055
31162
|
if (detectedMimeType && detectedMimeType !== mimeType)
|
|
31056
31163
|
throw new Error("File content does not match MIME type");
|
|
31057
31164
|
if (detectedMimeType && !allowedMimeTypes.includes(detectedMimeType))
|
|
@@ -31065,6 +31172,19 @@ var secureUploadedFile = async (file, context) => {
|
|
|
31065
31172
|
};
|
|
31066
31173
|
|
|
31067
31174
|
// ../wabe/src/file/hookUploadFile.ts
|
|
31175
|
+
var ALLOWED_URL_PROTOCOLS = ["https:"];
|
|
31176
|
+
var validateFileUrl = (url) => {
|
|
31177
|
+
let parsed;
|
|
31178
|
+
try {
|
|
31179
|
+
parsed = new URL(url);
|
|
31180
|
+
} catch {
|
|
31181
|
+
throw new Error("Invalid file URL");
|
|
31182
|
+
}
|
|
31183
|
+
if (!ALLOWED_URL_PROTOCOLS.includes(parsed.protocol))
|
|
31184
|
+
throw new Error("File URL must use HTTPS");
|
|
31185
|
+
if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]")
|
|
31186
|
+
throw new Error("File URL must not point to localhost");
|
|
31187
|
+
};
|
|
31068
31188
|
var handleFile = async (hookObject) => {
|
|
31069
31189
|
const newData = hookObject.getNewData();
|
|
31070
31190
|
const schema = hookObject.context.wabe.config.schema?.classes?.find((currentClass) => currentClass.name === hookObject.className);
|
|
@@ -31077,6 +31197,7 @@ var handleFile = async (hookObject) => {
|
|
|
31077
31197
|
if (!file && !url)
|
|
31078
31198
|
return;
|
|
31079
31199
|
if (url) {
|
|
31200
|
+
validateFileUrl(url);
|
|
31080
31201
|
hookObject.upsertNewData(keyName, {
|
|
31081
31202
|
url,
|
|
31082
31203
|
isPresignedUrl: false
|
|
@@ -31129,7 +31250,9 @@ class HookObject {
|
|
|
31129
31250
|
return this.context.user;
|
|
31130
31251
|
}
|
|
31131
31252
|
isFieldUpdated(field) {
|
|
31132
|
-
|
|
31253
|
+
if (!this.newData)
|
|
31254
|
+
return false;
|
|
31255
|
+
return Object.prototype.hasOwnProperty.call(this.newData, field);
|
|
31133
31256
|
}
|
|
31134
31257
|
upsertNewData(field, value) {
|
|
31135
31258
|
if (!this.newData)
|
|
@@ -31930,6 +32053,7 @@ var getDefaultHooks = () => [
|
|
|
31930
32053
|
];
|
|
31931
32054
|
|
|
31932
32055
|
// ../wabe/src/database/DatabaseController.ts
|
|
32056
|
+
var DEFAULT_MAX_WHERE_RECURSION_DEPTH = 10;
|
|
31933
32057
|
var scalarWhereOperators = new Set([
|
|
31934
32058
|
"equalTo",
|
|
31935
32059
|
"notEqualTo",
|
|
@@ -32158,7 +32282,10 @@ class DatabaseController {
|
|
|
32158
32282
|
return [];
|
|
32159
32283
|
return relationValue.map((value) => this._extractPointerId(value)).filter((id) => typeof id === "string");
|
|
32160
32284
|
}
|
|
32161
|
-
async _getWhereObjectWithPointerOrRelation(className, where, context) {
|
|
32285
|
+
async _getWhereObjectWithPointerOrRelation(className, where, context, depth = 0) {
|
|
32286
|
+
const maxDepth = context.wabe.config.security?.maxWhereRecursionDepth ?? DEFAULT_MAX_WHERE_RECURSION_DEPTH;
|
|
32287
|
+
if (depth > maxDepth)
|
|
32288
|
+
throw new Error(`Query recursion depth exceeded maximum (${maxDepth}). Reduce nested where conditions.`);
|
|
32162
32289
|
const whereKeys = Object.keys(where);
|
|
32163
32290
|
const realClass = this._getClass(className, context);
|
|
32164
32291
|
const newWhereObject = await whereKeys.reduce(async (acc, whereKey) => {
|
|
@@ -32166,7 +32293,7 @@ class DatabaseController {
|
|
|
32166
32293
|
const typedWhereKey = whereKey;
|
|
32167
32294
|
const field = realClass?.fields[typedWhereKey];
|
|
32168
32295
|
if (typedWhereKey === "AND" || typedWhereKey === "OR") {
|
|
32169
|
-
const newWhere = await Promise.all(where[typedWhereKey].map((whereObject) => this._getWhereObjectWithPointerOrRelation(className, whereObject, context)));
|
|
32296
|
+
const newWhere = await Promise.all(where[typedWhereKey].map((whereObject) => this._getWhereObjectWithPointerOrRelation(className, whereObject, context, depth + 1)));
|
|
32170
32297
|
return {
|
|
32171
32298
|
...currentAcc,
|
|
32172
32299
|
[typedWhereKey]: newWhere
|
|
@@ -32203,7 +32330,8 @@ class DatabaseController {
|
|
|
32203
32330
|
className: fieldTargetClass,
|
|
32204
32331
|
select: { id: true },
|
|
32205
32332
|
where: defaultWhere,
|
|
32206
|
-
context
|
|
32333
|
+
context,
|
|
32334
|
+
_whereRecursionDepth: depth + 1
|
|
32207
32335
|
});
|
|
32208
32336
|
const relationWhere = objects.length > 0 ? objects.map((object) => object?.id).filter(notEmpty) : [];
|
|
32209
32337
|
const neverMatchWhere = {
|
|
@@ -32544,7 +32672,8 @@ class DatabaseController {
|
|
|
32544
32672
|
operationType: "beforeRead" /* BeforeRead */,
|
|
32545
32673
|
id
|
|
32546
32674
|
});
|
|
32547
|
-
const
|
|
32675
|
+
const whereWithPointer = await this._getWhereObjectWithPointerOrRelation(className, where || {}, context);
|
|
32676
|
+
const whereWithACLCondition = this._buildWhereWithACL(whereWithPointer || {}, context, "read");
|
|
32548
32677
|
const selectWithPointersAndRelationsToGetId = this._buildSelectWithPointers({
|
|
32549
32678
|
adapterSelect,
|
|
32550
32679
|
pointers
|
|
@@ -32586,7 +32715,8 @@ class DatabaseController {
|
|
|
32586
32715
|
_skipHooks,
|
|
32587
32716
|
first,
|
|
32588
32717
|
offset,
|
|
32589
|
-
order
|
|
32718
|
+
order,
|
|
32719
|
+
_whereRecursionDepth
|
|
32590
32720
|
}) {
|
|
32591
32721
|
const { pointers, selectWithoutPointers } = this._getSelectMinusPointersAndRelations({
|
|
32592
32722
|
className,
|
|
@@ -32598,7 +32728,7 @@ class DatabaseController {
|
|
|
32598
32728
|
context,
|
|
32599
32729
|
selectWithoutPointers
|
|
32600
32730
|
});
|
|
32601
|
-
const whereWithPointer = await this._getWhereObjectWithPointerOrRelation(className, where || {}, context);
|
|
32731
|
+
const whereWithPointer = await this._getWhereObjectWithPointerOrRelation(className, where || {}, context, _whereRecursionDepth ?? 0);
|
|
32602
32732
|
const whereWithACLCondition = this._buildWhereWithACL(whereWithPointer || {}, context, "read");
|
|
32603
32733
|
const selectWithPointersAndRelationsToGetId = this._buildSelectWithPointers({
|
|
32604
32734
|
adapterSelect,
|
|
@@ -32728,10 +32858,11 @@ class DatabaseController {
|
|
|
32728
32858
|
})));
|
|
32729
32859
|
if (this._isEmptySelect(select))
|
|
32730
32860
|
return [];
|
|
32861
|
+
const selectWithoutPrivateFields = select ? selectFieldsWithoutPrivateFields(select) : undefined;
|
|
32731
32862
|
return this.getObjects({
|
|
32732
32863
|
className,
|
|
32733
32864
|
context: contextWithRoot(context),
|
|
32734
|
-
select,
|
|
32865
|
+
select: selectWithoutPrivateFields,
|
|
32735
32866
|
where: { id: { in: ids } },
|
|
32736
32867
|
first,
|
|
32737
32868
|
offset,
|
|
@@ -36023,11 +36154,20 @@ var add = async ({
|
|
|
36023
36154
|
context
|
|
36024
36155
|
});
|
|
36025
36156
|
const idsToAdd = add2.map(getPointerId).filter(notEmpty);
|
|
36026
|
-
if (typeOfExecution === "create")
|
|
36157
|
+
if (typeOfExecution === "create") {
|
|
36158
|
+
const linkedObjects = await Promise.all(idsToAdd.map((id2) => context.wabe.controllers.database.getObject({
|
|
36159
|
+
className: targetClass,
|
|
36160
|
+
id: id2,
|
|
36161
|
+
select: { id: true },
|
|
36162
|
+
context
|
|
36163
|
+
})));
|
|
36164
|
+
if (linkedObjects.some((object) => !object))
|
|
36165
|
+
throw new Error("Object not found");
|
|
36027
36166
|
return idsToAdd.map((id2) => toPointerObject2({
|
|
36028
36167
|
className: targetClass,
|
|
36029
36168
|
id: id2
|
|
36030
36169
|
}));
|
|
36170
|
+
}
|
|
36031
36171
|
if (typeOfExecution === "update" && id) {
|
|
36032
36172
|
const currentValue = await context.wabe.controllers.database.getObject({
|
|
36033
36173
|
className,
|
|
@@ -36198,6 +36338,14 @@ var executeRelationOnFields = ({
|
|
|
36198
36338
|
const targetClass = getTargetClassFromField2(fieldName);
|
|
36199
36339
|
if (!linkedId)
|
|
36200
36340
|
throw new Error(`Invalid link value for ${className}.${fieldName}`);
|
|
36341
|
+
const linkedObject = await context.wabe.controllers.database.getObject({
|
|
36342
|
+
className: targetClass,
|
|
36343
|
+
id: linkedId,
|
|
36344
|
+
select: { id: true },
|
|
36345
|
+
context: contextWithoutGraphQLCall(context)
|
|
36346
|
+
});
|
|
36347
|
+
if (!linkedObject)
|
|
36348
|
+
throw new Error(`Object not found`);
|
|
36201
36349
|
newAcc[fieldName] = {
|
|
36202
36350
|
class: targetClass,
|
|
36203
36351
|
id: linkedId,
|
|
@@ -38327,6 +38475,7 @@ class GitHub {
|
|
|
38327
38475
|
}
|
|
38328
38476
|
|
|
38329
38477
|
// ../wabe/src/server/routes/authHandler.ts
|
|
38478
|
+
var validProviders = new Set(Object.values(ProviderEnum));
|
|
38330
38479
|
var oauthHandlerCallback = async (context, wabeContext) => {
|
|
38331
38480
|
try {
|
|
38332
38481
|
const state = decodeURIComponent(context.query.state || "");
|
|
@@ -38336,6 +38485,8 @@ var oauthHandlerCallback = async (context, wabeContext) => {
|
|
|
38336
38485
|
throw new Error("Authentication failed");
|
|
38337
38486
|
const codeVerifier = context.getCookie("code_verifier");
|
|
38338
38487
|
const provider = context.getCookie("provider");
|
|
38488
|
+
if (!provider || !validProviders.has(provider))
|
|
38489
|
+
throw new Error("Authentication failed, invalid provider");
|
|
38339
38490
|
const { signInWith } = await getGraphqlClient(wabeContext.wabe.config.port).request(gql`
|
|
38340
38491
|
mutation signInWith(
|
|
38341
38492
|
$authorizationCode: String!
|
|
@@ -38372,7 +38523,7 @@ var oauthHandlerCallback = async (context, wabeContext) => {
|
|
|
38372
38523
|
context.res.setCookie("refreshToken", refreshToken, {
|
|
38373
38524
|
httpOnly: isCookieSession,
|
|
38374
38525
|
path: "/",
|
|
38375
|
-
maxAge: (wabeContext.wabe.config.authentication?.session?.
|
|
38526
|
+
maxAge: (wabeContext.wabe.config.authentication?.session?.refreshTokenExpiresInMs || 1000 * 60 * 60 * 24 * 7) / 1000,
|
|
38376
38527
|
sameSite,
|
|
38377
38528
|
secure: true
|
|
38378
38529
|
});
|
|
@@ -38441,6 +38592,7 @@ var authHandler = (context, wabeContext, provider) => {
|
|
|
38441
38592
|
};
|
|
38442
38593
|
|
|
38443
38594
|
// ../wabe/src/server/routes/index.ts
|
|
38595
|
+
var validProviders2 = new Set(Object.values(ProviderEnum));
|
|
38444
38596
|
var defaultRoutes = ({
|
|
38445
38597
|
devDirectory,
|
|
38446
38598
|
enableBucketRoute
|
|
@@ -38451,8 +38603,8 @@ var defaultRoutes = ({
|
|
|
38451
38603
|
path: "/auth/oauth",
|
|
38452
38604
|
handler: (context) => {
|
|
38453
38605
|
const provider = context.query.provider;
|
|
38454
|
-
if (!provider)
|
|
38455
|
-
throw new Error("Authentication failed, provider
|
|
38606
|
+
if (!provider || !validProviders2.has(provider))
|
|
38607
|
+
throw new Error("Authentication failed, invalid provider");
|
|
38456
38608
|
return authHandler(context, context.wabe, provider);
|
|
38457
38609
|
}
|
|
38458
38610
|
},
|
|
@@ -38484,18 +38636,25 @@ var wabePrimaryTypesToTypescriptTypes = {
|
|
|
38484
38636
|
Email: "string",
|
|
38485
38637
|
Phone: "string",
|
|
38486
38638
|
Date: "Date",
|
|
38487
|
-
File: "{url: string, name: string}",
|
|
38488
38639
|
Hash: "string"
|
|
38489
38640
|
};
|
|
38641
|
+
var getIndent = (options) => options?.indent ?? "\t";
|
|
38642
|
+
var getEndChar = (options) => options?.semi ? ";" : options?.comma ? "," : "";
|
|
38643
|
+
var getQuoteChar = (options) => options?.quote === "double" ? '"' : "'";
|
|
38644
|
+
var getFileTypeString = (options) => {
|
|
38645
|
+
const sep2 = options?.semi ? "; " : ", ";
|
|
38646
|
+
return `{ url: string${sep2}name: string }`;
|
|
38647
|
+
};
|
|
38490
38648
|
var wabeTypesToTypescriptTypes = ({
|
|
38491
38649
|
field,
|
|
38492
|
-
isInput = false
|
|
38650
|
+
isInput = false,
|
|
38651
|
+
formatOptions
|
|
38493
38652
|
}) => {
|
|
38494
38653
|
switch (field.type) {
|
|
38495
38654
|
case "Date":
|
|
38496
|
-
|
|
38497
|
-
|
|
38498
|
-
return
|
|
38655
|
+
return isInput ? "Date" : "string";
|
|
38656
|
+
case "File":
|
|
38657
|
+
return getFileTypeString(formatOptions);
|
|
38499
38658
|
case "Boolean":
|
|
38500
38659
|
case "Int":
|
|
38501
38660
|
case "Float":
|
|
@@ -38503,12 +38662,13 @@ var wabeTypesToTypescriptTypes = ({
|
|
|
38503
38662
|
case "Any":
|
|
38504
38663
|
case "Email":
|
|
38505
38664
|
case "Phone":
|
|
38506
|
-
case "File":
|
|
38507
38665
|
case "Hash":
|
|
38508
38666
|
return wabePrimaryTypesToTypescriptTypes[field.type];
|
|
38509
38667
|
case "Array":
|
|
38510
38668
|
if (field.typeValue === "Object")
|
|
38511
38669
|
return `Array<${field.object.name}>`;
|
|
38670
|
+
if (field.typeValue === "File")
|
|
38671
|
+
return `Array<${getFileTypeString(formatOptions)}>`;
|
|
38512
38672
|
return `Array<${wabePrimaryTypesToTypescriptTypes[field.typeValue]}>`;
|
|
38513
38673
|
case "Pointer":
|
|
38514
38674
|
return field.class;
|
|
@@ -38520,28 +38680,31 @@ var wabeTypesToTypescriptTypes = ({
|
|
|
38520
38680
|
return field.type;
|
|
38521
38681
|
}
|
|
38522
38682
|
};
|
|
38683
|
+
var fieldKey = (name, required) => `${name}${required ? "" : "undefined"}`;
|
|
38523
38684
|
var generateWabeObject = ({
|
|
38524
38685
|
object,
|
|
38525
38686
|
isInput = false,
|
|
38526
|
-
prefix = ""
|
|
38687
|
+
prefix = "",
|
|
38688
|
+
formatOptions
|
|
38527
38689
|
}) => {
|
|
38528
|
-
const
|
|
38690
|
+
const objectNameWithPrefix = `${prefix}${firstLetterUpperCase(object.name)}`;
|
|
38529
38691
|
return Object.entries(object.fields).reduce((acc, [fieldName, field]) => {
|
|
38530
|
-
const type = wabeTypesToTypescriptTypes({ field, isInput });
|
|
38531
|
-
const objectNameWithPrefix = `${prefix}${firstLetterUpperCase(objectName)}`;
|
|
38692
|
+
const type = wabeTypesToTypescriptTypes({ field, isInput, formatOptions });
|
|
38532
38693
|
if (field.type === "Object" || field.type === "Array" && field.typeValue === "Object") {
|
|
38533
38694
|
const subObject = generateWabeObject({
|
|
38534
38695
|
object: field.object,
|
|
38535
38696
|
isInput,
|
|
38536
|
-
prefix: objectNameWithPrefix
|
|
38697
|
+
prefix: objectNameWithPrefix,
|
|
38698
|
+
formatOptions
|
|
38537
38699
|
});
|
|
38538
38700
|
const isArray = field.type === "Array";
|
|
38701
|
+
const subTypeName = `${objectNameWithPrefix}${firstLetterUpperCase(field.object.name)}`;
|
|
38539
38702
|
return {
|
|
38540
38703
|
...acc,
|
|
38541
38704
|
...subObject,
|
|
38542
38705
|
[objectNameWithPrefix]: {
|
|
38543
38706
|
...acc[objectNameWithPrefix],
|
|
38544
|
-
[
|
|
38707
|
+
[fieldKey(fieldName, field.required)]: isArray ? `Array<${subTypeName}>` : subTypeName
|
|
38545
38708
|
}
|
|
38546
38709
|
};
|
|
38547
38710
|
}
|
|
@@ -38549,242 +38712,165 @@ var generateWabeObject = ({
|
|
|
38549
38712
|
...acc,
|
|
38550
38713
|
[objectNameWithPrefix]: {
|
|
38551
38714
|
...acc[objectNameWithPrefix],
|
|
38552
|
-
[
|
|
38715
|
+
[fieldKey(fieldName, field.required)]: `${type}`
|
|
38553
38716
|
}
|
|
38554
38717
|
};
|
|
38555
38718
|
}, {});
|
|
38556
38719
|
};
|
|
38557
|
-
var
|
|
38558
|
-
|
|
38559
|
-
|
|
38560
|
-
|
|
38561
|
-
const
|
|
38562
|
-
|
|
38563
|
-
|
|
38564
|
-
|
|
38565
|
-
objectsToLoad.push(wabeObject);
|
|
38566
|
-
}
|
|
38567
|
-
return {
|
|
38568
|
-
...acc2,
|
|
38569
|
-
[`${name2}${field.required ? "" : "undefined"}`]: type
|
|
38570
|
-
};
|
|
38571
|
-
}, {});
|
|
38572
|
-
const objects = mergeNestedStringRecords(objectsToLoad);
|
|
38573
|
-
return {
|
|
38574
|
-
...acc,
|
|
38575
|
-
...objects,
|
|
38576
|
-
[name]: { id: "string", ...currentClass }
|
|
38577
|
-
};
|
|
38578
|
-
}, {});
|
|
38579
|
-
return wabeTypes;
|
|
38580
|
-
};
|
|
38581
|
-
var generateWabeWhereTypes = (classes) => {
|
|
38582
|
-
const wabeTypes = classes.reduce((acc, classType) => {
|
|
38583
|
-
const { name, fields } = classType;
|
|
38584
|
-
const completeName = `Where${firstLetterUpperCase(name)}`;
|
|
38585
|
-
const objectsToLoad = [];
|
|
38586
|
-
const currentClass = Object.entries(fields).reduce((acc2, [name2, field]) => {
|
|
38587
|
-
const type = wabeTypesToTypescriptTypes({ field, isInput: true });
|
|
38588
|
-
if (field.type === "Object" || field.type === "Array" && field.typeValue === "Object") {
|
|
38589
|
-
const wabeObject = generateWabeObject({
|
|
38590
|
-
object: field.object,
|
|
38591
|
-
isInput: true
|
|
38592
|
-
});
|
|
38593
|
-
objectsToLoad.push(wabeObject);
|
|
38594
|
-
}
|
|
38595
|
-
return {
|
|
38596
|
-
...acc2,
|
|
38597
|
-
[`${name2}${field.required ? "" : "undefined"}`]: type
|
|
38598
|
-
};
|
|
38599
|
-
}, {});
|
|
38600
|
-
const objects = mergeNestedStringRecords(objectsToLoad);
|
|
38601
|
-
return {
|
|
38602
|
-
...acc,
|
|
38603
|
-
...objects,
|
|
38604
|
-
[completeName]: { id: "string", ...currentClass }
|
|
38605
|
-
};
|
|
38606
|
-
}, {});
|
|
38607
|
-
return wabeTypes;
|
|
38608
|
-
};
|
|
38609
|
-
var generateWabeEnumTypes = (enums) => {
|
|
38610
|
-
return Object.values(enums).reduce((acc, { name, values }) => {
|
|
38611
|
-
return {
|
|
38612
|
-
...acc,
|
|
38613
|
-
[name]: values
|
|
38614
|
-
};
|
|
38615
|
-
}, {});
|
|
38616
|
-
};
|
|
38617
|
-
var generateWabeScalarTypes = (scalars) => {
|
|
38618
|
-
return Object.values(scalars).reduce((acc, { name }) => {
|
|
38720
|
+
var mergeNestedStringRecords = (records) => Object.assign({}, ...records);
|
|
38721
|
+
var generateClassTypes = (classes, formatOptions, namePrefix = "", isInput = false) => classes.reduce((acc, { name, fields }) => {
|
|
38722
|
+
const objectsToLoad = [];
|
|
38723
|
+
const currentClass = Object.entries(fields).reduce((acc2, [fieldName, field]) => {
|
|
38724
|
+
const type = wabeTypesToTypescriptTypes({ field, isInput, formatOptions });
|
|
38725
|
+
if (field.type === "Object" || field.type === "Array" && field.typeValue === "Object") {
|
|
38726
|
+
objectsToLoad.push(generateWabeObject({ object: field.object, isInput, formatOptions }));
|
|
38727
|
+
}
|
|
38619
38728
|
return {
|
|
38620
|
-
...
|
|
38621
|
-
[
|
|
38729
|
+
...acc2,
|
|
38730
|
+
[fieldKey(fieldName, field.required)]: type
|
|
38622
38731
|
};
|
|
38623
38732
|
}, {});
|
|
38624
|
-
};
|
|
38625
|
-
|
|
38733
|
+
const completeName = namePrefix ? `${namePrefix}${firstLetterUpperCase(name)}` : name;
|
|
38734
|
+
return {
|
|
38735
|
+
...acc,
|
|
38736
|
+
...mergeNestedStringRecords(objectsToLoad),
|
|
38737
|
+
[completeName]: { id: "string", ...currentClass }
|
|
38738
|
+
};
|
|
38739
|
+
}, {});
|
|
38740
|
+
var generateWabeEnumTypes = (enums) => enums.reduce((acc, { name, values }) => ({ ...acc, [name]: values }), {});
|
|
38741
|
+
var generateWabeScalarTypes = (scalars) => scalars.reduce((acc, { name }) => ({ ...acc, [name]: "string" }), {});
|
|
38742
|
+
var generateWabeMutationOrQueryInput = (mutationOrQueryName, resolver, isMutation, formatOptions) => {
|
|
38626
38743
|
const objectsToLoad = [];
|
|
38627
|
-
const
|
|
38628
|
-
const
|
|
38629
|
-
let type = wabeTypesToTypescriptTypes({ field, isInput: true });
|
|
38744
|
+
const upperName = firstLetterUpperCase(mutationOrQueryName);
|
|
38745
|
+
const resolvedArgs = Object.entries((isMutation ? resolver.args?.input : resolver.args) || {}).reduce((acc, [name, field]) => {
|
|
38630
38746
|
if (field.type === "Object") {
|
|
38631
|
-
|
|
38632
|
-
|
|
38633
|
-
object: {
|
|
38634
|
-
|
|
38635
|
-
|
|
38636
|
-
|
|
38637
|
-
prefix: mutationNameWithFirstLetterUpperCase
|
|
38638
|
-
});
|
|
38639
|
-
objectsToLoad.push(wabeObject);
|
|
38747
|
+
const typeName = firstLetterInUpperCase(name);
|
|
38748
|
+
objectsToLoad.push(generateWabeObject({
|
|
38749
|
+
object: { ...field.object, name: typeName },
|
|
38750
|
+
prefix: upperName,
|
|
38751
|
+
formatOptions
|
|
38752
|
+
}));
|
|
38640
38753
|
return {
|
|
38641
38754
|
...acc,
|
|
38642
|
-
[
|
|
38755
|
+
[fieldKey(name, field.required)]: `${upperName}${typeName}`
|
|
38643
38756
|
};
|
|
38644
38757
|
}
|
|
38645
38758
|
return {
|
|
38646
38759
|
...acc,
|
|
38647
|
-
[
|
|
38648
|
-
|
|
38649
|
-
|
|
38650
|
-
|
|
38651
|
-
|
|
38652
|
-
...isMutation ? {
|
|
38653
|
-
[`${firstLetterInUpperCase(mutationOrQueryName)}Input`]: mutationObject
|
|
38654
|
-
} : {},
|
|
38655
|
-
[`${isMutation ? "Mutation" : "Query"}${firstLetterInUpperCase(mutationOrQueryName)}Args`]: isMutation ? {
|
|
38656
|
-
input: `${firstLetterInUpperCase(mutationOrQueryName)}Input`
|
|
38657
|
-
} : mutationObject,
|
|
38658
|
-
...objects
|
|
38659
|
-
};
|
|
38660
|
-
};
|
|
38661
|
-
var generateWabeMutationsAndQueriesTypes = (resolver) => {
|
|
38662
|
-
const mutationsObject = Object.entries(resolver.mutations || {}).reduce((acc, [mutationName, mutation]) => {
|
|
38663
|
-
return {
|
|
38664
|
-
...acc,
|
|
38665
|
-
...generateWabeMutationOrQueryInput(mutationName, mutation, true)
|
|
38666
|
-
};
|
|
38667
|
-
}, {});
|
|
38668
|
-
const queriesObject = Object.entries(resolver.queries || {}).reduce((acc, [queryName, query]) => {
|
|
38669
|
-
return {
|
|
38670
|
-
...acc,
|
|
38671
|
-
...generateWabeMutationOrQueryInput(queryName, query, false)
|
|
38760
|
+
[fieldKey(name, field.required)]: wabeTypesToTypescriptTypes({
|
|
38761
|
+
field,
|
|
38762
|
+
isInput: true,
|
|
38763
|
+
formatOptions
|
|
38764
|
+
})
|
|
38672
38765
|
};
|
|
38673
38766
|
}, {});
|
|
38767
|
+
const prettyName = firstLetterInUpperCase(mutationOrQueryName);
|
|
38674
38768
|
return {
|
|
38675
|
-
...
|
|
38676
|
-
|
|
38769
|
+
...isMutation ? { [`${prettyName}Input`]: resolvedArgs } : {},
|
|
38770
|
+
[`${isMutation ? "Mutation" : "Query"}${prettyName}Args`]: isMutation ? { input: `${prettyName}Input` } : resolvedArgs,
|
|
38771
|
+
...mergeNestedStringRecords(objectsToLoad)
|
|
38677
38772
|
};
|
|
38678
38773
|
};
|
|
38679
|
-
var
|
|
38680
|
-
|
|
38681
|
-
return import_graphql7.print(import_graphql7.parse(schemaContent));
|
|
38682
|
-
} catch {
|
|
38683
|
-
return schemaContent;
|
|
38684
|
-
}
|
|
38685
|
-
};
|
|
38686
|
-
var mergeNestedStringRecords = (records) => {
|
|
38687
|
-
return records.reduce((acc, currentRecord) => ({
|
|
38774
|
+
var generateWabeMutationsAndQueriesTypes = (resolver, formatOptions) => ({
|
|
38775
|
+
...Object.entries(resolver.mutations || {}).reduce((acc, [name, mutation]) => ({
|
|
38688
38776
|
...acc,
|
|
38689
|
-
...
|
|
38690
|
-
}), {})
|
|
38691
|
-
}
|
|
38692
|
-
|
|
38693
|
-
|
|
38694
|
-
|
|
38695
|
-
|
|
38696
|
-
}) => {
|
|
38697
|
-
return content.split(`
|
|
38698
|
-
`).map((line) => {
|
|
38699
|
-
if (line.length <= printWidth)
|
|
38700
|
-
return line;
|
|
38701
|
-
const fieldWithArgsMatch = line.match(/^(\s*)([_A-Za-z][_0-9A-Za-z]*)\((.+)\):\s*(.+)$/);
|
|
38702
|
-
if (!fieldWithArgsMatch)
|
|
38703
|
-
return line;
|
|
38704
|
-
const [, fieldIndent = "", fieldName = "", argsString = "", returnType = ""] = fieldWithArgsMatch;
|
|
38705
|
-
if (!fieldName || !argsString || !returnType)
|
|
38706
|
-
return line;
|
|
38707
|
-
const args = argsString.split(",").map((arg) => arg.trim()).filter((arg) => arg.length > 0);
|
|
38708
|
-
if (args.length <= 1)
|
|
38709
|
-
return line;
|
|
38710
|
-
return `${fieldIndent}${fieldName}(
|
|
38711
|
-
${args.map((arg) => `${fieldIndent}${indent}${arg}`).join(`
|
|
38712
|
-
`)}
|
|
38713
|
-
${fieldIndent}): ${returnType}`;
|
|
38714
|
-
}).join(`
|
|
38715
|
-
`);
|
|
38716
|
-
};
|
|
38777
|
+
...generateWabeMutationOrQueryInput(name, mutation, true, formatOptions)
|
|
38778
|
+
}), {}),
|
|
38779
|
+
...Object.entries(resolver.queries || {}).reduce((acc, [name, query]) => ({
|
|
38780
|
+
...acc,
|
|
38781
|
+
...generateWabeMutationOrQueryInput(name, query, false, formatOptions)
|
|
38782
|
+
}), {})
|
|
38783
|
+
});
|
|
38717
38784
|
var wabeClassRecordToString = (wabeClass, options) => {
|
|
38718
|
-
const indent = options
|
|
38719
|
-
const endChar = options
|
|
38785
|
+
const indent = getIndent(options);
|
|
38786
|
+
const endChar = getEndChar(options);
|
|
38720
38787
|
return Object.entries(wabeClass).reduce((acc, [className, fields]) => {
|
|
38721
|
-
|
|
38722
|
-
if (fieldsLength === 0)
|
|
38788
|
+
if (Object.keys(fields).length === 0)
|
|
38723
38789
|
return `${acc}export type ${className} = {}
|
|
38724
38790
|
|
|
38725
38791
|
`;
|
|
38792
|
+
const body = Object.entries(fields).map(([name, type]) => `${indent}${name.replace("undefined", "?")}: ${type}`).join(`${endChar}
|
|
38793
|
+
`);
|
|
38726
38794
|
return `${acc}export type ${className} = {
|
|
38727
|
-
${
|
|
38728
|
-
`)}${endChar}
|
|
38795
|
+
${body}${endChar}
|
|
38729
38796
|
}
|
|
38730
38797
|
|
|
38731
38798
|
`;
|
|
38732
38799
|
}, "");
|
|
38733
38800
|
};
|
|
38734
38801
|
var wabeEnumRecordToString = (wabeEnum, options) => {
|
|
38735
|
-
const indent = options
|
|
38802
|
+
const indent = getIndent(options);
|
|
38736
38803
|
const endChar = options?.semi ? ";" : options?.comma ?? true ? "," : "";
|
|
38737
|
-
const
|
|
38804
|
+
const quote = getQuoteChar(options);
|
|
38738
38805
|
return Object.entries(wabeEnum).reduce((acc, [enumName, values]) => {
|
|
38739
|
-
const
|
|
38806
|
+
const hasValues = Object.keys(values).length > 0;
|
|
38807
|
+
const body = Object.entries(values).map(([k, v]) => `${indent}${k} = ${quote}${v}${quote}`).join(`${endChar}
|
|
38808
|
+
`);
|
|
38740
38809
|
return `${acc}export enum ${enumName} {
|
|
38741
|
-
${
|
|
38742
|
-
`)}${valuesLength > 0 ? endChar : ""}
|
|
38810
|
+
${body}${hasValues ? endChar : ""}
|
|
38743
38811
|
}
|
|
38744
38812
|
|
|
38745
38813
|
`;
|
|
38746
38814
|
}, "");
|
|
38747
38815
|
};
|
|
38748
|
-
var wabeScalarRecordToString = (wabeScalar) => {
|
|
38749
|
-
return Object.entries(wabeScalar).reduce((acc, [scalarName, scalarType]) => {
|
|
38750
|
-
return `${acc}export type ${scalarName} = ${scalarType}
|
|
38816
|
+
var wabeScalarRecordToString = (wabeScalar) => Object.entries(wabeScalar).reduce((acc, [name, type]) => `${acc}export type ${name} = ${type}
|
|
38751
38817
|
|
|
38752
|
-
|
|
38753
|
-
|
|
38754
|
-
|
|
38818
|
+
`, "");
|
|
38819
|
+
var wrapLongGraphqlFieldArguments = ({
|
|
38820
|
+
content,
|
|
38821
|
+
indent,
|
|
38822
|
+
printWidth
|
|
38823
|
+
}) => content.split(`
|
|
38824
|
+
`).map((line) => {
|
|
38825
|
+
if (line.length <= printWidth)
|
|
38826
|
+
return line;
|
|
38827
|
+
const match = line.match(/^(\s*)([_A-Za-z][_0-9A-Za-z]*)\((.+)\):\s*(.+)$/);
|
|
38828
|
+
if (!match)
|
|
38829
|
+
return line;
|
|
38830
|
+
const [, fieldIndent = "", fieldName = "", argsString = "", returnType = ""] = match;
|
|
38831
|
+
if (!fieldName || !argsString || !returnType)
|
|
38832
|
+
return line;
|
|
38833
|
+
const args = argsString.split(",").map((arg) => arg.trim()).filter((arg) => arg.length > 0);
|
|
38834
|
+
if (args.length <= 1)
|
|
38835
|
+
return line;
|
|
38836
|
+
const wrappedArgs = args.map((arg) => `${fieldIndent}${indent}${arg}`).join(`
|
|
38837
|
+
`);
|
|
38838
|
+
return `${fieldIndent}${fieldName}(
|
|
38839
|
+
${wrappedArgs}
|
|
38840
|
+
${fieldIndent}): ${returnType}`;
|
|
38841
|
+
}).join(`
|
|
38842
|
+
`);
|
|
38755
38843
|
var generateWabeDevTypes = ({
|
|
38756
38844
|
scalars,
|
|
38757
38845
|
enums,
|
|
38758
38846
|
classes,
|
|
38759
38847
|
options
|
|
38760
38848
|
}) => {
|
|
38761
|
-
const indent = options
|
|
38762
|
-
const endChar = options
|
|
38763
|
-
const
|
|
38764
|
-
const
|
|
38765
|
-
const
|
|
38766
|
-
|
|
38767
|
-
const wabeEnumsGlobalTypesString = wabeEnumsGlobalTypes.length > 0 ? `export type WabeSchemaEnums = {
|
|
38768
|
-
${indent}${wabeEnumsGlobalTypes.join(`${endChar}
|
|
38769
|
-
${indent}`)}${endChar}
|
|
38770
|
-
}` : "";
|
|
38771
|
-
const allNames = classes.map((schema) => `${schema.name}: ${schema.name}`).filter((schema) => schema);
|
|
38772
|
-
const globalWabeTypeString = allNames.length > 0 ? `export type WabeSchemaTypes = {
|
|
38773
|
-
${indent}${allNames.join(`${endChar}
|
|
38774
|
-
${indent}`)}${endChar}
|
|
38775
|
-
}` : "";
|
|
38776
|
-
const allWhereNames = classes.map((schema) => `${schema.name}: Where${firstLetterUpperCase(schema.name)}`).filter((schema) => schema);
|
|
38777
|
-
const globalWabeWhereTypeString = allWhereNames.length > 0 ? `export type WabeSchemaWhereTypes = {
|
|
38778
|
-
${indent}${allWhereNames.join(`${endChar}
|
|
38849
|
+
const indent = getIndent(options);
|
|
38850
|
+
const endChar = getEndChar(options);
|
|
38851
|
+
const quote = getQuoteChar(options);
|
|
38852
|
+
const wabeScalarType = scalars && scalars.length > 0 ? `export type WabeSchemaScalars = ${scalars.map((s) => `${quote}${s.name}${quote}`).join(" | ")}` : `export type WabeSchemaScalars = ${quote}${quote}`;
|
|
38853
|
+
const buildTypeMap = (typeName, entries) => entries.length > 0 ? `export type ${typeName} = {
|
|
38854
|
+
${indent}${entries.join(`${endChar}
|
|
38779
38855
|
${indent}`)}${endChar}
|
|
38780
38856
|
}` : "";
|
|
38857
|
+
const wabeEnumsString = buildTypeMap("WabeSchemaEnums", enums?.map((e) => `${e.name}: ${e.name}`) ?? []);
|
|
38858
|
+
const wabeTypesString = buildTypeMap("WabeSchemaTypes", classes.map((c) => `${c.name}: ${c.name}`));
|
|
38859
|
+
const wabeWhereString = buildTypeMap("WabeSchemaWhereTypes", classes.map((c) => `${c.name}: Where${firstLetterUpperCase(c.name)}`));
|
|
38781
38860
|
return `${wabeScalarType}
|
|
38782
38861
|
|
|
38783
|
-
${
|
|
38862
|
+
${wabeEnumsString}
|
|
38784
38863
|
|
|
38785
|
-
${
|
|
38864
|
+
${wabeTypesString}
|
|
38786
38865
|
|
|
38787
|
-
${
|
|
38866
|
+
${wabeWhereString}`;
|
|
38867
|
+
};
|
|
38868
|
+
var normalizeGraphqlSchemaForComparison = (schemaContent) => {
|
|
38869
|
+
try {
|
|
38870
|
+
return import_graphql7.print(import_graphql7.parse(schemaContent));
|
|
38871
|
+
} catch {
|
|
38872
|
+
return schemaContent;
|
|
38873
|
+
}
|
|
38788
38874
|
};
|
|
38789
38875
|
var generateCodegen = async ({
|
|
38790
38876
|
schema,
|
|
@@ -38793,7 +38879,7 @@ var generateCodegen = async ({
|
|
|
38793
38879
|
options
|
|
38794
38880
|
}) => {
|
|
38795
38881
|
let graphqlSchemaContent = import_graphql7.printSchema(graphqlSchema);
|
|
38796
|
-
const indentStr = options
|
|
38882
|
+
const indentStr = getIndent(options);
|
|
38797
38883
|
const printWidth = options?.printWidth ?? 100;
|
|
38798
38884
|
const shouldEnsureFinalNewline = options?.finalNewline ?? true;
|
|
38799
38885
|
const shouldUseSemanticComparison = options?.semanticCompare ?? true;
|
|
@@ -38817,22 +38903,13 @@ ${indentation}"""`);
|
|
|
38817
38903
|
const resolvers = schema.resolvers ?? {};
|
|
38818
38904
|
const enums = schema.enums ?? [];
|
|
38819
38905
|
const scalars = schema.scalars ?? [];
|
|
38820
|
-
const wabeClasses =
|
|
38821
|
-
const wabeWhereTypes =
|
|
38822
|
-
const mutationsAndQueries = generateWabeMutationsAndQueriesTypes(resolvers);
|
|
38906
|
+
const wabeClasses = generateClassTypes(classes, options);
|
|
38907
|
+
const wabeWhereTypes = generateClassTypes(classes, options, "Where", true);
|
|
38908
|
+
const mutationsAndQueries = generateWabeMutationsAndQueriesTypes(resolvers, options);
|
|
38823
38909
|
const wabeEnumsInString = wabeEnumRecordToString(generateWabeEnumTypes(enums), options);
|
|
38824
38910
|
const wabeScalarsInString = wabeScalarRecordToString(generateWabeScalarTypes(scalars));
|
|
38825
|
-
const wabeObjectsInString = wabeClassRecordToString({
|
|
38826
|
-
|
|
38827
|
-
...wabeWhereTypes,
|
|
38828
|
-
...mutationsAndQueries
|
|
38829
|
-
}, options);
|
|
38830
|
-
const wabeDevTypes = generateWabeDevTypes({
|
|
38831
|
-
scalars,
|
|
38832
|
-
enums,
|
|
38833
|
-
classes,
|
|
38834
|
-
options
|
|
38835
|
-
});
|
|
38911
|
+
const wabeObjectsInString = wabeClassRecordToString({ ...wabeClasses, ...wabeWhereTypes, ...mutationsAndQueries }, options);
|
|
38912
|
+
const wabeDevTypes = generateWabeDevTypes({ scalars, enums, classes, options });
|
|
38836
38913
|
const wabeTsContent = `${wabeEnumsInString}${wabeScalarsInString}${wabeObjectsInString}${wabeDevTypes}
|
|
38837
38914
|
`;
|
|
38838
38915
|
let shouldWriteGraphqlSchema = true;
|
|
@@ -39175,7 +39252,8 @@ class EmailOTP {
|
|
|
39175
39252
|
if (!user.email)
|
|
39176
39253
|
throw new Error("No user email found");
|
|
39177
39254
|
const otpClass = new OTP(context.wabe.config.rootKey);
|
|
39178
|
-
const
|
|
39255
|
+
const salt = await getOrCreateOtpSalt(context, user.id);
|
|
39256
|
+
const otp = otpClass.generate(user.id, salt);
|
|
39179
39257
|
const template = context.wabe.config.email?.htmlTemplates?.sendOTPCode;
|
|
39180
39258
|
await emailController.send({
|
|
39181
39259
|
from: mainEmail,
|
|
@@ -39215,10 +39293,10 @@ class EmailOTP {
|
|
|
39215
39293
|
});
|
|
39216
39294
|
const realUser = users.length > 0 ? users[0] : null;
|
|
39217
39295
|
const userId = realUser?.id ?? DUMMY_USER_ID2;
|
|
39218
|
-
const isDevBypass = !context.wabe.config.isProduction && input.otp === "000000" && realUser !== null;
|
|
39219
39296
|
const otpClass = new OTP(context.wabe.config.rootKey);
|
|
39220
|
-
const
|
|
39221
|
-
|
|
39297
|
+
const salt = realUser ? await getOrCreateOtpSalt(context, userId) : undefined;
|
|
39298
|
+
const isOtpValid = otpClass.verify(input.otp, userId, salt);
|
|
39299
|
+
if (realUser && isOtpValid) {
|
|
39222
39300
|
clearRateLimit(context, "verifyChallenge", rateLimitKey);
|
|
39223
39301
|
return { userId: realUser.id };
|
|
39224
39302
|
}
|
|
@@ -39262,10 +39340,10 @@ class QRCodeOTP {
|
|
|
39262
39340
|
});
|
|
39263
39341
|
const realUser = users.length > 0 ? users[0] : null;
|
|
39264
39342
|
const userId = realUser?.id ?? DUMMY_USER_ID3;
|
|
39265
|
-
const isDevBypass = !context.wabe.config.isProduction && input.otp === "000000" && realUser !== null;
|
|
39266
39343
|
const otpClass = new OTP(context.wabe.config.rootKey);
|
|
39267
|
-
const
|
|
39268
|
-
|
|
39344
|
+
const salt = realUser ? await getOrCreateOtpSalt(context, userId) : undefined;
|
|
39345
|
+
const isOtpValid = otpClass.authenticatorVerify(input.otp, userId, salt);
|
|
39346
|
+
if (realUser && isOtpValid) {
|
|
39269
39347
|
clearRateLimit(context, "verifyChallenge", rateLimitKey);
|
|
39270
39348
|
return { userId: realUser.id };
|
|
39271
39349
|
}
|
|
@@ -51773,34 +51851,39 @@ class Wabe {
|
|
|
51773
51851
|
this.server.options("/*", (ctx) => {
|
|
51774
51852
|
return ctx.res.send("OK");
|
|
51775
51853
|
}, cors(this.config.security?.corsOptions));
|
|
51776
|
-
const rateLimitOptions = this.config.security?.rateLimit
|
|
51854
|
+
const rateLimitOptions = this.config.security?.rateLimit || (this.config.isProduction ? {
|
|
51855
|
+
numberOfRequests: 200,
|
|
51856
|
+
interval: 60000
|
|
51857
|
+
} : undefined);
|
|
51777
51858
|
if (rateLimitOptions)
|
|
51778
51859
|
this.server.beforeHandler(rateLimit(rateLimitOptions));
|
|
51779
51860
|
this.server.beforeHandler(cors(this.config.security?.corsOptions));
|
|
51780
51861
|
this.server.beforeHandler(this.config.authentication?.sessionHandler || defaultSessionHandler(this));
|
|
51781
|
-
const maxDepth = this.config.security?.maxGraphqlDepth ?? 50;
|
|
51862
|
+
const maxDepth = this.config.security?.maxGraphqlDepth ?? (this.config.isProduction ? 15 : 50);
|
|
51863
|
+
const introspectionDisabled = this.config.security?.disableIntrospection ?? this.config.isProduction;
|
|
51864
|
+
const disableGraphQLDashboard = this.config.security?.disableGraphQLDashboard ?? this.config.isProduction;
|
|
51782
51865
|
await this.server.usePlugin(M({
|
|
51783
51866
|
schema: this.config.graphqlSchema,
|
|
51784
|
-
allowGetRequests: !
|
|
51867
|
+
allowGetRequests: !disableGraphQLDashboard,
|
|
51785
51868
|
maskedErrors: this.config.security?.hideSensitiveErrorMessage || this.config.isProduction,
|
|
51786
|
-
allowIntrospection: !
|
|
51869
|
+
allowIntrospection: !introspectionDisabled,
|
|
51787
51870
|
maxDepth,
|
|
51788
51871
|
allowMultipleOperations: true,
|
|
51789
51872
|
graphqlEndpoint: "/graphql",
|
|
51790
51873
|
plugins: [
|
|
51791
51874
|
(() => {
|
|
51792
|
-
const
|
|
51875
|
+
const introspectionDisabledRequests = new WeakSet;
|
|
51793
51876
|
return {
|
|
51794
51877
|
onRequestParse: ({ request }) => {
|
|
51795
|
-
if (!
|
|
51878
|
+
if (!introspectionDisabled)
|
|
51796
51879
|
return;
|
|
51797
|
-
|
|
51880
|
+
introspectionDisabledRequests.add(request);
|
|
51798
51881
|
},
|
|
51799
51882
|
onValidate: ({
|
|
51800
51883
|
addValidationRule,
|
|
51801
51884
|
context
|
|
51802
51885
|
}) => {
|
|
51803
|
-
if (
|
|
51886
|
+
if (introspectionDisabledRequests.has(context.request)) {
|
|
51804
51887
|
addValidationRule(import_graphql52.NoSchemaIntrospectionCustomRule);
|
|
51805
51888
|
}
|
|
51806
51889
|
}
|
|
@@ -51816,6 +51899,9 @@ class Wabe {
|
|
|
51816
51899
|
await initializeRoles(this);
|
|
51817
51900
|
}
|
|
51818
51901
|
async close() {
|
|
51902
|
+
this.config.crons?.forEach(({ job }) => {
|
|
51903
|
+
job?.stop?.();
|
|
51904
|
+
});
|
|
51819
51905
|
await this.controllers.database.close();
|
|
51820
51906
|
await this.server.stop();
|
|
51821
51907
|
}
|
|
@@ -52356,8 +52442,10 @@ export {
|
|
|
52356
52442
|
isArgon2Hash,
|
|
52357
52443
|
initializeHook,
|
|
52358
52444
|
hashArgon2,
|
|
52445
|
+
getOrCreateOtpSalt,
|
|
52359
52446
|
getDefaultHooks,
|
|
52360
52447
|
getDatabaseController,
|
|
52448
|
+
generateOtpSalt,
|
|
52361
52449
|
generateCodegen,
|
|
52362
52450
|
encryptDeterministicToken,
|
|
52363
52451
|
defaultRoutes,
|