Просмотр исходного кода

Merge pull request #5327 from weseek/dev/4.5.x

Release v4.5.15
Yuki Takei 4 лет назад
Родитель
Сommit
8dfbb1cf44

+ 5 - 5
.devcontainer/docker-compose.yml

@@ -35,11 +35,11 @@ services:
       - /data/db
 
   ogp:
-  image: ghcr.io/weseek/growi-unique-ogp:latest
-  ports:
-    - 8088:8088
-  restart: unless-stopped
-  tty: true
+    image: ghcr.io/weseek/growi-unique-ogp:latest
+    ports:
+      - 8088:8088
+    restart: unless-stopped
+    tty: true
 
   # This container requires '../../growi-docker-compose' repository
   #   cloned from https://github.com/weseek/growi-docker-compose.git

+ 1 - 1
lerna.json

@@ -1,7 +1,7 @@
 {
   "npmClient": "yarn",
   "useWorkspaces": true,
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "packages": [
     "packages/*"
   ]

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -87,7 +87,7 @@
     "yargs": "^17.3.1"
   },
   "engines": {
-    "node": "^12 || ^14",
+    "node": "^12 || ^14 || ^16",
     "npm": ">=6.11.3 <7",
     "yarn": ">=1.19.1 <2"
   }

+ 8 - 8
packages/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "license": "MIT",
   "scripts": {
     "//// for production": "",
@@ -59,11 +59,11 @@
     "@browser-bunyan/console-formatted-stream": "^1.6.2",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/codemirror-textlint": "^4.5.14",
-    "@growi/plugin-attachment-refs": "^4.5.14",
-    "@growi/plugin-lsx": "^4.5.14",
-    "@growi/plugin-pukiwiki-like-linker": "^4.5.14",
-    "@growi/slack": "^4.5.14",
+    "@growi/codemirror-textlint": "^4.5.15-RC.0",
+    "@growi/plugin-attachment-refs": "^4.5.15-RC.0",
+    "@growi/plugin-lsx": "^4.5.15-RC.0",
+    "@growi/plugin-pukiwiki-like-linker": "^4.5.15-RC.0",
+    "@growi/slack": "^4.5.15-RC.0",
     "@promster/express": "^5.1.0",
     "@promster/server": "^6.0.3",
     "@slack/events-api": "^3.0.0",
@@ -105,7 +105,7 @@
     "extensible-custom-error": "^0.0.7",
     "graceful-fs": "^4.1.11",
     "helmet": "^4.6.0",
-    "http-errors": "~1.8.0",
+    "http-errors": "^2.0.0",
     "i18next": "^20.3.2",
     "i18next-express-middleware": "^2.0.0",
     "i18next-node-fs-backend": "^2.1.3",
@@ -162,7 +162,7 @@
   },
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.1.4",
-    "@growi/ui": "^4.5.14",
+    "@growi/ui": "^4.5.15-RC.0",
     "@handsontable/react": "=2.1.0",
     "@types/compression": "^1.7.0",
     "@types/express": "^4.17.11",

+ 1 - 1
packages/app/src/components/LoginForm.jsx

