Browse Source

Create condition to disable local auth method

https://youtrack.weseek.co.jp/issue/GW-7958
- Create method to check if local auth method has admin user
- Check if external accounts has admin
- Create method to check if local authentication method is able to disable
- Pass isAbleToDisableAuthMethod to generalSetting in security params
- Set isAbleToDisableAuthMethod to state in AdminGeneralSecurityContainer
- Change disabled props value of local authentication method toggle button
Mudana-Grune 2 năm trước cách đây
mục cha
commit
e2eb6a349a

+ 1 - 0
apps/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -82,6 +82,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       isOidcEnabled: generalAuth.isOidcEnabled,
       isOidcEnabled: generalAuth.isOidcEnabled,
       isGoogleEnabled: generalAuth.isGoogleEnabled,
       isGoogleEnabled: generalAuth.isGoogleEnabled,
       isGitHubEnabled: generalAuth.isGitHubEnabled,
       isGitHubEnabled: generalAuth.isGitHubEnabled,
+      isAbleToDisableAuthMethod: generalSetting.isAbleToDisableAuthMethod,
     });
     });
   }
   }
 
 

+ 2 - 2
apps/app/src/components/Admin/Security/LocalSecuritySettingContents.jsx

@@ -39,7 +39,7 @@ class LocalSecuritySettingContents extends React.Component {
       isMailerSetup,
       isMailerSetup,
     } = this.props;
     } = this.props;
     const { registrationMode, isPasswordResetEnabled, isEmailAuthenticationEnabled } = adminLocalSecurityContainer.state;
     const { registrationMode, isPasswordResetEnabled, isEmailAuthenticationEnabled } = adminLocalSecurityContainer.state;
-    const { isLocalEnabled } = adminGeneralSecurityContainer.state;
+    const { isLocalEnabled, isAbleToDisableAuthMethod } = adminGeneralSecurityContainer.state;
 
 
     return (
     return (
       <>
       <>
@@ -71,7 +71,7 @@ class LocalSecuritySettingContents extends React.Component {
                 id="isLocalEnabled"
                 id="isLocalEnabled"
                 checked={isLocalEnabled}
                 checked={isLocalEnabled}
                 onChange={() => adminGeneralSecurityContainer.switchIsLocalEnabled()}
                 onChange={() => adminGeneralSecurityContainer.switchIsLocalEnabled()}
-                disabled
+                disabled={isLocalEnabled && !isAbleToDisableAuthMethod}
               />
               />
               <label className="custom-control-label" htmlFor="isLocalEnabled">
               <label className="custom-control-label" htmlFor="isLocalEnabled">
                 {t('security_settings.Local.enable_local')}
                 {t('security_settings.Local.enable_local')}

+ 43 - 0
apps/app/src/server/routes/apiv3/security-setting.js

@@ -8,6 +8,7 @@ import { validateDeleteConfigs, prepareDeleteConfigValuesForCalc } from '~/utils
 
 
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
+import { GrowiExternalAuthProviderType } from '~/features/questionnaire/interfaces/growi-info';
 
 
 
 
 const logger = loggerFactory('growi:routes:apiv3:security-setting');
 const logger = loggerFactory('growi:routes:apiv3:security-setting');
@@ -315,6 +316,9 @@ module.exports = (crowi) => {
 
 
   const activityEvent = crowi.event('activity');
   const activityEvent = crowi.event('activity');
 
 
+  const ExternalAccount = crowi.model('ExternalAccount');
+  const User = crowi.model('User');
+
   async function updateAndReloadStrategySettings(authId, params) {
   async function updateAndReloadStrategySettings(authId, params) {
     const { configManager, passportService } = crowi;
     const { configManager, passportService } = crowi;
 
 
@@ -325,6 +329,44 @@ module.exports = (crowi) => {
     passportService.publishUpdatedMessage(authId);
     passportService.publishUpdatedMessage(authId);
   }
   }
 
 
+  async function isAbleToDisableAuthMethod() {
+    const activeExternalAccountTypes = Object.values(GrowiExternalAuthProviderType).filter((type) => {
+      return crowi.configManager.getConfig('crowi', `security:passport-${type}:isEnabled`);
+    });
+
+    const isAdminPromises = activeExternalAccountTypes.map(providerType => checkExternalAccountHasAdmin(providerType));
+    const isAdminResults = await Promise.all(isAdminPromises);
+    const isLocalAdminEnabled = await crowi.configManager.getConfig('crowi', 'security:passport-local:isEnabled');
+    const isLocalHasAdmin = await checkLocalAccountHasAdmin();
+
+    const totalActiveAuthMethods = activeExternalAccountTypes.reduce((count, providerType, index) => {
+      if (isAdminResults[index] === true) {
+        return count + 1;
+      }
+      return count;
+    }, 0) + (isLocalAdminEnabled && isLocalHasAdmin ? 1 : 0);
+
+    return totalActiveAuthMethods > 1;
+  }
+
+
+  async function checkExternalAccountHasAdmin(providerType) {
+    const externalAccounts = await ExternalAccount.find({ providerType }).populate('user').exec();
+    for (const externalAccount of externalAccounts) {
+      const { user } = externalAccount;
+      if (user && user.isAdmin) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  async function checkLocalAccountHasAdmin() {
+    const adminAccounts = await User.find({ isAdmin: true }).exec();
+    return adminAccounts.length > 0;
+  }
+
   /**
   /**
    * @swagger
    * @swagger
    *
    *
@@ -356,6 +398,7 @@ module.exports = (crowi) => {
         hideRestrictedByGroup: await crowi.configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
         hideRestrictedByGroup: await crowi.configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
         wikiMode: await crowi.configManager.getConfig('crowi', 'security:wikiMode'),
         wikiMode: await crowi.configManager.getConfig('crowi', 'security:wikiMode'),
         sessionMaxAge: await crowi.configManager.getConfig('crowi', 'security:sessionMaxAge'),
         sessionMaxAge: await crowi.configManager.getConfig('crowi', 'security:sessionMaxAge'),
+        isAbleToDisableAuthMethod: await isAbleToDisableAuthMethod(),
       },
       },
       shareLinkSetting: {
       shareLinkSetting: {
         disableLinkSharing: await crowi.configManager.getConfig('crowi', 'security:disableLinkSharing'),
         disableLinkSharing: await crowi.configManager.getConfig('crowi', 'security:disableLinkSharing'),