validatedRequest.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. const { SystemSettings } = require("../../models/systemSettings");
  2. const { User } = require("../../models/user");
  3. const { EncryptionManager } = require("../EncryptionManager");
  4. const { decodeJWT } = require("../http");
  5. const { UserMetaCache } = require("../userLocale");
  6. const EncryptionMgr = new EncryptionManager();
  7. async function validatedRequest(request, response, next) {
  8. const multiUserMode = await SystemSettings.isMultiUserMode();
  9. response.locals.multiUserMode = multiUserMode;
  10. if (multiUserMode)
  11. return await validateMultiUserRequest(request, response, next);
  12. // When in development passthrough auth token for ease of development.
  13. // Or if the user simply did not set an Auth token or JWT Secret
  14. if (
  15. process.env.NODE_ENV === "development" ||
  16. !process.env.AUTH_TOKEN ||
  17. !process.env.JWT_SECRET
  18. ) {
  19. UserMetaCache.setFromRequest(request);
  20. next();
  21. return;
  22. }
  23. if (!process.env.AUTH_TOKEN) {
  24. response.status(401).json({
  25. error: "You need to set an AUTH_TOKEN environment variable.",
  26. });
  27. return;
  28. }
  29. const auth = request.header("Authorization");
  30. const token = auth ? auth.split(" ")[1] : null;
  31. if (!token) {
  32. response.status(401).json({
  33. error: "No auth token found.",
  34. });
  35. return;
  36. }
  37. const bcrypt = require("bcryptjs");
  38. const { p } = decodeJWT(token);
  39. if (p === null || !/\w{32}:\w{32}/.test(p)) {
  40. response.status(401).json({
  41. error: "Token expired or failed validation.",
  42. });
  43. return;
  44. }
  45. // Since the blame of this comment we have been encrypting the `p` property of JWTs with the persistent
  46. // encryptionManager PEM's. This prevents us from storing the `p` unencrypted in the JWT itself, which could
  47. // be unsafe. As a consequence, existing JWTs with invalid `p` values that do not match the regex
  48. // in ln:44 will be marked invalid so they can be logged out and forced to log back in and obtain an encrypted token.
  49. // This kind of methodology only applies to single-user password mode.
  50. // [PARANDUS 2026-09-26] Käsitle käsitlemata erand: vigane/aegunud JWT (nt vana
  51. // krüptovõtmega) ei tohi protsessi kukutada. Tagasta 401.
  52. try {
  53. const _decrypted = EncryptionMgr.decrypt(p);
  54. if (
  55. !_decrypted ||
  56. !bcrypt.compareSync(_decrypted, bcrypt.hashSync(process.env.AUTH_TOKEN, 10))
  57. ) {
  58. response.status(401).json({ error: "Invalid auth credentials." });
  59. return;
  60. }
  61. } catch (e) {
  62. response.status(401).json({ error: "Invalid auth credentials." });
  63. return;
  64. }
  65. UserMetaCache.setFromRequest(request);
  66. next();
  67. }
  68. async function validateMultiUserRequest(request, response, next) {
  69. const auth = request.header("Authorization");
  70. const token = auth ? auth.split(" ")[1] : null;
  71. if (!token) {
  72. response.status(401).json({
  73. error: "No auth token found.",
  74. });
  75. return;
  76. }
  77. const valid = decodeJWT(token);
  78. if (!valid || !valid.id) {
  79. response.status(401).json({
  80. error: "Invalid auth token.",
  81. });
  82. return;
  83. }
  84. const user = await User.get({ id: valid.id });
  85. if (!user) {
  86. response.status(401).json({
  87. error: "Invalid auth for user.",
  88. });
  89. return;
  90. }
  91. if (user.suspended) {
  92. response.status(401).json({
  93. error: "User is suspended from system",
  94. });
  95. return;
  96. }
  97. response.locals.user = user;
  98. UserMetaCache.setFromRequest(request, user.id);
  99. next();
  100. }
  101. module.exports = {
  102. validatedRequest,
  103. };