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

Merge branch 'master' into fix/cypress-error-10-30-50

jam411 3 лет назад
Родитель
Сommit
fd87197b2c
35 измененных файлов с 104 добавлено и 569 удалено
  1. 1 0
      package.json
  2. 0 1
      packages/app/_obsolete/src/client/nologin.jsx
  3. 1 1
      packages/app/cypress.config.ts
  4. 0 1
      packages/app/package.json
  5. 0 11
      packages/app/public/static/locales/en_US/admin.json
  6. 0 1
      packages/app/public/static/locales/en_US/translation.json
  7. 0 11
      packages/app/public/static/locales/ja_JP/admin.json
  8. 0 11
      packages/app/public/static/locales/zh_CN/admin.json
  9. 0 1
      packages/app/public/static/locales/zh_CN/translation.json
  10. 0 77
      packages/app/src/client/services/AdminBasicSecurityContainer.js
  11. 0 9
      packages/app/src/client/services/AdminGeneralSecurityContainer.js
  12. 0 42
      packages/app/src/components/Admin/Security/BasicSecuritySetting.jsx
  13. 0 139
      packages/app/src/components/Admin/Security/BasicSecuritySettingContents.jsx
  14. 4 13
      packages/app/src/components/Admin/Security/SecurityManagementContents.jsx
  15. 1 1
      packages/app/src/components/Layout/BasicLayout.tsx
  16. 0 4
      packages/app/src/components/Layout/NoLoginLayout.module.scss
  17. 1 2
      packages/app/src/components/LoginForm.tsx
  18. 7 3
      packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx
  19. 1 1
      packages/app/src/components/Navbar/PersonalDropdown.jsx
  20. 1 1
      packages/app/src/components/PageEditor/Cheatsheet.tsx
  21. 1 1
      packages/app/src/components/User/UserInfo.tsx
  22. 0 12
      packages/app/src/interfaces/activity.ts
  23. 25 0
      packages/app/src/migrations/20221219011829-remove-basic-auth-related-config.js
  24. 0 4
      packages/app/src/pages/admin/security.page.tsx
  25. 0 1
      packages/app/src/pages/login.page.tsx
  26. 0 1
      packages/app/src/server/crowi/index.js
  27. 0 3
      packages/app/src/server/models/config.ts
  28. 1 64
      packages/app/src/server/routes/apiv3/security-setting.js
  29. 0 1
      packages/app/src/server/routes/index.js
  30. 0 44
      packages/app/src/server/routes/login-passport.js
  31. 0 54
      packages/app/src/server/service/passport.ts
  32. 7 12
      packages/app/test/cypress/integration/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.spec.ts
  33. 14 12
      packages/app/test/cypress/integration/60-home/60-home--home.spec.ts
  34. 34 23
      packages/app/test/cypress/support/commands.ts
  35. 5 7
      yarn.lock

+ 1 - 0
package.json

@@ -62,6 +62,7 @@
     "@typescript-eslint/eslint-plugin": "^5.0.0",
     "@typescript-eslint/parser": "^5.0.0",
     "cypress": "^12.0.1",
+    "cypress-wait-until": "^1.7.2",
     "eslint": "^8.18.0",
     "eslint-config-next": "^12.1.6",
     "eslint-config-weseek": "^2.1.0",

+ 0 - 1
packages/app/_obsolete/src/client/nologin.jsx

@@ -52,7 +52,6 @@ if (loginFormElem) {
     twitter: loginFormElem.dataset.isTwitterAuthEnabled === 'true',
     saml: loginFormElem.dataset.isSamlAuthEnabled === 'true',
     oidc: loginFormElem.dataset.isOidcAuthEnabled === 'true',
-    basic: loginFormElem.dataset.isBasicAuthEnabled === 'true',
   };
 
   Object.assign(componentMappings, {

+ 1 - 1
packages/app/cypress.config.ts

@@ -16,6 +16,7 @@ export default defineConfig({
         return launchOptions;
       });
     },
+    defaultCommandTimeout: 10000,
   },
   fileServerFolder: 'test/cypress',
   fixturesFolder: 'test/cypress/fixtures',
@@ -25,5 +26,4 @@ export default defineConfig({
   viewportWidth: 1400,
   viewportHeight: 1024,
 
-  defaultCommandTimeout: 30000,
 });

+ 0 - 1
packages/app/package.json

@@ -146,7 +146,6 @@
     "passport": "^0.6.0",
     "passport-github": "^1.1.0",
     "passport-google-oauth20": "^2.0.0",
-    "passport-http": "^0.3.0",
     "passport-ldapauth": "^3.0.1",
     "passport-local": "^1.0.0",
     "passport-saml": "^3.2.0",

+ 0 - 11
packages/app/public/static/locales/en_US/admin.json

@@ -157,13 +157,6 @@
       "attr_based_login_control_rule_example2": "<h5>Example for escaping</h5>If you would like to use URL as a query value, escape the following:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
       "updated_saml": "Succeeded to update SAML setting"
     },
-    "Basic": {
-      "enable_basic": "Enable Basic",
-      "name": "Basic Authentication",
-      "desc_1": "Login with <code>username</code> in Authorization header.",
-      "desc_2": "User will be automatically generated if not exist.",
-      "updated_basic": "Succeeded to update Basic setting"
-    },
     "OAuth": {
       "enable_oidc": "Enable OIDC",
       "register": "Register for %s",
@@ -891,7 +884,6 @@
     "USER_LOGIN_WITH_TWITTER": "Login with Twitter",
     "USER_LOGIN_WITH_OIDC": "Login with OIDC",
     "USER_LOGIN_WITH_SAML": "Login with SAML",
-    "USER_LOGIN_WITH_BASIC": "Login with BASIC",
     "USER_LOGIN_FAILURE": "Login failure",
     "USER_LOGOUT": "Logout",
     "USER_FOGOT_PASSWORD": "Request password reset",
@@ -969,9 +961,6 @@
     "ADMIN_AUTH_OIDC_ENABLED": "Enable OIDC auth",
     "ADMIN_AUTH_OIDC_DISABLED": "Disable OIDC auth",
     "ADMIN_AUTH_OIDC_UPDATE": "Update OIDC settings",
-    "ADMIN_AUTH_BASIC_ENABLED": "Enable BASIC auth",
-    "ADMIN_AUTH_BASIC_DISABLED": "Disable BASIC auth",
-    "ADMIN_AUTH_BASIC_UPDATE": "Update BASIC auth settings",
     "ADMIN_AUTH_GOOGLE_ENABLED": "Enable Google auth",
     "ADMIN_AUTH_GOOGLE_DISABLED": "Disable Google auth",
     "ADMIN_AUTH_GOOGLE_UPDATE": "Update Google auth settings",

+ 0 - 1
packages/app/public/static/locales/en_US/translation.json

@@ -114,7 +114,6 @@
   "external_account_management": "External Account Management",
   "UserGroup": "UserGroup",
   "Basic Settings": "Basic Settings",
-  "Basic authentication": "Basic authentication",
   "The contents entered here will be shown in the header etc": "The contents entered here will be shown in the header etc",
   "Public": "Public",
   "Anyone with the link": "Anyone with the link",

+ 0 - 11
packages/app/public/static/locales/ja_JP/admin.json

@@ -165,13 +165,6 @@
       "attr_based_login_control_rule_exampl2": "<h5>エスケープの例</h5>ルールに URL を利用したい場合は、次のようにエスケープしてください:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
       "updated_saml": "Succeeded to update SAML setting"
     },
