You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

111 lines
2.9 KiB

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