Yuki Takei 4 lat temu
rodzic
commit
9321dfd706

+ 19 - 16
packages/app/src/server/middlewares/inject-reset-order-by-token-middleware.ts

@@ -1,24 +1,27 @@
-const createError = require('http-errors');
+import { NextFunction, Request, Response } from 'express';
+import createError from 'http-errors';
 
 
-module.exports = (crowi, app) => {
-  const PasswordResetOrder = crowi.model('PasswordResetOrder');
+import PasswordResetOrder, { IPasswordResetOrder } from '../models/password-reset-order';
 
 
-  return async(req, res, next) => {
-    const token = req.params.token || req.body.token;
+export type ReqWithPasswordResetOrder = Request & {
+  passwordResetOrder: IPasswordResetOrder,
+};
+
+export default async(req: ReqWithPasswordResetOrder, res: Response, next: NextFunction): Promise<void> => {
+  const token = req.params.token || req.body.token;
 
 
-    if (token == null) {
-      return next(createError(400, 'Token not found', { code: 'token-not-found' }));
-    }
+  if (token == null) {
+    return next(createError(400, 'Token not found', { code: 'token-not-found' }));
+  }
 
 
-    const passwordResetOrder = await PasswordResetOrder.findOne({ token });
+  const passwordResetOrder = await PasswordResetOrder.findOne({ token });
 
 
-    // check if the token is valid
-    if (passwordResetOrder == null || passwordResetOrder.isExpired() || passwordResetOrder.isRevoked) {
-      return next(createError(400, 'passwordResetOrder is null or expired or revoked', { code: 'password-reset-order-is-not-appropriate' }));
-    }
+  // check if the token is valid
+  if (passwordResetOrder == null || passwordResetOrder.isExpired() || passwordResetOrder.isRevoked) {
+    return next(createError(400, 'passwordResetOrder is null or expired or revoked', { code: 'password-reset-order-is-not-appropriate' }));
+  }
 
 
-    req.passwordResetOrder = passwordResetOrder;
+  req.passwordResetOrder = passwordResetOrder;
 
 
-    return next();
-  };
+  return next();
 };
 };

+ 0 - 1
packages/app/src/server/models/index.js

@@ -17,5 +17,4 @@ module.exports = {
   GlobalNotificationSlackSetting: require('./GlobalNotificationSetting/GlobalNotificationSlackSetting'),
   GlobalNotificationSlackSetting: require('./GlobalNotificationSetting/GlobalNotificationSlackSetting'),
   ShareLink: require('./share-link'),
   ShareLink: require('./share-link'),
   SlackAppIntegration: require('./slack-app-integration'),
   SlackAppIntegration: require('./slack-app-integration'),
-  PasswordResetOrder: require('./password-reset-order'),
 };
 };

+ 52 - 37
packages/app/src/server/models/password-reset-order.ts

@@ -1,10 +1,34 @@
-const mongoose = require('mongoose');
-const uniqueValidator = require('mongoose-unique-validator');
-const crypto = require('crypto');
+import mongoose, {
+  Schema, Model, Document,
+} from 'mongoose';
+
+import uniqueValidator from 'mongoose-unique-validator';
+import crypto from 'crypto';
+import { getOrCreateModel } from '../util/mongoose-utils';
 
 
 const ObjectId = mongoose.Schema.Types.ObjectId;
 const ObjectId = mongoose.Schema.Types.ObjectId;
 
 