-    "Basic": {
-      "enable_basic": "Basic を有効にする",
-      "name": "Basic 認証",
-      "desc_1": "Authorization ヘッダに格納されている <code>username</code> でログインします。",
-      "desc_2": "ユーザーが存在しなかった場合は自動生成します。",
-      "updated_basic": "Basic認証 を更新しました"
-    },
     "OAuth": {
       "enable_oidc": "OIDC を有効にする",
       "register": "%sに登録",
@@ -899,7 +892,6 @@
     "USER_LOGIN_WITH_TWITTER": "Twitter 認証でログイン",
     "USER_LOGIN_WITH_OIDC": "OIDC 認証でログイン",
     "USER_LOGIN_WITH_SAML": "SAML 認証でログイン",
-    "USER_LOGIN_WITH_BASIC": "BASIC 認証でログイン",
     "USER_LOGIN_FAILURE": "ログイン失敗",
     "USER_LOGOUT": "ログアウト",
     "USER_FOGOT_PASSWORD": "パスワードリセットのリクエスト",
@@ -977,9 +969,6 @@
     "ADMIN_AUTH_OIDC_ENABLED": "OIDC 認証を有効",
     "ADMIN_AUTH_OIDC_DISABLED": "OIDC 認証を無効",
     "ADMIN_AUTH_OIDC_UPDATE": "OIDC 認証設定の更新",
-    "ADMIN_AUTH_BASIC_ENABLED": "BASIC 認証の有効",
-    "ADMIN_AUTH_BASIC_DISABLED": "BASIC 認証の無効",
-    "ADMIN_AUTH_BASIC_UPDATE": "BASIC 認証設定の更新",
     "ADMIN_AUTH_GOOGLE_ENABLED": "Google 認証の有効",
     "ADMIN_AUTH_GOOGLE_DISABLED": "Google 認証の無効",
     "ADMIN_AUTH_GOOGLE_UPDATE": "Google 認証設定の更新",

+ 0 - 11
packages/app/public/static/locales/zh_CN/admin.json

@@ -165,13 +165,6 @@
       "attr_based_login_control_rule_example2": "<h5>Example for escaping</h5>If you would like to use URL as a query value, escape the following:<br><code>http&#92;:&#92;/&#92;/schemas.example.com&#92;/ws&#92;/2005&#92;/05&#92;/identity&#92;/claims&#92;/emailaddress: &quot;myname@example.com&quot;</code>",
       "updated_saml": "Succeeded to update SAML setting"
 		},