@@ -297,7 +297,7 @@ class LoginForm extends React.Component {
               <div className="front">
                 {isLocalOrLdapStrategiesEnabled && this.renderLocalOrLdapLoginForm()}
                 {isSomeExternalAuthEnabled && this.renderExternalAuthLoginForm()}
-                {isPasswordResetEnabled && (
+                {isLocalOrLdapStrategiesEnabled && isPasswordResetEnabled && (
                   <div className="text-right mb-2">
                     <a href="/forgot-password" className="d-block link-switch">
                       <i className="icon-key"></i> {t('forgot_password.forgot_password')}

+ 1 - 1
packages/app/src/server/middlewares/auto-reconnect-to-search.js

@@ -21,7 +21,7 @@ module.exports = (crowi) => {
   };
 
   return (req, res, next) => {
-    if (searchService != null && !searchService.isReachable) {
+    if (searchService != null && searchService.isConfigured && !searchService.isReachable) {
       // NON-BLOCKING CALL
       // for the latency of the response
       nextTick(reconnectContext, reconnectHandler);

+ 1 - 16
packages/app/src/server/middlewares/http-error-handler.js

@@ -1,23 +1,8 @@
-import { HttpError } from 'http-errors';
+import { isHttpError } from 'http-errors';
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:middleware:htto-error-handler');
 
-const isHttpError = (val) => {
-  if (!val || typeof val !== 'object') {
-    return false;
-  }
-
-  if (val instanceof HttpError) {
-    return true;
-  }
-
-  return val instanceof Error
-    && typeof val.expose === 'boolean'
-    && typeof val.statusCode === 'number'
-    && val.status === val.statusCode;
-};
-
 module.exports = async(err, req, res, next) => {
   // handle if the err is a HttpError instance
   if (isHttpError(err)) {

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

@@ -5,6 +5,9 @@ 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 { checkForgotPasswordEnabledMiddlewareFactory } from '../forgot-password';
+import httpErrorHandler from '../../middlewares/http-error-handler';
+
 const logger = loggerFactory('growi:routes:apiv3:forgotPassword'); // eslint-disable-line no-unused-vars
 
 const express = require('express');
@@ -40,6 +43,8 @@ module.exports = (crowi) => {
       'Too many requests were sent from this IP. Please try a password reset request again on the password reset request form',
   });
 
+  const checkPassportStrategyMiddleware = checkForgotPasswordEnabledMiddlewareFactory(crowi, true);
+
   async function sendPasswordResetEmail(txtFileName, i18n, email, url) {
     return mailService.send({
       to: email,
@@ -53,7 +58,7 @@ module.exports = (crowi) => {
     });
   }
 
-  router.post('/', async(req, res) => {
+  router.post('/', checkPassportStrategyMiddleware, async(req, res) => {
     const { email } = req.body;
     const grobalLang = configManager.getConfig('crowi', 'app:globalLang');
     const i18n = req.language || grobalLang;
@@ -81,7 +86,8 @@ module.exports = (crowi) => {
     }
   });
 
-  router.put('/', apiLimiter, injectResetOrderByTokenMiddleware, csrf, validator.password, apiV3FormValidator, async(req, res) => {
+  // eslint-disable-next-line max-len
+  router.put('/', apiLimiter, checkPassportStrategyMiddleware, injectResetOrderByTokenMiddleware, csrf, validator.password, apiV3FormValidator, async(req, res) => {
     const { passwordResetOrder } = req;
     const { email } = passwordResetOrder;
     const grobalLang = configManager.getConfig('crowi', 'app:globalLang');
@@ -109,6 +115,7 @@ module.exports = (crowi) => {
   });
 
   // middleware to handle error
+  router.use(httpErrorHandler);
   router.use((error, req, res, next) => {
     if (error != null) {
       return res.apiv3Err(new ErrorV3(error.message, error.code));

+ 32 - 2
packages/app/src/server/routes/forgot-password.ts

@@ -1,8 +1,38 @@
 import {
   NextFunction, Request, RequestHandler, Response,
 } from 'express';
+
+import createError from 'http-errors';
+
+import loggerFactory from '~/utils/logger';
+
 import { ReqWithPasswordResetOrder } from '../middlewares/inject-reset-order-by-token-middleware';
 
+const logger = loggerFactory('growi:routes:forgot-password');
+
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
+export const checkForgotPasswordEnabledMiddlewareFactory = (crowi: any, forApi = false) => {
+
+  return (req: Request, res: Response, next: NextFunction): void => {
+    const isPasswordResetEnabled = crowi.configManager.getConfig('crowi', 'security:passport-local:isPasswordResetEnabled') as boolean | null;
+    const isLocalStrategySetup = crowi.passportService.isLocalStrategySetup as boolean ?? false;
+
+    const isEnabled = isLocalStrategySetup && isPasswordResetEnabled;
+
+    if (!isEnabled) {
+      const message = 'Forgot-password function is unavailable because neither LocalStrategy and LdapStrategy is not setup.';
+      logger.error(message);
+
+      const statusCode = forApi ? 405 : 404;
+      return next(createError(statusCode, message, { code: 'password-reset-is-unavailable' }));
+    }
+
+    next();
+  };
+
+};
+
 export const forgotPassword = (req: Request, res: Response): void => {
   return res.render('forgot-password');
 };
@@ -13,9 +43,9 @@ export const resetPassword = (req: ReqWithPasswordResetOrder, res: Response): vo
 };
 
 // middleware to handle error
-export const handleHttpErrosMiddleware = (error: Error & { code: string }, req: Request, res: Response, next: NextFunction): Promise<RequestHandler> | void => {
+export const handleErrosMiddleware = (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();
+  next(error);
 };

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

@@ -195,9 +195,10 @@ module.exports = function(crowi, app) {
   app.post('/_api/hackmd.saveOnHackmd'   , accessTokenParser , loginRequiredStrictly , csrf, hackmd.validateForApi, hackmd.saveOnHackmd);
 
   app.use('/forgot-password', express.Router()
+    .use(forgotPassword.checkForgotPasswordEnabledMiddlewareFactory(crowi))
     .get('/', forgotPassword.forgotPassword)
     .get('/:token', apiLimiter, injectResetOrderByTokenMiddleware, forgotPassword.resetPassword)
-    .use(forgotPassword.handleHttpErrosMiddleware));
+    .use(forgotPassword.handleErrosMiddleware));
 
   app.use('/user-activation', express.Router()
     .get('/:token', apiLimiter, applicationInstalled, injectUserRegistrationOrderByTokenMiddleware, userActivation.form)

+ 6 - 0
packages/app/src/server/views/forgot-password/error.html

@@ -33,6 +33,11 @@
             <div class="text-center">
               <h1><i class="icon-lock-open large"></i></h1>
               <h2 class="text-center">{{ t('forgot_password.reset_password') }}</h2>
+
+                {% if key === 'password-reset-is-unavailable' %}
+                <h3 class="text-muted">This feature is unavailable.</h3>
+                {% endif %}
+
                 {% if key === 'password-reset-order-is-not-appropriate' %}
                 <div>
                   <div class="alert alert-warning mb-3">
@@ -43,6 +48,7 @@
                   </a>
                 </div>
                 {% endif %}
+
             </div>
           </div>
         </div>

+ 2 - 0
packages/app/src/server/views/layout-growi/page.html

@@ -1,6 +1,8 @@
 {% extends 'base/layout.html' %}
 
 {% block html_additional_headers %}
+  {% parent %}
+
   <!-- OGP -->
   <meta property="og:site_name" content="{{ appService.getAppTitle() | preventXss }}" />
   <meta property="og:title" content="{{ page.path | preventXss }}" />

+ 1 - 1
packages/codemirror-textlint/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/codemirror-textlint",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "scripts": {

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/core",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "description": "GROWI Core Libraries",
   "license": "MIT",
   "keywords": [

+ 2 - 2
packages/plugin-attachment-refs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-attachment-refs",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "license": "MIT",
   "keywords": [
@@ -24,7 +24,7 @@
   "dependencies": {
     "browser-bunyan": "^1.6.3",
     "bunyan": "^1.8.15",
-    "http-errors": "^1.8.0",
+    "http-errors": "^2.0.0",
     "react-images": "~1.0.0",
     "react-motion": "^0.5.2",
     "universal-bunyan": "^0.9.2"

+ 1 - 1
packages/plugin-lsx/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-lsx",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "description": "GROWI plugin to list pages",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/plugin-pukiwiki-like-linker/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/plugin-pukiwiki-like-linker",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "description": "GROWI plugin to add PukiwikiLikeLinker",
   "license": "MIT",
   "keywords": [

+ 2 - 2
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slack",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "typings": "dist/index.d.ts",
@@ -18,7 +18,7 @@
     "browser-bunyan": "^1.6.3",
     "bunyan": "^1.8.15",
     "extensible-custom-error": "^0.0.7",
-    "http-errors": "^1.8.0",
+    "http-errors": "^2.0.0",
     "qs": "^6.10.2",
     "universal-bunyan": "^0.9.2",
     "url-join": "^4.0.0"

+ 3 - 3
packages/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slackbot-proxy",
-  "version": "4.5.14",
+  "version": "4.5.15-slackbot-proxy.0",
   "license": "MIT",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
@@ -25,7 +25,7 @@
   },
   "dependencies": {
     "@godaddy/terminus": "^4.9.0",
-    "@growi/slack": "^4.5.14",
+    "@growi/slack": "^4.5.15-RC.0",
     "@slack/oauth": "^2.0.1",
     "@slack/web-api": "^6.2.4",
     "@tsed/common": "^6.43.0",
@@ -42,7 +42,7 @@
     "express-bunyan-logger": "^1.3.3",
     "extensible-custom-error": "^0.0.7",
     "helmet": "^4.6.0",
-    "http-errors": "^1.8.0",
+    "http-errors": "^2.0.0",
     "method-override": "^3.0.0",
     "mysql2": "^2.2.5",
     "read-pkg-up": "^7.0.1",

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/ui",
-  "version": "4.5.14",
+  "version": "4.5.15-RC.0",
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "keywords": [

+ 21 - 11
yarn.lock

@@ -7101,14 +7101,14 @@ depd@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
 
+depd@2.0.0, depd@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+
 depd@^1.1.2, depd@~1.1.1, depd@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
 
-depd@~2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
-
 deprecation@^2.0.0, deprecation@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
@@ -10179,16 +10179,16 @@ http-errors@1.7.3, http-errors@~1.7.2:
     statuses ">= 1.5.0 < 2"
     toidentifier "1.0.0"
 
-http-errors@^1.8.0, http-errors@~1.8.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507"
-  integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==
+http-errors@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+  integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
   dependencies:
-    depd "~1.1.2"
+    depd "2.0.0"
     inherits "2.0.4"
     setprototypeof "1.2.0"
-    statuses ">= 1.5.0 < 2"
-    toidentifier "1.0.0"
+    statuses "2.0.1"
+    toidentifier "1.0.1"
 
 http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1:
   version "4.0.1"
@@ -18845,6 +18845,11 @@ static-extend@^0.1.1:
     define-property "^0.2.5"
     object-copy "^0.1.0"
 
+statuses@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+  integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
 "statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
@@ -20166,6 +20171,11 @@ toidentifier@1.0.0:
   resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
   integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
 
+toidentifier@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+  integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
 tough-cookie@^2.3.3, tough-cookie@~2.5.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"