-const schema = new mongoose.Schema({
+export interface IPasswordResetOrder {
+  token: string,
+  email: string,
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  relatedUser: any,
+  isRevoked: boolean,
+  createdAt: Date,
+  expiredAt: Date,
+}
+
+export interface PasswordResetOrderDocument extends IPasswordResetOrder, Document {
+  isExpired(): Promise<boolean>
+  revokeOneTimeToken(): Promise<void>
+}
+
+export interface PasswordResetOrderModel extends Model<PasswordResetOrderDocument> {
+  generateOneTimeToken(): string
+  createPasswordResetOrder(email: string): PasswordResetOrderDocument
+}
+
+const schema = new Schema<PasswordResetOrderDocument, PasswordResetOrderModel>({
   token: { type: String, required: true, unique: true },
   token: { type: String, required: true, unique: true },
   email: { type: String, required: true },
   email: { type: String, required: true },
   relatedUser: { type: ObjectId, ref: 'User' },
   relatedUser: { type: ObjectId, ref: 'User' },
@@ -14,44 +38,35 @@ const schema = new mongoose.Schema({
 });
 });
 schema.plugin(uniqueValidator);
 schema.plugin(uniqueValidator);
 
 
-class PasswordResetOrder {
-
-  static generateOneTimeToken() {
-    const buf = crypto.randomBytes(256);
-    const token = buf.toString('hex');
+schema.statics.generateOneTimeToken = function() {
+  const buf = crypto.randomBytes(256);
+  const token = buf.toString('hex');
 
 
-    return token;
-  }
-
-  static async createPasswordResetOrder(email) {
-    let token;
-    let duplicateToken;
-
-    do {
-      token = this.generateOneTimeToken();
-      // eslint-disable-next-line no-await-in-loop
-      duplicateToken = await this.findOne({ token });
-    } while (duplicateToken != null);
+  return token;
+};
 
 
-    const passwordResetOrderData = await this.create({ token, email });
+schema.statics.createPasswordResetOrder = async function(email) {
+  let token;
+  let duplicateToken;
 
 
-    return passwordResetOrderData;
-  }
+  do {
+    token = this.generateOneTimeToken();
+    // eslint-disable-next-line no-await-in-loop
+    duplicateToken = await this.findOne({ token });
+  } while (duplicateToken != null);
 
 
-  isExpired() {
-    return this.expiredAt.getTime() < Date.now();
-  }
+  const passwordResetOrderData = await this.create({ token, email });
 
 
-  async revokeOneTimeToken() {
-    this.isRevoked = true;
-    return this.save();
-  }
+  return passwordResetOrderData;
+};
 
 
-}
+schema.methods.isExpired = function() {
+  return this.expiredAt.getTime() < Date.now();
+};
 
 
-module.exports = function(crowi) {
-  PasswordResetOrder.crowi = crowi;
-  schema.loadClass(PasswordResetOrder);
-  const model = mongoose.model('PasswordResetOrder', schema);
-  return model;
+schema.methods.revokeOneTimeToken = async function() {
+  this.isRevoked = true;
+  return this.save();
 };
 };
+
+export default getOrCreateModel<PasswordResetOrderDocument, PasswordResetOrderModel>('PasswordResetOrder', schema);

+ 3 - 2
packages/app/src/server/routes/apiv3/forgot-password.js

@@ -1,5 +1,8 @@
 import rateLimit from 'express-rate-limit';
 import rateLimit from 'express-rate-limit';
+
+import PasswordResetOrder from '~/server/models/password-reset-order';
 import ErrorV3 from '~/server/models/vo/error-apiv3';
 import ErrorV3 from '~/server/models/vo/error-apiv3';
+import injectResetOrderByTokenMiddleware from '~/server/middlewares/inject-reset-order-by-token-middleware';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:routes:apiv3:forgotPassword'); // eslint-disable-line no-unused-vars
 const logger = loggerFactory('growi:routes:apiv3:forgotPassword'); // eslint-disable-line no-unused-vars
@@ -12,12 +15,10 @@ const router = express.Router();
 
 
 module.exports = (crowi) => {
 module.exports = (crowi) => {
   const { appService, mailService, configManager } = crowi;
   const { appService, mailService, configManager } = crowi;
-  const PasswordResetOrder = crowi.model('PasswordResetOrder');
   const User = crowi.model('User');
   const User = crowi.model('User');
   const path = require('path');
   const path = require('path');
   const csrf = require('../../middlewares/csrf')(crowi);
   const csrf = require('../../middlewares/csrf')(crowi);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
-  const injectResetOrderByTokenMiddleware = require('../../middlewares/inject-reset-order-by-token-middleware')(crowi);
 
 
   const validator = {
   const validator = {
     password: [
     password: [

+ 17 - 20
packages/app/src/server/routes/forgot-password.ts

@@ -1,24 +1,21 @@
-module.exports = function(crowi, app) {
-  const actions = {};
-  const api = {};
-  actions.api = api;
+import {
+  NextFunction, Request, RequestHandler, Response,
+} from 'express';
+import { ReqWithPasswordResetOrder } from '../middlewares/inject-reset-order-by-token-middleware';
 
 
-  actions.forgotPassword = async function(req, res) {
-    return res.render('forgot-password');
-  };
-
-  actions.resetPassword = async function(req, res) {
-    const { passwordResetOrder } = req;
-    return res.render('reset-password', { email: passwordResetOrder.email });
-  };
+export const forgotPassword = (req: Request, res: Response): void => {
+  return res.render('forgot-password');
+};
 
 
-  // middleware to handle error
-  actions.handleHttpErrosMiddleware = (error, req, res, next) => {
-    if (error != null) {
-      return res.render('forgot-password/error', { key: error.code });
-    }
-    next();
-  };
+export const resetPassword = (req: ReqWithPasswordResetOrder, res: Response): void => {
+  const { passwordResetOrder } = req;
+  return res.render('reset-password', { email: passwordResetOrder.email });
+};
 
 
-  return actions;
+// middleware to handle error
+export const handleHttpErrosMiddleware = (error: Error & { code: string }, req: Request, res: Response, next: NextFunction): Promise<RequestHandler> | void => {
+  if (error != null) {
+    return res.render('forgot-password/error', { key: error.code });
+  }
+  next();
 };
 };

+ 4 - 2
packages/app/src/server/routes/index.js

@@ -1,5 +1,9 @@
 import express from 'express';
 import express from 'express';
 
 
+import injectResetOrderByTokenMiddleware from '../middlewares/inject-reset-order-by-token-middleware';
+
+import * as forgotPassword from './forgot-password';
+
 const multer = require('multer');
 const multer = require('multer');
 const autoReap = require('multer-autoreap');
 const autoReap = require('multer-autoreap');
 const rateLimit = require('express-rate-limit');
 const rateLimit = require('express-rate-limit');
@@ -23,7 +27,6 @@ module.exports = function(crowi, app) {
   const adminRequired = require('../middlewares/admin-required')(crowi);
   const adminRequired = require('../middlewares/admin-required')(crowi);
   const certifySharedFile = require('../middlewares/certify-shared-file')(crowi);
   const certifySharedFile = require('../middlewares/certify-shared-file')(crowi);
   const csrf = require('../middlewares/csrf')(crowi);
   const csrf = require('../middlewares/csrf')(crowi);
-  const injectResetOrderByTokenMiddleware = require('../middlewares/inject-reset-order-by-token-middleware')(crowi);
 
 
   const uploads = multer({ dest: `${crowi.tmpDir}uploads` });
   const uploads = multer({ dest: `${crowi.tmpDir}uploads` });
   const form = require('../form');
   const form = require('../form');
@@ -39,7 +42,6 @@ module.exports = function(crowi, app) {
   const tag = require('./tag')(crowi, app);
   const tag = require('./tag')(crowi, app);
   const search = require('./search')(crowi, app);
   const search = require('./search')(crowi, app);
   const hackmd = require('./hackmd')(crowi, app);
   const hackmd = require('./hackmd')(crowi, app);
-  const forgotPassword = require('./forgot-password')(crowi, app);
 
 
   const isInstalled = crowi.configManager.getConfig('crowi', 'app:installed');
   const isInstalled = crowi.configManager.getConfig('crowi', 'app:installed');