-		"Basic": {
-			"enable_basic": "Enable Basic",
-			"name": "Basic Authentication",
-			"desc_1": "Login with <code>username</code> in Authorization header.",
-			"desc_2": "User will be automatically generated if not exist.",
-			"updated_basic": "Succeeded to update Basic setting"
-		},
 		"OAuth": {
 			"enable_oidc": "Enable OIDC",
 			"register": "Register for %s",
@@ -899,7 +892,6 @@
     "USER_LOGIN_WITH_TWITTER": "使用 Twitter 登录",
     "USER_LOGIN_WITH_OIDC": "使用 OIDC 登录",
     "USER_LOGIN_WITH_SAML": "使用 SAML 登录",
-    "USER_LOGIN_WITH_BASIC": "使用 BASIC 登录",
     "USER_LOGIN_FAILURE": "登录失败",
     "USER_LOGOUT": "注销",
     "USER_FOGOT_PASSWORD": "要求重置密码",
@@ -977,9 +969,6 @@
     "ADMIN_AUTH_OIDC_ENABLED": "启用 OIDC 身份验证",
     "ADMIN_AUTH_OIDC_DISABLED": "禁用 OIDC 身份验证",
     "ADMIN_AUTH_OIDC_UPDATE": "更新 OIDC 设置",
-    "ADMIN_AUTH_BASIC_ENABLED": "启用基本身份验证",
-    "ADMIN_AUTH_BASIC_DISABLED": "禁用基本身份验证",
-    "ADMIN_AUTH_BASIC_UPDATE": "更新基本认证设置",
     "ADMIN_AUTH_GOOGLE_ENABLED": "启用谷歌身份验证",
     "ADMIN_AUTH_GOOGLE_DISABLED": "禁用谷歌身份验证",
     "ADMIN_AUTH_GOOGLE_UPDATE": "更新谷歌授权设置",

+ 0 - 1
packages/app/public/static/locales/zh_CN/translation.json

@@ -121,7 +121,6 @@
   "UserGroup": "用户组",
   "ChildUserGroup": "儿童用户组",
 	"Basic Settings": "基础设置",
-	"Basic authentication": "基本身份验证",
 	"The contents entered here will be shown in the header etc": "此处输入的内容将显示在标题等中",
 	"Public": "公共",
 	"Anyone with the link": "任何人",

+ 0 - 77
packages/app/src/client/services/AdminBasicSecurityContainer.js

@@ -1,77 +0,0 @@
-import { isServer } from '@growi/core';
-import { Container } from 'unstated';
-
-import loggerFactory from '~/utils/logger';
-import { removeNullPropertyFromObject } from '~/utils/object-utils';
-
-import { apiv3Get, apiv3Put } from '../util/apiv3-client';
-
-const logger = loggerFactory('growi:security:AdminTwitterSecurityContainer');
-
-/**
- * Service container for admin security page (BasicSecuritySetting.jsx)
- * @extends {Container} unstated Container
- */
-export default class AdminBasicSecurityContainer extends Container {
-
-  constructor() {
-    super();
-
-    if (isServer()) {
-      return;
-    }
-
-    this.state = {
-      isSameUsernameTreatedAsIdenticalUser: false,
-    };
-  }
-
-  /**
-   * retrieve security data
-   */
-  async retrieveSecurityData() {
-    try {
-      const response = await apiv3Get('/security-setting/');
-      const { basicAuth } = response.data.securityParams;
-      this.setState({
-        isSameUsernameTreatedAsIdenticalUser: basicAuth.isSameUsernameTreatedAsIdenticalUser,
-      });
-    }
-    catch (err) {
-      this.setState({ retrieveError: err });
-      logger.error(err);
-      throw new Error('Failed to fetch data');
-    }
-  }
-
-  /**
-   * Workaround for the mangling in production build to break constructor.name
-   */
-  static getClassName() {
-    return 'AdminBasicSecurityContainer';
-  }
-
-  /**
-   * Switch isSameUsernameTreatedAsIdenticalUser
-   */
-  switchIsSameUsernameTreatedAsIdenticalUser() {
-    this.setState({ isSameUsernameTreatedAsIdenticalUser: !this.state.isSameUsernameTreatedAsIdenticalUser });
-  }
-
-  /**
-   * Update basicSetting
-   */
-  async updateBasicSetting() {
-    let requestParams = { isSameUsernameTreatedAsIdenticalUser: this.state.isSameUsernameTreatedAsIdenticalUser };
-
-    requestParams = await removeNullPropertyFromObject(requestParams);
-    const response = await apiv3Put('/security-setting/basic', requestParams);
-    const { securitySettingParams } = response.data;
-
-    this.setState({
-      isSameUsernameTreatedAsIdenticalUser: securitySettingParams.isSameUsernameTreatedAsIdenticalUser,
-    });
-    return response;
-  }
-
-}

+ 0 - 9
packages/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -42,7 +42,6 @@ export default class AdminGeneralSecurityContainer extends Container {
       isLdapEnabled: false,
       isSamlEnabled: false,
       isOidcEnabled: false,
-      isBasicEnabled: false,
       isGoogleEnabled: false,
       isGitHubEnabled: false,
       isTwitterEnabled: false,
@@ -82,7 +81,6 @@ export default class AdminGeneralSecurityContainer extends Container {
       isLdapEnabled: generalAuth.isLdapEnabled,
       isSamlEnabled: generalAuth.isSamlEnabled,
       isOidcEnabled: generalAuth.isOidcEnabled,
-      isBasicEnabled: generalAuth.isBasicEnabled,
       isGoogleEnabled: generalAuth.isGoogleEnabled,
       isGitHubEnabled: generalAuth.isGitHubEnabled,
       isTwitterEnabled: generalAuth.isTwitterEnabled,
@@ -318,13 +316,6 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.switchAuthentication('isOidcEnabled', 'oidc');
   }
 
-  /**
-   * Switch Basic enabled
-   */
-  async switchIsBasicEnabled() {
-    this.switchAuthentication('isBasicEnabled', 'basic');
-  }
-
   /**
    * Switch GoogleOAuth enabled
    */

+ 0 - 42
packages/app/src/components/Admin/Security/BasicSecuritySetting.jsx

@@ -1,42 +0,0 @@
-import React, { useEffect, useCallback } from 'react';
-
-import PropTypes from 'prop-types';
-
-import AdminBasicSecurityContainer from '~/client/services/AdminBasicSecurityContainer';
-import { toastError } from '~/client/util/apiNotification';
-import { toArrayIfNot } from '~/utils/array-utils';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-import BasicSecurityManagementContents from './BasicSecuritySettingContents';
-
-const BasicSecurityManagement = (props) => {
-  const { adminBasicSecurityContainer } = props;
-
-  const fetchBasicSecuritySettingsData = useCallback(async() => {
-    try {
-      await adminBasicSecurityContainer.retrieveSecurityData();
-    }
-    catch (err) {
-      const errs = toArrayIfNot(err);
-      toastError(errs);
-    }
-  }, [adminBasicSecurityContainer]);
-
-  useEffect(() => {
-    fetchBasicSecuritySettingsData();
-  }, [adminBasicSecurityContainer, fetchBasicSecuritySettingsData]);
-
-
-  return <BasicSecurityManagementContents />;
-};
-
-BasicSecurityManagement.propTypes = {
-  adminBasicSecurityContainer: PropTypes.instanceOf(AdminBasicSecurityContainer).isRequired,
-};
-
-const BasicSecurityManagementWithUnstatedContainer = withUnstatedContainers(BasicSecurityManagement, [
-  AdminBasicSecurityContainer,
-]);
-
-export default BasicSecurityManagementWithUnstatedContainer;

+ 0 - 139
packages/app/src/components/Admin/Security/BasicSecuritySettingContents.jsx

@@ -1,139 +0,0 @@
-/* eslint-disable react/no-danger */
-import React from 'react';
-
-import PropTypes from 'prop-types';
-import { useTranslation } from 'next-i18next';
-
-import AdminBasicSecurityContainer from '~/client/services/AdminBasicSecurityContainer';
-import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-class BasicSecurityManagementContents extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickSubmit = this.onClickSubmit.bind(this);
-  }
-
-  async onClickSubmit() {
-    const { t, adminBasicSecurityContainer, adminGeneralSecurityContainer } = this.props;
-
-    try {
-      await adminBasicSecurityContainer.updateBasicSetting();
-      await adminGeneralSecurityContainer.retrieveSetupStratedies();
-      toastSuccess(t('security_settings.Basic.updated_basic'));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-  render() {
-    const { t, adminGeneralSecurityContainer, adminBasicSecurityContainer } = this.props;
-    const { isBasicEnabled } = adminGeneralSecurityContainer.state;
-
-    return (
-      <React.Fragment>
-
-        <h2 className="alert-anchor border-bottom">
-          { t('security_settings.Basic.name') }
-        </h2>
-
-        {adminBasicSecurityContainer.state.retrieveError != null && (
-          <div className="alert alert-danger">
-            <p>{t('Error occurred')} : {adminBasicSecurityContainer.state.retrieveError}</p>
-          </div>
-        )}
-
-        <div className="form-group row">
-          <div className="col-6 offset-3">
-            <div className="custom-control custom-switch custom-checkbox-success">
-              <input
-                id="isBasicEnabled"
-                className="custom-control-input"
-                type="checkbox"
-                checked={adminGeneralSecurityContainer.state.isBasicEnabled}
-                onChange={() => { adminGeneralSecurityContainer.switchIsBasicEnabled() }}
-              />
-              <label className="custom-control-label" htmlFor="isBasicEnabled">
-                { t('security_settings.Basic.enable_basic') }
-              </label>
-            </div>
-            <p className="form-text text-muted">
-              <small>
-                <span dangerouslySetInnerHTML={{ __html: t('security_settings.Basic.desc_1') }} /><br />
-                { t('security_settings.Basic.desc_2')}
-              </small>
-            </p>
-            {(!adminGeneralSecurityContainer.state.setupStrategies.includes('basic') && isBasicEnabled)
-            && <div className="badge badge-warning">{t('security_settings.setup_is_not_yet_complete')}</div>}
-          </div>
-        </div>
-
-        {isBasicEnabled && (
-          <React.Fragment>
-            <div className="row mb-5">
-              <div className="offset-md-3 col-md-6">
-                <div className="custom-control custom-checkbox custom-checkbox-success">
-                  <input
-                    id="bindByEmail-basic"
-                    className="custom-control-input"
-                    type="checkbox"
-                    checked={adminBasicSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser || false}
-                    onChange={() => { adminBasicSecurityContainer.switchIsSameUsernameTreatedAsIdenticalUser() }}
-                  />
-                  <label
-                    className="custom-control-label"
-                    htmlFor="bindByEmail-basic"
-                    dangerouslySetInnerHTML={{ __html: t('security_settings.Treat username matching as identical', 'username') }}
-                  />
-                </div>
-                <p className="form-text text-muted">
-                  <small dangerouslySetInnerHTML={{ __html: t('security_settings.Treat username matching as identical_warn', 'username') }} />
-                </p>
-              </div>
-            </div>
-
-            <div className="row my-3">
-              <div className="offset-4 col-5">
-                <button
-                  type="button"
-                  className="btn btn-primary"
-                  disabled={adminBasicSecurityContainer.state.retrieveError != null}
-                  onClick={this.onClickSubmit}
-                >
-                  {t('Update')}
-                </button>
-              </div>
-            </div>
-
-          </React.Fragment>
-        )}
-
-      </React.Fragment>
-    );
-  }
-
-}
-
-BasicSecurityManagementContents.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
-  adminBasicSecurityContainer: PropTypes.instanceOf(AdminBasicSecurityContainer).isRequired,
-};
-
-const BasicSecurityManagementContentsWrapperFC = (props) => {
-  const { t } = useTranslation('admin');
-
-  return <BasicSecurityManagementContents t={t} {...props} />;
-};
-
-const BasicSecurityManagementContentsWrapper = withUnstatedContainers(BasicSecurityManagementContentsWrapperFC, [
-  AdminGeneralSecurityContainer,
-  AdminBasicSecurityContainer,
-]);
-
-export default BasicSecurityManagementContentsWrapper;

+ 4 - 13
packages/app/src/components/Admin/Security/SecurityManagementContents.jsx

@@ -6,7 +6,6 @@ import { TabContent, TabPane } from 'reactstrap';
 
 import CustomNav from '../../CustomNavigation/CustomNav';
 
-import BasicSecuritySetting from './BasicSecuritySetting';
 import FacebookSecuritySetting from './FacebookSecuritySetting';
 import GitHubSecuritySetting from './GitHubSecuritySetting';
 import GoogleSecuritySetting from './GoogleSecuritySetting';
@@ -51,30 +50,25 @@ const SecurityManagementContents = () => {
         i18n: 'OIDC',
         index: 3,
       },
-      passport_basic: {
-        Icon: () => <i className="fa fa-lock" />,
-        i18n: 'BASIC',
-        index: 4,
-      },
       passport_google: {
         Icon: () => <i className="fa fa-google" />,
         i18n: 'Google',
-        index: 5,
+        index: 4,
       },
       passport_github: {
         Icon: () => <i className="fa fa-github" />,
         i18n: 'GitHub',
-        index: 6,
+        index: 5,
       },
       passport_twitter: {
         Icon: () => <i className="fa fa-twitter" />,
         i18n: 'Twitter',
-        index: 7,
+        index: 6,
       },
       passport_facebook: {
         Icon: () => <i className="fa fa-facebook" />,
         i18n: '(TBD) Facebook',
-        index: 8,
+        index: 7,
       },
     };
   }, []);
@@ -126,9 +120,6 @@ const SecurityManagementContents = () => {
           <TabPane tabId="passport_oidc">
             {activeComponents.has('passport_oidc') && <OidcSecuritySetting />}
           </TabPane>
-          <TabPane tabId="passport_basic">
-            {activeComponents.has('passport_basic') && <BasicSecuritySetting />}
-          </TabPane>
           <TabPane tabId="passport_google">
             {activeComponents.has('passport_google') && <GoogleSecuritySetting />}
           </TabPane>

+ 1 - 1
packages/app/src/components/Layout/BasicLayout.tsx

@@ -46,7 +46,7 @@ export const BasicLayout = ({
         <GrowiNavbar />
 
         <div className="page-wrapper d-flex d-print-block">
-          <div className="grw-sidebar-wrapper">
+          <div className="grw-sidebar-wrapper" data-testid="grw-sidebar-wrapper">
             <Sidebar />
           </div>
 

+ 0 - 4
packages/app/src/components/Layout/NoLoginLayout.module.scss

@@ -112,10 +112,6 @@
       rgba(#55a79a, 0.4),
       $gray-700,
     ),
-    'basic': (
-      rgba(#24292e, 0.4),
-      $gray-700,
-    ),
   );
 
   @each $label, $colors in $btn-fill-colors {

+ 1 - 2
packages/app/src/components/LoginForm.tsx

@@ -220,7 +220,6 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
       twitter: 'twitter',
       oidc: 'openid',
       saml: 'key',
-      basic: 'lock',
     };
 
     return (
@@ -497,7 +496,7 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
   }
 
   return (
-    <div className="noLogin-dialog mx-auto" id="noLogin-dialog">
+    <div className="noLogin-dialog mx-auto" id="noLogin-dialog" data-testid="login-form">
       <div className="row mx-0">
         <div className="col-12">
           <ReactCardFlip isFlipped={isRegistering} flipDirection="horizontal" cardZIndex="3">

+ 7 - 3
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -191,13 +191,15 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
   const { data: shareLinkId } = useShareLinkId();
   const { data: currentPage, mutate: mutateCurrentPage } = useSWRxCurrentPage(shareLinkId ?? undefined);
 
+  const { data: currentPathname } = useCurrentPathname();
+  const isSharedPage = pagePathUtils.isSharedPage(currentPathname ?? '');
+
   const revision = currentPage?.revision;
   const revisionId = (revision != null && isPopulated(revision)) ? revision._id : undefined;
 
   const { data: isDrawerMode } = useDrawerMode();
   const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
   const { data: pageId } = useCurrentPageId();
-  const { data: currentPathname } = useCurrentPathname();
   const { data: currentUser } = useCurrentUser();
   const { data: isNotFound } = useIsNotFound();
   const { data: isGuestUser } = useIsGuestUser();
@@ -209,8 +211,10 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
   const { data: isAbleToShowPageEditorModeManager } = useIsAbleToShowPageEditorModeManager();
   const { data: isAbleToShowPageAuthors } = useIsAbleToShowPageAuthors();
 
-  const { mutate: mutateSWRTagsInfo, data: tagsInfoData } = useSWRxTagsInfo(currentPage?._id);
-  const { data: tagsForEditors, mutate: mutatePageTagsForEditors, sync: syncPageTagsForEditors } = usePageTagsForEditors(currentPage?._id);
+  const { mutate: mutateSWRTagsInfo, data: tagsInfoData } = useSWRxTagsInfo(!isSharedPage ? currentPage?._id : undefined);
+
+  // eslint-disable-next-line max-len
+  const { data: tagsForEditors, mutate: mutatePageTagsForEditors, sync: syncPageTagsForEditors } = usePageTagsForEditors(!isSharedPage ? currentPage?._id : undefined);
 
   const { open: openDuplicateModal } = usePageDuplicateModal();
   const { open: openRenameModal } = usePageRenameModal();

+ 1 - 1
packages/app/src/components/Navbar/PersonalDropdown.jsx

@@ -34,7 +34,7 @@ const PersonalDropdown = () => {
       {/* Button */}
       {/* remove .dropdown-toggle for hide caret */}
       {/* See https://stackoverflow.com/a/44577512/13183572 */}
-      <button className="bg-transparent border-0 nav-link" type="button" ref={buttonRef} data-toggle="dropdown">
+      <button className="bg-transparent border-0 nav-link" type="button" ref={buttonRef} data-toggle="dropdown" data-testid="personal-dropdown-button">
         <UserPicture user={user} noLink noTooltip /><span className="ml-1 d-none d-lg-inline-block">&nbsp;{user.name}</span>
       </button>
 

+ 1 - 1
packages/app/src/components/PageEditor/Cheatsheet.tsx

@@ -17,7 +17,7 @@ export const Cheatsheet = (): JSX.Element => {
   const codeBlockStr = 'text\n\ntext';
   const lineBlockStr = 'text\ntext';
   const typographyStr = `*${t('sandbox.italics')}*\n**${t('sandbox.bold')}**\n***${t('sandbox.italic_bold')}***\n~~${t('sandbox.strikethrough')}~~`;
-  const linkStr = '[Google](https://www.google.co.jp/)\n[/Page1/ChildPage1]';
+  const linkStr = '[Google](https://www.google.co.jp/)';
   const codeHighlightStr = '```javascript:index.js\nwriteCode();\n```';
 
   // Right Side

+ 1 - 1
packages/app/src/components/User/UserInfo.tsx

@@ -19,7 +19,7 @@ export const UserInfo = (props: UserInfoProps): JSX.Element => {
   }
 
   return (
-    <div className={`${styles['grw-users-info']} grw-users-info d-flex align-items-center d-edit-none mb-5 pb-3 border-bottom`}>
+    <div className={`${styles['grw-users-info']} grw-users-info d-flex align-items-center d-edit-none mb-5 pb-3 border-bottom`} data-testid="grw-users-info">
       <UserPicture user={author} />
       <div className="users-meta">
         <h1 className="user-page-name">

+ 0 - 12
packages/app/src/interfaces/activity.ts

@@ -16,7 +16,6 @@ const ACTION_USER_LOGIN_WITH_GITHUB = 'USER_LOGIN_WITH_GITHUB';
 const ACTION_USER_LOGIN_WITH_TWITTER = 'USER_LOGIN_WITH_TWITTER';
 const ACTION_USER_LOGIN_WITH_OIDC = 'USER_LOGIN_WITH_OIDC';
 const ACTION_USER_LOGIN_WITH_SAML = 'USER_LOGIN_WITH_SAML';
-const ACTION_USER_LOGIN_WITH_BASIC = 'USER_LOGIN_WITH_BASIC';
 const ACTION_USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE';
 const ACTION_USER_LOGOUT = 'USER_LOGOUT';
 const ACTION_USER_FOGOT_PASSWORD = 'USER_FOGOT_PASSWORD';
@@ -94,9 +93,6 @@ const ACTION_ADMIN_AUTH_SAML_UPDATE = 'ADMIN_AUTH_SAML_UPDATE';
 const ACTION_ADMIN_AUTH_OIDC_ENABLED = 'ADMIN_AUTH_OIDC_ENABLED';
 const ACTION_ADMIN_AUTH_OIDC_DISABLED = 'ADMIN_AUTH_OIDC_DISABLED';
 const ACTION_ADMIN_AUTH_OIDC_UPDATE = 'ADMIN_AUTH_OIDC_UPDATE';
-const ACTION_ADMIN_AUTH_BASIC_ENABLED = 'ADMIN_AUTH_BASIC_ENABLED';
-const ACTION_ADMIN_AUTH_BASIC_DISABLED = 'ADMIN_AUTH_BASIC_DISABLED';
-const ACTION_ADMIN_AUTH_BASIC_UPDATE = 'ADMIN_AUTH_BASIC_UPDATE';
 const ACTION_ADMIN_AUTH_GOOGLE_ENABLED = 'ADMIN_AUTH_GOOGLE_ENABLED';
 const ACTION_ADMIN_AUTH_GOOGLE_DISABLED = 'ADMIN_AUTH_GOOGLE_DISABLED';
 const ACTION_ADMIN_AUTH_GOOGLE_UPDATE = 'ADMIN_AUTH_GOOGLE_UPDATE';
@@ -199,7 +195,6 @@ export const SupportedAction = {
   ACTION_USER_LOGIN_WITH_TWITTER,
   ACTION_USER_LOGIN_WITH_OIDC,
   ACTION_USER_LOGIN_WITH_SAML,
-  ACTION_USER_LOGIN_WITH_BASIC,
   ACTION_USER_LOGIN_FAILURE,
   ACTION_USER_LOGOUT,
   ACTION_USER_FOGOT_PASSWORD,
@@ -277,9 +272,6 @@ export const SupportedAction = {
   ACTION_ADMIN_AUTH_OIDC_ENABLED,
   ACTION_ADMIN_AUTH_OIDC_DISABLED,
   ACTION_ADMIN_AUTH_OIDC_UPDATE,
-  ACTION_ADMIN_AUTH_BASIC_ENABLED,
-  ACTION_ADMIN_AUTH_BASIC_DISABLED,
-  ACTION_ADMIN_AUTH_BASIC_UPDATE,
   ACTION_ADMIN_AUTH_GOOGLE_ENABLED,
   ACTION_ADMIN_AUTH_GOOGLE_DISABLED,
   ACTION_ADMIN_AUTH_GOOGLE_UPDATE,
@@ -383,7 +375,6 @@ export const SmallActionGroup = {
   ACTION_USER_LOGIN_WITH_TWITTER,
   ACTION_USER_LOGIN_WITH_OIDC,
   ACTION_USER_LOGIN_WITH_SAML,
-  ACTION_USER_LOGIN_WITH_BASIC,
   ACTION_USER_LOGIN_FAILURE,
   ACTION_USER_LOGOUT,
   ACTION_PAGE_CREATE,
@@ -468,9 +459,6 @@ export const LargeActionGroup = {
   ACTION_ADMIN_AUTH_OIDC_ENABLED,
   ACTION_ADMIN_AUTH_OIDC_DISABLED,
   ACTION_ADMIN_AUTH_OIDC_UPDATE,
-  ACTION_ADMIN_AUTH_BASIC_ENABLED,
-  ACTION_ADMIN_AUTH_BASIC_DISABLED,
-  ACTION_ADMIN_AUTH_BASIC_UPDATE,
   ACTION_ADMIN_AUTH_GOOGLE_ENABLED,
   ACTION_ADMIN_AUTH_GOOGLE_DISABLED,
   ACTION_ADMIN_AUTH_GOOGLE_UPDATE,

+ 25 - 0
packages/app/src/migrations/20221219011829-remove-basic-auth-related-config.js

@@ -0,0 +1,25 @@
+// eslint-disable-next-line import/no-named-as-default
+import Config from '~/server/models/config';
+import { getMongoUri, mongoOptions } from '~/server/util/mongoose-utils';
+import loggerFactory from '~/utils/logger';
+
+
+const logger = loggerFactory('growi:remove-basic-auth-related-config');
+
+const mongoose = require('mongoose');
+
+module.exports = {
+  async up() {
+    logger.info('Apply migration');
+    mongoose.connect(getMongoUri(), mongoOptions);
+
+    await Config.findOneAndDelete({ key: 'security:passport-basic:isEnabled' });
+    await Config.findOneAndDelete({ key: 'security:passport-basic:isSameUsernameTreatedAsIdenticalUser' });
+
+    logger.info('Migration has successfully applied');
+  },
+
+  async down() {
+    // No rollback
+  },
+};

+ 0 - 4
packages/app/src/pages/admin/security.page.tsx

@@ -6,8 +6,6 @@ import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import { Container, Provider } from 'unstated';
 
-
-import AdminBasicSecurityContainer from '~/client/services/AdminBasicSecurityContainer';
 import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
 import AdminGitHubSecurityContainer from '~/client/services/AdminGitHubSecurityContainer';
 import AdminGoogleSecurityContainer from '~/client/services/AdminGoogleSecurityContainer';
@@ -50,7 +48,6 @@ const AdminSecuritySettingsPage: NextPage<Props> = (props) => {
       const adminLdapSecurityContainer = new AdminLdapSecurityContainer();
       const adminSamlSecurityContainer = new AdminSamlSecurityContainer();
       const adminOidcSecurityContainer = new AdminOidcSecurityContainer();
-      const adminBasicSecurityContainer = new AdminBasicSecurityContainer();
       const adminGoogleSecurityContainer = new AdminGoogleSecurityContainer();
       const adminGitHubSecurityContainer = new AdminGitHubSecurityContainer();
       const adminTwitterSecurityContainer = new AdminTwitterSecurityContainer();
@@ -61,7 +58,6 @@ const AdminSecuritySettingsPage: NextPage<Props> = (props) => {
         adminLdapSecurityContainer,
         adminSamlSecurityContainer,
         adminOidcSecurityContainer,
-        adminBasicSecurityContainer,
         adminGoogleSecurityContainer,
         adminGitHubSecurityContainer,
         adminTwitterSecurityContainer,

+ 0 - 1
packages/app/src/pages/login.page.tsx

@@ -87,7 +87,6 @@ function injectEnabledStrategies(context: GetServerSidePropsContext, props: Prop
     twitter: configManager.getConfig('crowi', 'security:passport-twitter:isEnabled'),
     saml: configManager.getConfig('crowi', 'security:passport-saml:isEnabled'),
     oidc: configManager.getConfig('crowi', 'security:passport-oidc:isEnabled'),
-    basic: configManager.getConfig('crowi', 'security:passport-basic:isEnabled'),
   };
 
   props.enabledStrategies = enabledStrategies;

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

@@ -354,7 +354,6 @@ Crowi.prototype.setupPassport = async function() {
     this.passportService.setupStrategyById('ldap');
     this.passportService.setupStrategyById('saml');
     this.passportService.setupStrategyById('oidc');
-    this.passportService.setupStrategyById('basic');
     this.passportService.setupStrategyById('google');
     this.passportService.setupStrategyById('github');
     this.passportService.setupStrategyById('twitter');

+ 0 - 3
packages/app/src/server/models/config.ts

@@ -102,9 +102,6 @@ export const defaultCrowiConfigs: { [key: string]: any } = {
 
   'security:passport-oidc:isEnabled' : false,
 
-  'security:passport-basic:isEnabled' : false,
-  'security:passport-basic:isSameUsernameTreatedAsIdenticalUser': false,
-
   'aws:s3Bucket'          : 'growi',
   'aws:s3Region'          : 'ap-northeast-1',
   'aws:s3AccessKeyId'     : undefined,

+ 1 - 64
packages/app/src/server/routes/apiv3/security-setting.js

@@ -34,7 +34,7 @@ const validator = {
   authenticationSetting: [
     body('isEnabled').if(value => value != null).isBoolean(),
     body('authId').isString().isIn([
-      'local', 'ldap', 'saml', 'oidc', 'basic', 'google', 'github', 'twitter',
+      'local', 'ldap', 'saml', 'oidc', 'google', 'github', 'twitter',
     ]),
   ],
   localSetting: [
@@ -91,9 +91,6 @@ const validator = {
     body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
     body('isSameEmailTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
   ],
-  basicAuth: [
-    body('isSameUsernameTreatedAsIdenticalUser').if(value => value != null).isBoolean(),
-  ],
   googleOAuth: [
     body('googleClientId').if(value => value != null).isString(),
     body('googleClientSecret').if(value => value != null).isString(),
@@ -291,12 +288,6 @@ const validator = {
  *          isSameEmailTreatedAsIdenticalUser:
  *            type: boolean
  *            description: local account automatically linked the email matched
- *      BasicAuthSetting:
- *        type: object
- *        properties:
- *          isSameUsernameTreatedAsIdenticalUser:
- *            type: boolean
- *            description: local account automatically linked the email matched
  *      GitHubOAuthSetting:
  *        type: object
  *        properties:
@@ -398,7 +389,6 @@ module.exports = (crowi) => {
         isLdapEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-ldap:isEnabled'),
         isSamlEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-saml:isEnabled'),
         isOidcEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-oidc:isEnabled'),
-        isBasicEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-basic:isEnabled'),
         isGoogleEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-google:isEnabled'),
         isGitHubEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-github:isEnabled'),
         isTwitterEnabled: await crowi.configManager.getConfig('crowi', 'security:passport-twitter:isEnabled'),
@@ -461,9 +451,6 @@ module.exports = (crowi) => {
         isSameUsernameTreatedAsIdenticalUser: await crowi.configManager.getConfig('crowi', 'security:passport-oidc:isSameUsernameTreatedAsIdenticalUser'),
         isSameEmailTreatedAsIdenticalUser: await crowi.configManager.getConfig('crowi', 'security:passport-oidc:isSameEmailTreatedAsIdenticalUser'),
       },
-      basicAuth: {
-        isSameUsernameTreatedAsIdenticalUser: await crowi.configManager.getConfig('crowi', 'security:passport-basic:isSameUsernameTreatedAsIdenticalUser'),
-      },
       googleOAuth: {
         googleClientId: await crowi.configManager.getConfig('crowi', 'security:passport-google:clientId'),
         googleClientSecret: await crowi.configManager.getConfig('crowi', 'security:passport-google:clientSecret'),
@@ -562,13 +549,6 @@ module.exports = (crowi) => {
           }
           parameters.action = SupportedAction.ACTION_ADMIN_AUTH_OIDC_DISABLED;
           break;
-        case 'basic':
-          if (isEnabled) {
-            parameters.action = SupportedAction.ACTION_ADMIN_AUTH_BASIC_ENABLED;
-            break;
-          }
-          parameters.action = SupportedAction.ACTION_ADMIN_AUTH_BASIC_DISABLED;
-          break;
         case 'google':
           if (isEnabled) {
             parameters.action = SupportedAction.ACTION_ADMIN_AUTH_GOOGLE_ENABLED;
@@ -1100,49 +1080,6 @@ module.exports = (crowi) => {
     }
   });
 
-  /**
-   * @swagger
-   *
-   *    /_api/v3/security-setting/basic:
-   *      put:
-   *        tags: [SecuritySetting, apiv3]
-   *        description: Update basic
-   *        requestBody:
-   *          required: true
-   *          content:
-   *            application/json:
-   *              schema:
-   *                $ref: '#/components/schemas/BasicAuthSetting'
-   *        responses:
-   *          200:
-   *            description: Succeeded to update basic
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  $ref: '#/components/schemas/BasicAuthSetting'
-   */
-  router.put('/basic', loginRequiredStrictly, adminRequired, addActivity, validator.basicAuth, apiV3FormValidator, async(req, res) => {
-    const requestParams = {
-      'security:passport-basic:isSameUsernameTreatedAsIdenticalUser': req.body.isSameUsernameTreatedAsIdenticalUser,
-    };
-
-    try {
-      await updateAndReloadStrategySettings('basic', requestParams);
-
-      const securitySettingParams = {
-        isSameUsernameTreatedAsIdenticalUser: await crowi.configManager.getConfig('crowi', 'security:passport-basic:isSameUsernameTreatedAsIdenticalUser'),
-      };
-      const parameters = { action: SupportedAction.ACTION_ADMIN_AUTH_BASIC_UPDATE };
-      activityEvent.emit('update', res.locals.activity._id, parameters);
-      return res.apiv3({ securitySettingParams });
-    }
-    catch (err) {
-      const msg = 'Error occurred in updating basicAuth';
-      logger.error('Error', err);
-      return res.apiv3Err(new ErrorV3(msg, 'update-basicOAuth-failed'));
-    }
-  });
-
   /**
    * @swagger
    *

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

@@ -96,7 +96,6 @@ module.exports = function(crowi, app) {
   app.get('/passport/twitter'                     , loginPassport.loginWithTwitter, loginPassport.loginFailureForExternalAccount);
   app.get('/passport/oidc'                        , loginPassport.loginWithOidc, loginPassport.loginFailureForExternalAccount);
   app.get('/passport/saml'                        , loginPassport.loginWithSaml, loginPassport.loginFailureForExternalAccount);
-  app.get('/passport/basic'                       , loginPassport.loginWithBasic, loginPassport.loginFailureForExternalAccount);
   app.get('/passport/google/callback'             , loginPassport.loginPassportGoogleCallback   , loginPassport.loginFailureForExternalAccount);
   app.get('/passport/github/callback'             , loginPassport.loginPassportGitHubCallback   , loginPassport.loginFailureForExternalAccount);
   app.get('/passport/twitter/callback'            , loginPassport.loginPassportTwitterCallback  , loginPassport.loginFailureForExternalAccount);

+ 0 - 44
packages/app/src/server/routes/login-passport.js

@@ -626,49 +626,6 @@ module.exports = function(crowi, app) {
     });
   };
 
-  /**
-   * middleware that login with BasicStrategy
-   * @param {*} req
-   * @param {*} res
-   * @param {*} next
-   */
-  const loginWithBasic = async(req, res, next) => {
-    if (!passportService.isBasicStrategySetup) {
-      debug('BasicStrategy has not been set up');
-      const error = new ExternalAccountLoginError('message.strategy_has_not_been_set_up', { strategy: 'Basic' });
-      return next(error);
-    }
-
-    const providerId = 'basic';
-    const strategyName = 'basic';
-    let userId;
-
-    try {
-      userId = await promisifiedPassportAuthentication(strategyName, req, res);
-    }
-    catch (err) {
-      return next(new ExternalAccountLoginError(err.message));
-    }
-
-    const userInfo = {
-      id: userId,
-      username: userId,
-      name: userId,
-    };
-
-    const externalAccount = await getOrCreateUser(req, res, userInfo, providerId);
-    if (!externalAccount) {
-      return next(new ExternalAccountLoginError('message.sign_in_failure'));
-    }
-
-    const user = await externalAccount.getPopulatedUser();
-    await req.logIn(user, (err) => {
-      if (err) { debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
-
-      return loginSuccessHandler(req, res, user, SupportedAction.ACTION_USER_LOGIN_WITH_BASIC, true);
-    });
-  };
-
   return {
     cannotLoginErrorHadnler,
     loginFailure,
@@ -681,7 +638,6 @@ module.exports = function(crowi, app) {
     loginWithTwitter,
     loginWithOidc,
     loginWithSaml,
-    loginWithBasic,
     loginPassportGoogleCallback,
     loginPassportGitHubCallback,
     loginPassportTwitterCallback,

+ 0 - 54
packages/app/src/server/service/passport.ts

@@ -7,7 +7,6 @@ import pRetry from 'p-retry';
 import passport from 'passport';
 import { Strategy as GitHubStrategy } from 'passport-github';
 import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
-import { BasicStrategy } from 'passport-http';
 import LdapStrategy from 'passport-ldapauth';
 import { Strategy as LocalStrategy } from 'passport-local';
 import { Profile, Strategy as SamlStrategy, VerifiedCallback } from 'passport-saml';
@@ -76,11 +75,6 @@ class PassportService implements S2sMessageHandlable {
    */
   isSamlStrategySetup = false;
 
-  /**
-   * the flag whether BasicStrategy is set up successfully
-   */
-  isBasicStrategySetup = false;
-
   /**
    * the flag whether serializer/deserializer are set up successfully
    */
@@ -115,10 +109,6 @@ class PassportService implements S2sMessageHandlable {
       setup: 'setupOidcStrategy',
       reset: 'resetOidcStrategy',
     },
-    basic: {
-      setup: 'setupBasicStrategy',
-      reset: 'resetBasicStrategy',
-    },
     google: {
       setup: 'setupGoogleStrategy',
       reset: 'resetGoogleStrategy',
@@ -193,7 +183,6 @@ class PassportService implements S2sMessageHandlable {
     if (this.isLdapStrategySetup) { setupStrategies.push('ldap') }
     if (this.isSamlStrategySetup) { setupStrategies.push('saml') }
     if (this.isOidcStrategySetup) { setupStrategies.push('oidc') }
-    if (this.isBasicStrategySetup) { setupStrategies.push('basic') }
     if (this.isGoogleStrategySetup) { setupStrategies.push('google') }
     if (this.isGitHubStrategySetup) { setupStrategies.push('github') }
     if (this.isTwitterStrategySetup) { setupStrategies.push('twitter') }
@@ -991,49 +980,6 @@ class PassportService implements S2sMessageHandlable {
     return result;
   }
 
-  /**
-   * reset BasicStrategy
-   *
-   * @memberof PassportService
-   */
-  resetBasicStrategy() {
-    logger.debug('BasicStrategy: reset');
-    passport.unuse('basic');
-    this.isBasicStrategySetup = false;
-  }
-
-  /**
-   * setup BasicStrategy
-   *
-   * @memberof PassportService
-   */
-  setupBasicStrategy() {
-
-    this.resetBasicStrategy();
-
-    const configManager = this.crowi.configManager;
-    const isBasicEnabled = configManager.getConfig('crowi', 'security:passport-basic:isEnabled');
-
-    // when disabled
-    if (!isBasicEnabled) {
-      return;
-    }
-
-    logger.debug('BasicStrategy: setting up..');
-
-    passport.use(new BasicStrategy(
-      (userId, password, done) => {
-        if (userId != null) {
-          return done(null, userId);
-        }
-        return done(null, false, { message: 'Incorrect credentials.' });
-      },
-    ));
-
-    this.isBasicStrategySetup = true;
-    logger.debug('BasicStrategy: setup is done');
-  }
-
   /**
    * setup serializer and deserializer
    *

+ 7 - 12
packages/app/test/cypress/integration/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.spec.ts

@@ -3,16 +3,14 @@ context('Access to page by guest', () => {
 
   it('/Sandbox is successfully loaded', () => {
     cy.visit('/Sandbox');
-    cy.waitUntilSpinnerDisappear();
     cy.getByTestid('grw-pagetree-item-container').should('be.visible');
-    cy.collapseSidebar(true, true);
+    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}-sandbox`);
   });
 
   it('/Sandbox with anchor hash is successfully loaded', () => {
     cy.visit('/Sandbox#Headers');
     cy.getByTestid('grw-pagetree-item-container').should('be.visible');
-    cy.collapseSidebar(true, true);
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
     // cy.wait(500);
@@ -20,16 +18,17 @@ context('Access to page by guest', () => {
     // hide fab // disable fab for sticky-events warning
     // cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
 
+    cy.collapseSidebar(true, true);
     cy.screenshot(`${ssPrefix}-sandbox-headers`);
   });
 
   it('/Sandbox/Math is successfully loaded', () => {
     cy.visit('/Sandbox/Math');
     cy.getByTestid('revision-toc-content').should('be.visible');
-    cy.collapseSidebar(true, true);
 
     cy.get('.math').should('be.visible');
 
+    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}-sandbox-math`, {
       blackout: ['.revision-toc', '[data-hide-in-vrt=true]']
     });
@@ -37,11 +36,11 @@ context('Access to page by guest', () => {
 
   it('/Sandbox with edit is successfully loaded', () => {
     cy.visit('/Sandbox#edit');
-    cy.collapseSidebar(true, true);
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
     cy.wait(1000);
 
+    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}-sandbox-edit-page`);
   })
 
@@ -51,13 +50,9 @@ context('Access to page by guest', () => {
 context('Access to /me page', () => {
   const ssPrefix = 'access-to-me-page-by-guest-';
 
-  beforeEach(() => {
-    // collapse sidebar
-    cy.collapseSidebar(true);
-  });
-
   it('/me should be redirected to /login', () => {
-    cy.visit('/me', {  });
+    cy.visit('/me');
+    cy.getByTestid('login-form').should('be.visible');
     cy.screenshot(`${ssPrefix}-me`);
   });
 
@@ -69,8 +64,8 @@ context('Access to special pages by guest', () => {
 
   it('/trash is successfully loaded', () => {
     cy.visit('/trash', {  });
-    cy.collapseSidebar(true, true);
     cy.getByTestid('trash-page-list').should('be.visible');
+    cy.collapseSidebar(true);
     cy.screenshot(`${ssPrefix}-trash`);
   });
 

+ 14 - 12
packages/app/test/cypress/integration/60-home/60-home--home.spec.ts

@@ -6,27 +6,30 @@ context('Access Home', () => {
     cy.fixture("user-admin.json").then(user => {
       cy.login(user.username, user.password);
     });
-    // collapse sidebar
-    cy.collapseSidebar(true);
   });
 
   it('Visit home', () => {
     cy.visit('/dummy');
-    cy.waitUntilSkeletonDisappear();
-    cy.get('.grw-personal-dropdown').as('dropdown').should('be.visible').click()
-    cy.get('@dropdown').within(()=>{
-      cy.getByTestid('personal-dropdown-menu').should('have.css', 'display', 'block');
+
+    // open PersonalDropdown
+    cy.waitUntil(() => {
+      // do
+      cy.getByTestid('personal-dropdown-button').should('be.visible').click();
+      // wait until
+      return cy.getByTestid('grw-personal-dropdown-menu-user-home').then($elem => $elem.is(':visible'));
     });
+    // click the Home button
     cy.getByTestid('grw-personal-dropdown-menu-user-home').should('be.visible').click();
-    cy.waitUntilSkeletonDisappear();
 
-    // eslint-disable-next-line cypress/no-unnecessary-waiting
-    cy.wait(2000); // wait for calcViewHeight and rendering
+    cy.getByTestid('grw-users-info').should('be.visible');
 
     // for check download toc data
-    cy.get('.toc-link', { timeout: 60000 }).should('be.visible');
+    // https://redmine.weseek.co.jp/issues/111384
+    // cy.get('.toc-link').should('be.visible');
 
     // same screenshot is taken in access-to-page.spec
+    cy.collapseSidebar(true);
+    cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}-visit-home`);
   });
 
@@ -41,9 +44,8 @@ context('Access User settings', () => {
     cy.fixture("user-admin.json").then(user => {
       cy.login(user.username, user.password);
     });
-    // collapse sidebar
-    cy.collapseSidebar(true);
     cy.visit('/me');
+    cy.collapseSidebar(true);
     // hide fab // disable fab for sticky-events warning
     // cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
   });

+ 34 - 23
packages/app/test/cypress/support/commands.ts

@@ -24,6 +24,20 @@
 // -- This will overwrite an existing command --
 // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
 
+import 'cypress-wait-until';
+
+function isVisible($elem: JQuery<Element>) {
+  return $elem.is(':visible');
+}
+function isHidden($elem: JQuery<Element>) {
+  return !isVisible($elem);
+}
+function isVisibleByTestId(testId: string) {
+  return isVisible(Cypress.$(`[data-testid=${testId}]`));
+}
+function isHiddenByTestId(testId: string) {
+  return !isVisibleByTestId(testId);
+}
 
 Cypress.Commands.add('getByTestid', (selector, options?) => {
   return cy.get(`[data-testid=${selector}]`, options);
@@ -41,43 +55,40 @@ Cypress.Commands.add('login', (username, password) => {
   });
 });
 
-/**
- * use only for the pages which use component with skeleton
- */
 Cypress.Commands.add('waitUntilSkeletonDisappear', () => {
-  cy.get('.grw-skeleton').should('exist');
+  if (isHidden(Cypress.$('.grw-skeleton'))) {
+    return;
+  }
   cy.get('.grw-skeleton').should('not.exist');
 });
 
 Cypress.Commands.add('waitUntilSpinnerDisappear', () => {
-  cy.get('.fa-spinner').should('exist');
+  if (isHidden(Cypress.$('.fa-spinner'))) {
+    return;
+  }
   cy.get('.fa-spinner').should('not.exist');
 });
 
-let isSidebarCollapsed: boolean | undefined;
-
-Cypress.Commands.add('collapseSidebar', (isCollapsed, force=false) => {
+Cypress.Commands.add('collapseSidebar', (isCollapsed: boolean) => {
+  const isSidebarExists = isVisibleByTestId('grw-sidebar-wrapper');
 
-  if (!force && isSidebarCollapsed === isCollapsed) {
+  if (!isSidebarExists) {
     return;
   }
 
-  const isGrowiPage = Cypress.$('div.growi').length > 0;
-  if (!isGrowiPage) {
-    cy.visit('/page-to-toggle-sidebar-collapsed');
+  const isSidebarContextualNavigationHidden = isHiddenByTestId('grw-contextual-navigation-sub');
+  if (isSidebarContextualNavigationHidden === isCollapsed) {
+    return;
   }
 
-  cy.getByTestid('grw-contextual-navigation-sub').then(($contents) => {
-    const isCurrentCollapsed = $contents.hasClass('d-none');
-    // toggle when the current state and isCoolapsed is not match
-    if (isCurrentCollapsed !== isCollapsed) {
-      cy.getByTestid("grw-navigation-resize-button").click({force: true});
+  cy.waitUntil(() => {
+    // do
+    cy.getByTestid("grw-navigation-resize-button").click({force: true});
+    // wait until saving UserUISettings
+    // eslint-disable-next-line cypress/no-unnecessary-waiting
+    cy.wait(1500);
 
-      // wait until saving UserUISettings
-      // eslint-disable-next-line cypress/no-unnecessary-waiting
-      cy.wait(1500);
-    }
+    // wait until
+    return cy.getByTestid('grw-contextual-navigation-sub').then($contents => isHidden($contents) === isCollapsed);
   });
-
-  isSidebarCollapsed = isCollapsed;
 });

+ 5 - 7
yarn.lock

@@ -7800,6 +7800,11 @@ currently-unhandled@^0.4.1:
   dependencies:
     array-find-index "^1.0.1"
 
+cypress-wait-until@^1.7.2:
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/cypress-wait-until/-/cypress-wait-until-1.7.2.tgz#7f534dd5a11c89b65359e7a0210f20d3dfc22107"
+  integrity sha512-uZ+M8/MqRcpf+FII/UZrU7g1qYZ4aVlHcgyVopnladyoBrpoaMJ4PKZDrdOJ05H5RHbr7s9Tid635X3E+ZLU/Q==
+
 cypress@^12.0.1:
   version "12.0.1"
   resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.0.1.tgz#3a51a38b2f162256c7226e68e902cfe1750e3d92"
@@ -17563,13 +17568,6 @@ passport-google-oauth20@^2.0.0:
   dependencies:
     passport-oauth2 "1.x.x"
 
-passport-http@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/passport-http/-/passport-http-0.3.0.tgz#8ee53d4380be9c60df2151925029826f77115603"
-  integrity sha1-juU9Q4C+nGDfIVGSUCmCb3cRVgM=
-  dependencies:
-    passport-strategy "1.x.x"
-
 passport-ldapauth@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz#1432e8469de18bd46b5b39a46a866b416c1ddded"