Browse Source

Merge pull request #10519 from growilabs/master

Release v7.3.7
mergify[bot] 4 months ago
parent
commit
20b7f563a1
24 changed files with 276 additions and 435 deletions
  1. 1 1
      apps/app/package.json
  2. 3 2
      apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx
  3. 9 2
      apps/app/src/client/components/Admin/AdminHome/EnvVarsTable.tsx
  4. 3 1
      apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx
  5. 5 3
      apps/app/src/client/components/Admin/Security/GitHubSecuritySettingContents.jsx
  6. 5 3
      apps/app/src/client/components/Admin/Security/GoogleSecuritySettingContents.jsx
  7. 14 11
      apps/app/src/client/components/Admin/Security/LdapSecuritySettingContents.tsx
  8. 6 2
      apps/app/src/client/components/Admin/Security/LocalSecuritySettingContents.tsx
  9. 20 17
      apps/app/src/client/components/Admin/Security/OidcSecuritySettingContents.tsx
  10. 13 11
      apps/app/src/client/components/Admin/Security/SamlSecuritySettingContents.tsx
  11. 15 4
      apps/app/src/client/components/Admin/Security/SecuritySetting/index.tsx
  12. 16 3
      apps/app/src/client/services/AdminGeneralSecurityContainer.js
  13. 10 18
      apps/app/src/client/services/AdminGitHubSecurityContainer.js
  14. 9 19
      apps/app/src/client/services/AdminGoogleSecurityContainer.js
  15. 27 89
      apps/app/src/client/services/AdminLdapSecurityContainer.js
  16. 12 14
      apps/app/src/client/services/AdminLocalSecurityContainer.js
  17. 39 153
      apps/app/src/client/services/AdminOidcSecurityContainer.js
  18. 15 66
      apps/app/src/client/services/AdminSamlSecurityContainer.js
  19. 1 0
      apps/pdf-converter/.env
  20. 2 2
      apps/pdf-converter/docker/README.md
  21. 1 1
      apps/pdf-converter/package.json
  22. 48 11
      apps/pdf-converter/src/service/pdf-convert.ts
  23. 1 1
      apps/slackbot-proxy/package.json
  24. 1 1
      package.json

+ 1 - 1
apps/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "7.3.6",
+  "version": "7.3.7-RC.0",
   "license": "MIT",
   "private": "true",
   "scripts": {

+ 3 - 2
apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx

@@ -17,6 +17,7 @@ import { withUnstatedContainers } from '../../UnstatedUtils';
 import { EnvVarsTable } from './EnvVarsTable';
 import SystemInfomationTable from './SystemInfomationTable';
 
+
 const logger = loggerFactory('growi:admin');
 
 const AdminHome = (props) => {
@@ -59,7 +60,7 @@ const AdminHome = (props) => {
         )
       }
       {
-      // Alert message will be displayed in case that V5 migration has not been compleated
+        // Alert message will be displayed in case that V5 migration has not been compleated
         (migrationStatus != null && !migrationStatus.isV5Compatible)
         && (
           <div className={`alert ${migrationStatus.isV5Compatible == null ? 'alert-warning' : 'alert-info'}`}>
@@ -90,7 +91,7 @@ const AdminHome = (props) => {
           <p>{t('admin:admin_top.env_var_priority')}</p>
           {/* eslint-disable-next-line react/no-danger */}
           <p dangerouslySetInnerHTML={{ __html: t('admin:admin_top.about_security') }} />
-          {adminHomeContainer.state.envVars && <EnvVarsTable envVars={adminHomeContainer.state.envVars} />}
+          <EnvVarsTable envVars={adminHomeContainer.state.envVars} />
         </div>
       </div>
 

+ 9 - 2
apps/app/src/client/components/Admin/AdminHome/EnvVarsTable.tsx

@@ -1,13 +1,20 @@
 import React, { type JSX } from 'react';
 
+import { LoadingSpinner } from '@growi/ui/dist/components';
+
 type EnvVarsTableProps = {
-  envVars: Record<string, string | number | boolean>,
+  envVars?: Record<string, string | number | boolean>,
 }
 
 export const EnvVarsTable: React.FC<EnvVarsTableProps> = (props: EnvVarsTableProps) => {
+  const { envVars } = props;
+  if (envVars == null) {
+    return <LoadingSpinner />;
+  }
+
   const envVarRows: JSX.Element[] = [];
 
-  for (const [key, value] of Object.entries(props.envVars)) {
+  for (const [key, value] of Object.entries(envVars ?? {})) {
     if (value != null) {
       envVarRows.push(
         <tr key={key}>

+ 3 - 1
apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx

@@ -1,5 +1,7 @@
 import React from 'react';
 
+import { LoadingSpinner } from '@growi/ui/dist/components';
+
 import AdminHomeContainer from '~/client/services/AdminHomeContainer';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
@@ -17,7 +19,7 @@ const SystemInformationTable = (props: Props) => {
   } = adminHomeContainer.state;
 
   if (growiVersion == null || nodeVersion == null || npmVersion == null || pnpmVersion == null) {
-    return <></>;
+    return <LoadingSpinner />;
   }
 
   return (

+ 5 - 3
apps/app/src/client/components/Admin/Security/GitHubSecuritySettingContents.jsx

@@ -36,9 +36,11 @@ const GitHubSecurityManagementContents = (props) => {
 
   const onClickSubmit = useCallback(async(data) => {
     try {
-      await adminGitHubSecurityContainer.changeGitHubClientId(data.githubClientId ?? '');
-      await adminGitHubSecurityContainer.changeGitHubClientSecret(data.githubClientSecret ?? '');
-      await adminGitHubSecurityContainer.updateGitHubSetting();
+      await adminGitHubSecurityContainer.updateGitHubSetting({
+        githubClientId: data.githubClientId ?? '',
+        githubClientSecret: data.githubClientSecret ?? '',
+        isSameUsernameTreatedAsIdenticalUser: adminGitHubSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
+      });
       await adminGeneralSecurityContainer.retrieveSetupStratedies();
       toastSuccess(t('security_settings.OAuth.GitHub.updated_github'));
     }

+ 5 - 3
apps/app/src/client/components/Admin/Security/GoogleSecuritySettingContents.jsx

@@ -34,9 +34,11 @@ const GoogleSecurityManagementContents = (props) => {
 
   const onClickSubmit = useCallback(async(data) => {
     try {
-      await adminGoogleSecurityContainer.changeGoogleClientId(data.googleClientId ?? '');
-      await adminGoogleSecurityContainer.changeGoogleClientSecret(data.googleClientSecret ?? '');
-      await adminGoogleSecurityContainer.updateGoogleSetting();
+      await adminGoogleSecurityContainer.updateGoogleSetting({
+        googleClientId: data.googleClientId ?? '',
+        googleClientSecret: data.googleClientSecret ?? '',
+        isSameEmailTreatedAsIdenticalUser: adminGoogleSecurityContainer.state.isSameEmailTreatedAsIdenticalUser,
+      });
       await adminGeneralSecurityContainer.retrieveSetupStratedies();
       toastSuccess(t('security_settings.OAuth.Google.updated_google'));
     }

+ 14 - 11
apps/app/src/client/components/Admin/Security/LdapSecuritySettingContents.tsx

@@ -56,17 +56,20 @@ const LdapSecuritySettingContents = (props: Props) => {
 
   const onSubmit = useCallback(async(data) => {
     try {
-      await adminLdapSecurityContainer.changeServerUrl(data.serverUrl);
-      await adminLdapSecurityContainer.changeBindDN(data.ldapBindDN);
-      await adminLdapSecurityContainer.changeBindDNPassword(data.ldapBindDNPassword);
-      await adminLdapSecurityContainer.changeSearchFilter(data.ldapSearchFilter);
-      await adminLdapSecurityContainer.changeAttrMapUsername(data.ldapAttrMapUsername);
-      await adminLdapSecurityContainer.changeAttrMapMail(data.ldapAttrMapMail);
-      await adminLdapSecurityContainer.changeAttrMapName(data.ldapAttrMapName);
-      await adminLdapSecurityContainer.changeGroupSearchBase(data.ldapGroupSearchBase);
-      await adminLdapSecurityContainer.changeGroupSearchFilter(data.ldapGroupSearchFilter);
-      await adminLdapSecurityContainer.changeGroupDnProperty(data.ldapGroupDnProperty);
-      await adminLdapSecurityContainer.updateLdapSetting();
+      await adminLdapSecurityContainer.updateLdapSetting({
+        serverUrl: data.serverUrl,
+        isUserBind: adminLdapSecurityContainer.state.isUserBind,
+        ldapBindDN: data.ldapBindDN,
+        ldapBindDNPassword: data.ldapBindDNPassword,
+        ldapSearchFilter: data.ldapSearchFilter,
+        ldapAttrMapUsername: data.ldapAttrMapUsername,
+        isSameUsernameTreatedAsIdenticalUser: adminLdapSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
+        ldapAttrMapMail: data.ldapAttrMapMail,
+        ldapAttrMapName: data.ldapAttrMapName,
+        ldapGroupSearchBase: data.ldapGroupSearchBase,
+        ldapGroupSearchFilter: data.ldapGroupSearchFilter,
+        ldapGroupDnProperty: data.ldapGroupDnProperty,
+      });
       await adminGeneralSecurityContainer.retrieveSetupStratedies();
       toastSuccess(t('security_settings.ldap.updated_ldap'));
     }

+ 6 - 2
apps/app/src/client/components/Admin/Security/LocalSecuritySettingContents.tsx

@@ -38,8 +38,12 @@ const LocalSecuritySettingContents = (props: Props): JSX.Element => {
 
   const onSubmit = useCallback(async(data) => {
     try {
-      await adminLocalSecurityContainer.changeRegistrationWhitelist(data.registrationWhitelist);
-      await adminLocalSecurityContainer.updateLocalSecuritySetting();
+      await adminLocalSecurityContainer.updateLocalSecuritySetting({
+        registrationMode: adminLocalSecurityContainer.state.registrationMode,
+        registrationWhitelist: data.registrationWhitelist.split('\n'),
+        isPasswordResetEnabled: adminLocalSecurityContainer.state.isPasswordResetEnabled,
+        isEmailAuthenticationEnabled: adminLocalSecurityContainer.state.isEmailAuthenticationEnabled,
+      });
       await adminGeneralSecurityContainer.retrieveSetupStratedies();
       toastSuccess(t('security_settings.updated_general_security_setting'));
     }

+ 20 - 17
apps/app/src/client/components/Admin/Security/OidcSecuritySettingContents.tsx

@@ -68,23 +68,26 @@ const OidcSecurityManagementContents = (props: Props) => {
 
   const onSubmit = useCallback(async(data) => {
     try {
-      await adminOidcSecurityContainer.changeOidcProviderName(data.oidcProviderName);
-      await adminOidcSecurityContainer.changeOidcIssuerHost(data.oidcIssuerHost);
-      await adminOidcSecurityContainer.changeOidcClientId(data.oidcClientId);
-      await adminOidcSecurityContainer.changeOidcClientSecret(data.oidcClientSecret);
-      await adminOidcSecurityContainer.changeOidcAuthorizationEndpoint(data.oidcAuthorizationEndpoint);
-      await adminOidcSecurityContainer.changeOidcTokenEndpoint(data.oidcTokenEndpoint);
-      await adminOidcSecurityContainer.changeOidcRevocationEndpoint(data.oidcRevocationEndpoint);
-      await adminOidcSecurityContainer.changeOidcIntrospectionEndpoint(data.oidcIntrospectionEndpoint);
-      await adminOidcSecurityContainer.changeOidcUserInfoEndpoint(data.oidcUserInfoEndpoint);
-      await adminOidcSecurityContainer.changeOidcEndSessionEndpoint(data.oidcEndSessionEndpoint);
-      await adminOidcSecurityContainer.changeOidcRegistrationEndpoint(data.oidcRegistrationEndpoint);
-      await adminOidcSecurityContainer.changeOidcJWKSUri(data.oidcJWKSUri);
-      await adminOidcSecurityContainer.changeOidcAttrMapId(data.oidcAttrMapId);
-      await adminOidcSecurityContainer.changeOidcAttrMapUserName(data.oidcAttrMapUserName);
-      await adminOidcSecurityContainer.changeOidcAttrMapName(data.oidcAttrMapName);
-      await adminOidcSecurityContainer.changeOidcAttrMapEmail(data.oidcAttrMapEmail);
-      await adminOidcSecurityContainer.updateOidcSetting();
+      await adminOidcSecurityContainer.updateOidcSetting({
+        oidcProviderName: data.oidcProviderName,
+        oidcIssuerHost: data.oidcIssuerHost,
+        oidcClientId: data.oidcClientId,
+        oidcClientSecret: data.oidcClientSecret,
+        oidcAuthorizationEndpoint: data.oidcAuthorizationEndpoint,
+        oidcTokenEndpoint: data.oidcTokenEndpoint,
+        oidcRevocationEndpoint: data.oidcRevocationEndpoint,
+        oidcIntrospectionEndpoint: data.oidcIntrospectionEndpoint,
+        oidcUserInfoEndpoint: data.oidcUserInfoEndpoint,
+        oidcEndSessionEndpoint: data.oidcEndSessionEndpoint,
+        oidcRegistrationEndpoint: data.oidcRegistrationEndpoint,
+        oidcJWKSUri: data.oidcJWKSUri,
+        oidcAttrMapId: data.oidcAttrMapId,
+        oidcAttrMapUserName: data.oidcAttrMapUserName,
+        oidcAttrMapName: data.oidcAttrMapName,
+        oidcAttrMapEmail: data.oidcAttrMapEmail,
+        isSameUsernameTreatedAsIdenticalUser: adminOidcSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
+        isSameEmailTreatedAsIdenticalUser: adminOidcSecurityContainer.state.isSameEmailTreatedAsIdenticalUser,
+      });
       await adminGeneralSecurityContainer.retrieveSetupStratedies();
       toastSuccess(t('security_settings.OAuth.OIDC.updated_oidc'));
     }

+ 13 - 11
apps/app/src/client/components/Admin/Security/SamlSecuritySettingContents.tsx

@@ -46,18 +46,20 @@ const SamlSecurityManagementContents = (props: Props) => {
   }, [adminSamlSecurityContainer.state, reset]);
 
   const onSubmit = useCallback(async(data) => {
-    adminSamlSecurityContainer.changeSamlEntryPoint(data.samlEntryPoint);
-    adminSamlSecurityContainer.changeSamlIssuer(data.samlIssuer);
-    adminSamlSecurityContainer.changeSamlCert(data.samlCert);
-    adminSamlSecurityContainer.changeSamlAttrMapId(data.samlAttrMapId);
-    adminSamlSecurityContainer.changeSamlAttrMapUserName(data.samlAttrMapUsername);
-    adminSamlSecurityContainer.changeSamlAttrMapMail(data.samlAttrMapMail);
-    adminSamlSecurityContainer.changeSamlAttrMapFirstName(data.samlAttrMapFirstName);
-    adminSamlSecurityContainer.changeSamlAttrMapLastName(data.samlAttrMapLastName);
-    adminSamlSecurityContainer.changeSamlABLCRule(data.samlABLCRule);
-
     try {
-      await adminSamlSecurityContainer.updateSamlSetting();
+      await adminSamlSecurityContainer.updateSamlSetting({
+        samlEntryPoint: data.samlEntryPoint,
+        samlIssuer: data.samlIssuer,
+        samlCert: data.samlCert,
+        samlAttrMapId: data.samlAttrMapId,
+        samlAttrMapUsername: data.samlAttrMapUsername,
+        samlAttrMapMail: data.samlAttrMapMail,
+        samlAttrMapFirstName: data.samlAttrMapFirstName,
+        samlAttrMapLastName: data.samlAttrMapLastName,
+        isSameUsernameTreatedAsIdenticalUser: adminSamlSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser,
+        isSameEmailTreatedAsIdenticalUser: adminSamlSecurityContainer.state.isSameEmailTreatedAsIdenticalUser,
+        samlABLCRule: data.samlABLCRule,
+      });
       toastSuccess(t('security_settings.SAML.updated_saml'));
     }
     catch (err) {

+ 15 - 4
apps/app/src/client/components/Admin/Security/SecuritySetting/index.tsx

@@ -36,10 +36,21 @@ const SecuritySettingComponent: React.FC<Props> = ({ adminGeneralSecurityContain
 
   const onSubmit = useCallback(async(data: FormData) => {
     try {
-      // Update sessionMaxAge from form data
-      await adminGeneralSecurityContainer.setSessionMaxAge(data.sessionMaxAge);
-      // Save all security settings
-      await adminGeneralSecurityContainer.updateGeneralSecuritySetting();
+      // Save all security settings with form data
+      await adminGeneralSecurityContainer.updateGeneralSecuritySetting({
+        sessionMaxAge: data.sessionMaxAge,
+        restrictGuestMode: adminGeneralSecurityContainer.state.currentRestrictGuestMode,
+        pageDeletionAuthority: adminGeneralSecurityContainer.state.currentPageDeletionAuthority,
+        pageCompleteDeletionAuthority: adminGeneralSecurityContainer.state.currentPageCompleteDeletionAuthority,
+        pageRecursiveDeletionAuthority: adminGeneralSecurityContainer.state.currentPageRecursiveDeletionAuthority,
+        pageRecursiveCompleteDeletionAuthority: adminGeneralSecurityContainer.state.currentPageRecursiveCompleteDeletionAuthority,
+        isAllGroupMembershipRequiredForPageCompleteDeletion: adminGeneralSecurityContainer.state.isAllGroupMembershipRequiredForPageCompleteDeletion,
+        hideRestrictedByGroup: adminGeneralSecurityContainer.state.currentGroupRestrictionDisplayMode === 'Hidden',
+        hideRestrictedByOwner: adminGeneralSecurityContainer.state.currentOwnerRestrictionDisplayMode === 'Hidden',
+        isUsersHomepageDeletionEnabled: adminGeneralSecurityContainer.state.isUsersHomepageDeletionEnabled,
+        isForceDeleteUserHomepageOnUserDeletion: adminGeneralSecurityContainer.state.isForceDeleteUserHomepageOnUserDeletion,
+        isRomUserAllowedToComment: adminGeneralSecurityContainer.state.isRomUserAllowedToComment,
+      });
       toastSuccess(t('security_settings.updated_general_security_setting'));
     }
     catch (err) {

+ 16 - 3
apps/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -239,9 +239,22 @@ export default class AdminGeneralSecurityContainer extends Container {
    * @memberOf AdminGeneralSecuritySContainer
    * @return {string} Appearance
    */
-  async updateGeneralSecuritySetting() {
-
-    let requestParams = {
+  async updateGeneralSecuritySetting(formData) {
+
+    let requestParams = formData != null ? {
+      sessionMaxAge: formData.sessionMaxAge,
+      restrictGuestMode: formData.restrictGuestMode,
+      pageDeletionAuthority: formData.pageDeletionAuthority,
+      pageCompleteDeletionAuthority: formData.pageCompleteDeletionAuthority,
+      pageRecursiveDeletionAuthority: formData.pageRecursiveDeletionAuthority,
+      pageRecursiveCompleteDeletionAuthority: formData.pageRecursiveCompleteDeletionAuthority,
+      isAllGroupMembershipRequiredForPageCompleteDeletion: formData.isAllGroupMembershipRequiredForPageCompleteDeletion,
+      hideRestrictedByGroup: formData.hideRestrictedByGroup,
+      hideRestrictedByOwner: formData.hideRestrictedByOwner,
+      isUsersHomepageDeletionEnabled: formData.isUsersHomepageDeletionEnabled,
+      isForceDeleteUserHomepageOnUserDeletion: formData.isForceDeleteUserHomepageOnUserDeletion,
+      isRomUserAllowedToComment: formData.isRomUserAllowedToComment,
+    } : {
       sessionMaxAge: this.state.sessionMaxAge,
       restrictGuestMode: this.state.currentRestrictGuestMode,
       pageDeletionAuthority: this.state.currentPageDeletionAuthority,

+ 10 - 18
apps/app/src/client/services/AdminGitHubSecurityContainer.js

@@ -61,20 +61,6 @@ export default class AdminGitHubSecurityContainer extends Container {
     return 'AdminGitHubSecurityContainer';
   }
 
-  /**
-   * Change githubClientId
-   */
-  changeGitHubClientId(value) {
-    this.setState({ githubClientId: value });
-  }
-
-  /**
-   * Change githubClientSecret
-   */
-  changeGitHubClientSecret(value) {
-    this.setState({ githubClientSecret: value });
-  }
-
   /**
    * Switch isSameUsernameTreatedAsIdenticalUser
    */
@@ -85,10 +71,16 @@ export default class AdminGitHubSecurityContainer extends Container {
   /**
    * Update githubSetting
    */
-  async updateGitHubSetting() {
-    const { githubClientId, githubClientSecret, isSameUsernameTreatedAsIdenticalUser } = this.state;
-
-    let requestParams = { githubClientId, githubClientSecret, isSameUsernameTreatedAsIdenticalUser };
+  async updateGitHubSetting(formData) {
+    let requestParams = formData != null ? {
+      githubClientId: formData.githubClientId,
+      githubClientSecret: formData.githubClientSecret,
+      isSameUsernameTreatedAsIdenticalUser: formData.isSameUsernameTreatedAsIdenticalUser,
+    } : {
+      githubClientId: this.state.githubClientId,
+      githubClientSecret: this.state.githubClientSecret,
+      isSameUsernameTreatedAsIdenticalUser: this.state.isSameUsernameTreatedAsIdenticalUser,
+    };
 
     requestParams = await removeNullPropertyFromObject(requestParams);
     const response = await apiv3Put('/security-setting/github-oauth', requestParams);

+ 9 - 19
apps/app/src/client/services/AdminGoogleSecurityContainer.js

@@ -62,20 +62,6 @@ export default class AdminGoogleSecurityContainer extends Container {
     return 'AdminGoogleSecurityContainer';
   }
 
-  /**
-   * Change googleClientId
-   */
-  changeGoogleClientId(value) {
-    this.setState({ googleClientId: value });
-  }
-
-  /**
-   * Change googleClientSecret
-   */
-  changeGoogleClientSecret(value) {
-    this.setState({ googleClientSecret: value });
-  }
-
   /**
    * Switch isSameEmailTreatedAsIdenticalUser
    */
@@ -87,11 +73,15 @@ export default class AdminGoogleSecurityContainer extends Container {
   /**
    * Update googleSetting
    */
-  async updateGoogleSetting() {
-    const { googleClientId, googleClientSecret, isSameEmailTreatedAsIdenticalUser } = this.state;
-
-    let requestParams = {
-      googleClientId, googleClientSecret, isSameEmailTreatedAsIdenticalUser,
+  async updateGoogleSetting(formData) {
+    let requestParams = formData != null ? {
+      googleClientId: formData.googleClientId,
+      googleClientSecret: formData.googleClientSecret,
+      isSameEmailTreatedAsIdenticalUser: formData.isSameEmailTreatedAsIdenticalUser,
+    } : {
+      googleClientId: this.state.googleClientId,
+      googleClientSecret: this.state.googleClientSecret,
+      isSameEmailTreatedAsIdenticalUser: this.state.isSameEmailTreatedAsIdenticalUser,
     };
 
     requestParams = await removeNullPropertyFromObject(requestParams);

+ 27 - 89
apps/app/src/client/services/AdminLdapSecurityContainer.js

@@ -78,13 +78,6 @@ export default class AdminLdapSecurityContainer extends Container {
     return 'AdminLdapSecurityContainer';
   }
 
-  /**
-   * Change serverUrl
-   */
-  changeServerUrl(serverUrl) {
-    this.setState({ serverUrl });
-  }
-
   /**
    * Change ldapBindMode
    * @param {boolean} isUserBind true: User Bind, false: Admin Bind
@@ -93,34 +86,6 @@ export default class AdminLdapSecurityContainer extends Container {
     this.setState({ isUserBind });
   }
 
-  /**
-   * Change bindDN
-   */
-  changeBindDN(ldapBindDN) {
-    this.setState({ ldapBindDN });
-  }
-
-  /**
-   * Change bindDNPassword
-   */
-  changeBindDNPassword(ldapBindDNPassword) {
-    this.setState({ ldapBindDNPassword });
-  }
-
-  /**
-   * Change ldapSearchFilter
-   */
-  changeSearchFilter(ldapSearchFilter) {
-    this.setState({ ldapSearchFilter });
-  }
-
-  /**
-   * Change ldapAttrMapUsername
-   */
-  changeAttrMapUsername(ldapAttrMapUsername) {
-    this.setState({ ldapAttrMapUsername });
-  }
-
   /**
    * Switch is same username treated as identical user
    */
@@ -128,63 +93,36 @@ export default class AdminLdapSecurityContainer extends Container {
     this.setState({ isSameUsernameTreatedAsIdenticalUser: !this.state.isSameUsernameTreatedAsIdenticalUser });
   }
 
-  /**
-   * Change ldapAttrMapMail
-   */
-  changeAttrMapMail(ldapAttrMapMail) {
-    this.setState({ ldapAttrMapMail });
-  }
-
-  /**
-   * Change ldapAttrMapName
-   */
-  changeAttrMapName(ldapAttrMapName) {
-    this.setState({ ldapAttrMapName });
-  }
-
-  /**
-   * Change ldapGroupSearchBase
-   */
-  changeGroupSearchBase(ldapGroupSearchBase) {
-    this.setState({ ldapGroupSearchBase });
-  }
-
-  /**
-   * Change ldapGroupSearchFilter
-   */
-  changeGroupSearchFilter(ldapGroupSearchFilter) {
-    this.setState({ ldapGroupSearchFilter });
-  }
-
-  /**
-   * Change ldapGroupDnProperty
-   */
-  changeGroupDnProperty(ldapGroupDnProperty) {
-    this.setState({ ldapGroupDnProperty });
-  }
-
   /**
    * Update ldap option
    */
-  async updateLdapSetting() {
-    const {
-      serverUrl, isUserBind, ldapBindDN, ldapBindDNPassword, ldapSearchFilter, ldapAttrMapUsername, isSameUsernameTreatedAsIdenticalUser,
-      ldapAttrMapMail, ldapAttrMapName, ldapGroupSearchBase, ldapGroupSearchFilter, ldapGroupDnProperty,
-    } = this.state;
-
-    let requestParams = {
-      serverUrl,
-      isUserBind,
-      ldapBindDN,
-      ldapBindDNPassword,
-      ldapSearchFilter,
-      ldapAttrMapUsername,
-      isSameUsernameTreatedAsIdenticalUser,
-      ldapAttrMapMail,
-      ldapAttrMapName,
-      ldapGroupSearchBase,
-      ldapGroupSearchFilter,
-      ldapGroupDnProperty,
+  async updateLdapSetting(formData) {
+    let requestParams = formData != null ? {
+      serverUrl: formData.serverUrl,
+      isUserBind: formData.isUserBind,
+      ldapBindDN: formData.ldapBindDN,
+      ldapBindDNPassword: formData.ldapBindDNPassword,
+      ldapSearchFilter: formData.ldapSearchFilter,
+      ldapAttrMapUsername: formData.ldapAttrMapUsername,
+      isSameUsernameTreatedAsIdenticalUser: formData.isSameUsernameTreatedAsIdenticalUser,
+      ldapAttrMapMail: formData.ldapAttrMapMail,
+      ldapAttrMapName: formData.ldapAttrMapName,
+      ldapGroupSearchBase: formData.ldapGroupSearchBase,
+      ldapGroupSearchFilter: formData.ldapGroupSearchFilter,
+      ldapGroupDnProperty: formData.ldapGroupDnProperty,
+    } : {
+      serverUrl: this.state.serverUrl,
+      isUserBind: this.state.isUserBind,
+      ldapBindDN: this.state.ldapBindDN,
+      ldapBindDNPassword: this.state.ldapBindDNPassword,
+      ldapSearchFilter: this.state.ldapSearchFilter,
+      ldapAttrMapUsername: this.state.ldapAttrMapUsername,
+      isSameUsernameTreatedAsIdenticalUser: this.state.isSameUsernameTreatedAsIdenticalUser,
+      ldapAttrMapMail: this.state.ldapAttrMapMail,
+      ldapAttrMapName: this.state.ldapAttrMapName,
+      ldapGroupSearchBase: this.state.ldapGroupSearchBase,
+      ldapGroupSearchFilter: this.state.ldapGroupSearchFilter,
+      ldapGroupDnProperty: this.state.ldapGroupDnProperty,
     };
 
     requestParams = await removeNullPropertyFromObject(requestParams);

+ 12 - 14
apps/app/src/client/services/AdminLocalSecurityContainer.js

@@ -71,13 +71,6 @@ export default class AdminLocalSecurityContainer extends Container {
     this.setState({ registrationMode: value });
   }
 
-  /**
-   * Change registration whitelist
-   */
-  changeRegistrationWhitelist(value) {
-    this.setState({ registrationWhitelist: value.split('\n') });
-  }
-
   /**
    * Switch password reset enabled
    */
@@ -95,14 +88,19 @@ export default class AdminLocalSecurityContainer extends Container {
   /**
    * update local security setting
    */
-  async updateLocalSecuritySetting() {
-    const { registrationWhitelist, isPasswordResetEnabled, isEmailAuthenticationEnabled } = this.state;
-    const response = await apiv3Put('/security-setting/local-setting', {
+  async updateLocalSecuritySetting(formData) {
+    const requestParams = formData != null ? {
+      registrationMode: formData.registrationMode,
+      registrationWhitelist: formData.registrationWhitelist,
+      isPasswordResetEnabled: formData.isPasswordResetEnabled,
+      isEmailAuthenticationEnabled: formData.isEmailAuthenticationEnabled,
+    } : {
       registrationMode: this.state.registrationMode,
-      registrationWhitelist,
-      isPasswordResetEnabled,
-      isEmailAuthenticationEnabled,
-    });
+      registrationWhitelist: this.state.registrationWhitelist,
+      isPasswordResetEnabled: this.state.isPasswordResetEnabled,
+      isEmailAuthenticationEnabled: this.state.isEmailAuthenticationEnabled,
+    };
+    const response = await apiv3Put('/security-setting/local-setting', requestParams);
 
     const { localSettingParams } = response.data;
 

+ 39 - 153
apps/app/src/client/services/AdminOidcSecurityContainer.js

@@ -89,118 +89,6 @@ export default class AdminOidcSecurityContainer extends Container {
     return 'AdminOidcSecurityContainer';
   }
 
-  /**
-   * Change oidcProviderName
-   */
-  changeOidcProviderName(inputValue) {
-    this.setState({ oidcProviderName: inputValue });
-  }
-
-  /**
-   * Change oidcIssuerHost
-   */
-  changeOidcIssuerHost(inputValue) {
-    this.setState({ oidcIssuerHost: inputValue });
-  }
-
-  /**
-   * Change oidcAuthorizationEndpoint
-   */
-  changeOidcAuthorizationEndpoint(inputValue) {
-    this.setState({ oidcAuthorizationEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcTokenEndpoint
-   */
-  changeOidcTokenEndpoint(inputValue) {
-    this.setState({ oidcTokenEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcRevocationEndpoint
-   */
-  changeOidcRevocationEndpoint(inputValue) {
-    this.setState({ oidcRevocationEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcIntrospectionEndpoint
-   */
-  changeOidcIntrospectionEndpoint(inputValue) {
-    this.setState({ oidcIntrospectionEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcUserInfoEndpoint
-   */
-  changeOidcUserInfoEndpoint(inputValue) {
-    this.setState({ oidcUserInfoEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcEndSessionEndpoint
-   */
-  changeOidcEndSessionEndpoint(inputValue) {
-    this.setState({ oidcEndSessionEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcRegistrationEndpoint
-   */
-  changeOidcRegistrationEndpoint(inputValue) {
-    this.setState({ oidcRegistrationEndpoint: inputValue });
-  }
-
-  /**
-   * Change oidcJWKSUri
-   */
-  changeOidcJWKSUri(inputValue) {
-    this.setState({ oidcJWKSUri: inputValue });
-  }
-
-  /**
-   * Change oidcClientId
-   */
-  changeOidcClientId(inputValue) {
-    this.setState({ oidcClientId: inputValue });
-  }
-
-  /**
-   * Change oidcClientSecret
-   */
-  changeOidcClientSecret(inputValue) {
-    this.setState({ oidcClientSecret: inputValue });
-  }
-
-  /**
-   * Change oidcAttrMapId
-   */
-  changeOidcAttrMapId(inputValue) {
-    this.setState({ oidcAttrMapId: inputValue });
-  }
-
-  /**
-   * Change oidcAttrMapUserName
-   */
-  changeOidcAttrMapUserName(inputValue) {
-    this.setState({ oidcAttrMapUserName: inputValue });
-  }
-
-  /**
-   * Change oidcAttrMapName
-   */
-  changeOidcAttrMapName(inputValue) {
-    this.setState({ oidcAttrMapName: inputValue });
-  }
-
-  /**
-   * Change oidcAttrMapEmail
-   */
-  changeOidcAttrMapEmail(inputValue) {
-    this.setState({ oidcAttrMapEmail: inputValue });
-  }
-
   /**
    * Switch sameUsernameTreatedAsIdenticalUser
    */
@@ -218,47 +106,45 @@ export default class AdminOidcSecurityContainer extends Container {
   /**
    * Update OpenID Connect
    */
-  async updateOidcSetting() {
-    const {
-      oidcProviderName,
-      oidcIssuerHost,
-      oidcAuthorizationEndpoint,
-      oidcTokenEndpoint,
-      oidcRevocationEndpoint,
-      oidcIntrospectionEndpoint,
-      oidcUserInfoEndpoint,
-      oidcEndSessionEndpoint,
-      oidcRegistrationEndpoint,
-      oidcJWKSUri,
-      oidcClientId,
-      oidcClientSecret,
-      oidcAttrMapId,
-      oidcAttrMapUserName,
-      oidcAttrMapName,
-      oidcAttrMapEmail,
-      isSameUsernameTreatedAsIdenticalUser,
-      isSameEmailTreatedAsIdenticalUser,
-    } = this.state;
-
-    let requestParams = {
-      oidcProviderName,
-      oidcIssuerHost,
-      oidcAuthorizationEndpoint,
-      oidcTokenEndpoint,
-      oidcRevocationEndpoint,
-      oidcIntrospectionEndpoint,
-      oidcUserInfoEndpoint,
-      oidcEndSessionEndpoint,
-      oidcRegistrationEndpoint,
-      oidcJWKSUri,
-      oidcClientId,
-      oidcClientSecret,
-      oidcAttrMapId,
-      oidcAttrMapUserName,
-      oidcAttrMapName,
-      oidcAttrMapEmail,
-      isSameUsernameTreatedAsIdenticalUser,
-      isSameEmailTreatedAsIdenticalUser,
+  async updateOidcSetting(formData) {
+    let requestParams = formData != null ? {
+      oidcProviderName: formData.oidcProviderName,
+      oidcIssuerHost: formData.oidcIssuerHost,
+      oidcAuthorizationEndpoint: formData.oidcAuthorizationEndpoint,
+      oidcTokenEndpoint: formData.oidcTokenEndpoint,
+      oidcRevocationEndpoint: formData.oidcRevocationEndpoint,
+      oidcIntrospectionEndpoint: formData.oidcIntrospectionEndpoint,
+      oidcUserInfoEndpoint: formData.oidcUserInfoEndpoint,
+      oidcEndSessionEndpoint: formData.oidcEndSessionEndpoint,
+      oidcRegistrationEndpoint: formData.oidcRegistrationEndpoint,
+      oidcJWKSUri: formData.oidcJWKSUri,
+      oidcClientId: formData.oidcClientId,
+      oidcClientSecret: formData.oidcClientSecret,
+      oidcAttrMapId: formData.oidcAttrMapId,
+      oidcAttrMapUserName: formData.oidcAttrMapUserName,
+      oidcAttrMapName: formData.oidcAttrMapName,
+      oidcAttrMapEmail: formData.oidcAttrMapEmail,
+      isSameUsernameTreatedAsIdenticalUser: formData.isSameUsernameTreatedAsIdenticalUser,
+      isSameEmailTreatedAsIdenticalUser: formData.isSameEmailTreatedAsIdenticalUser,
+    } : {
+      oidcProviderName: this.state.oidcProviderName,
+      oidcIssuerHost: this.state.oidcIssuerHost,
+      oidcAuthorizationEndpoint: this.state.oidcAuthorizationEndpoint,
+      oidcTokenEndpoint: this.state.oidcTokenEndpoint,
+      oidcRevocationEndpoint: this.state.oidcRevocationEndpoint,
+      oidcIntrospectionEndpoint: this.state.oidcIntrospectionEndpoint,
+      oidcUserInfoEndpoint: this.state.oidcUserInfoEndpoint,
+      oidcEndSessionEndpoint: this.state.oidcEndSessionEndpoint,
+      oidcRegistrationEndpoint: this.state.oidcRegistrationEndpoint,
+      oidcJWKSUri: this.state.oidcJWKSUri,
+      oidcClientId: this.state.oidcClientId,
+      oidcClientSecret: this.state.oidcClientSecret,
+      oidcAttrMapId: this.state.oidcAttrMapId,
+      oidcAttrMapUserName: this.state.oidcAttrMapUserName,
+      oidcAttrMapName: this.state.oidcAttrMapName,
+      oidcAttrMapEmail: this.state.oidcAttrMapEmail,
+      isSameUsernameTreatedAsIdenticalUser: this.state.isSameUsernameTreatedAsIdenticalUser,
+      isSameEmailTreatedAsIdenticalUser: this.state.isSameEmailTreatedAsIdenticalUser,
     };
 
     requestParams = await removeNullPropertyFromObject(requestParams);

+ 15 - 66
apps/app/src/client/services/AdminSamlSecurityContainer.js

@@ -98,62 +98,6 @@ export default class AdminSamlSecurityContainer extends Container {
     return 'AdminSamlSecurityContainer';
   }
 
-  /**
-   * Change samlEntryPoint
-   */
-  changeSamlEntryPoint(inputValue) {
-    this.setState({ samlEntryPoint: inputValue });
-  }
-
-  /**
-   * Change samlIssuer
-   */
-  changeSamlIssuer(inputValue) {
-    this.setState({ samlIssuer: inputValue });
-  }
-
-  /**
-   * Change samlCert
-   */
-  changeSamlCert(inputValue) {
-    this.setState({ samlCert: inputValue });
-  }
-
-  /**
-   * Change samlAttrMapId
-   */
-  changeSamlAttrMapId(inputValue) {
-    this.setState({ samlAttrMapId: inputValue });
-  }
-
-  /**
-   * Change samlAttrMapUsername
-   */
-  changeSamlAttrMapUserName(inputValue) {
-    this.setState({ samlAttrMapUsername: inputValue });
-  }
-
-  /**
-   * Change samlAttrMapMail
-   */
-  changeSamlAttrMapMail(inputValue) {
-    this.setState({ samlAttrMapMail: inputValue });
-  }
-
-  /**
-   * Change samlAttrMapFirstName
-   */
-  changeSamlAttrMapFirstName(inputValue) {
-    this.setState({ samlAttrMapFirstName: inputValue });
-  }
-
-  /**
-   * Change samlAttrMapLastName
-   */
-  changeSamlAttrMapLastName(inputValue) {
-    this.setState({ samlAttrMapLastName: inputValue });
-  }
-
   /**
    * Switch isSameUsernameTreatedAsIdenticalUser
    */
@@ -168,19 +112,24 @@ export default class AdminSamlSecurityContainer extends Container {
     this.setState({ isSameEmailTreatedAsIdenticalUser: !this.state.isSameEmailTreatedAsIdenticalUser });
   }
 
-  /**
-   * Change samlABLCRule
-   */
-  changeSamlABLCRule(inputValue) {
-    this.setState({ samlABLCRule: inputValue });
-  }
-
   /**
    * Update saml option
    */
-  async updateSamlSetting() {
-
-    let requestParams = {
+  async updateSamlSetting(formData) {
+
+    let requestParams = formData != null ? {
+      entryPoint: formData.samlEntryPoint,
+      issuer: formData.samlIssuer,
+      cert: formData.samlCert,
+      attrMapId: formData.samlAttrMapId,
+      attrMapUsername: formData.samlAttrMapUsername,
+      attrMapMail: formData.samlAttrMapMail,
+      attrMapFirstName: formData.samlAttrMapFirstName,
+      attrMapLastName: formData.samlAttrMapLastName,
+      isSameUsernameTreatedAsIdenticalUser: formData.isSameUsernameTreatedAsIdenticalUser,
+      isSameEmailTreatedAsIdenticalUser: formData.isSameEmailTreatedAsIdenticalUser,
+      ABLCRule: formData.samlABLCRule,
+    } : {
       entryPoint: this.state.samlEntryPoint,
       issuer: this.state.samlIssuer,
       cert: this.state.samlCert,

+ 1 - 0
apps/pdf-converter/.env

@@ -1 +1,2 @@
 PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
+PUPPETEER_CLUSTER_CONFIG={"maxConcurrency":1, "concurrency": 2}

+ 2 - 2
apps/pdf-converter/docker/README.md

@@ -5,10 +5,10 @@ GROWI PDF Converter Official docker image
 [![Node CI for pdf-converter](https://github.com/growilabs/growi/actions/workflows/ci-pdf-converter.yml/badge.svg)](https://github.com/growilabs/growi/actions/workflows/ci-pdf-converter.yml) [![docker-pulls](https://img.shields.io/docker/pulls/growilabs/pdf-converter.svg)](https://hub.docker.com/r/growilabs/pdf-converter/)
 
 
-Supported tags and respective Dockerfile links
+Dockerfile link
 ------------------------------------------------
 
-* [`1.0.0`, `latest` (Dockerfile)](https://github.com/growilabs/growi/blob/master/apps/pdf-converter/docker/Dockerfile)
+https://github.com/growilabs/growi/blob/master/apps/pdf-converter/docker/Dockerfile
 
 
 What is GROWI PDF Converter used for?

+ 1 - 1
apps/pdf-converter/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/pdf-converter",
-  "version": "1.1.3-RC.0",
+  "version": "1.2.1-RC.0",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
   "license": "MIT",

+ 48 - 11
apps/pdf-converter/src/service/pdf-convert.ts

@@ -5,6 +5,7 @@ import { pipeline as pipelinePromise } from 'node:stream/promises';
 import { OnInit } from '@tsed/common';
 import { Service } from '@tsed/di';
 import { Logger } from '@tsed/logger';
+import type { PuppeteerNodeLaunchOptions } from 'puppeteer';
 import { Cluster } from 'puppeteer-cluster';
 
 interface PageInfo {
@@ -37,8 +38,6 @@ interface JobInfo {
 class PdfConvertService implements OnInit {
   private puppeteerCluster: Cluster | undefined;
 
-  private maxConcurrency = 1;
-
   private convertRetryLimit = 5;
 
   private tmpOutputRootDir = '/tmp/page-bulk-export';
@@ -292,15 +291,8 @@ class PdfConvertService implements OnInit {
   private async initPuppeteerCluster(): Promise<void> {
     if (process.env.SKIP_PUPPETEER_INIT === 'true') return;
 
-    this.puppeteerCluster = await Cluster.launch({
-      concurrency: Cluster.CONCURRENCY_PAGE,
-      maxConcurrency: this.maxConcurrency,
-      workerCreationDelay: 10000,
-      puppeteerOptions: {
-        // ref) https://github.com/growilabs/growi/pull/10192
-        args: ['--no-sandbox'],
-      },
-    });
+    const config = this.getPuppeteerClusterConfig();
+    this.puppeteerCluster = await Cluster.launch(config);
 
     await this.puppeteerCluster.task(async ({ page, data: htmlString }) => {
       await page.setContent(htmlString, { waitUntil: 'domcontentloaded' });
@@ -326,6 +318,51 @@ class PdfConvertService implements OnInit {
     });
   }
 
+  /**
+   * Get puppeteer cluster configuration from environment variable
+   * @returns merged cluster configuration
+   */
+  private getPuppeteerClusterConfig(): Record<string, any> {
+    // Default cluster configuration
+    const defaultConfig = {
+      concurrency: Cluster.CONCURRENCY_CONTEXT,
+      maxConcurrency: 1,
+      workerCreationDelay: 10000,
+      // Puppeteer options (not configurable for security reasons)
+      // ref) https://github.com/growilabs/growi/pull/10192
+      puppeteerOptions: {
+        args: ['--no-sandbox'],
+      },
+    };
+
+    // Parse configuration from environment variable
+    let customConfig: Record<string, any> = {};
+    if (process.env.PUPPETEER_CLUSTER_CONFIG) {
+      try {
+        customConfig = JSON.parse(process.env.PUPPETEER_CLUSTER_CONFIG);
+      } catch (err) {
+        this.logger.warn(
+          'Failed to parse PUPPETEER_CLUSTER_CONFIG, using default values',
+          err,
+        );
+      }
+    }
+
+    // Remove puppeteerOptions from custom config if present (not allowed for security)
+    if (customConfig.puppeteerOptions) {
+      this.logger.warn(
+        'puppeteerOptions configuration is not allowed for security reasons and will be ignored',
+      );
+      delete customConfig.puppeteerOptions;
+    }
+
+    // Merge configurations (customConfig overrides defaultConfig, except puppeteerOptions)
+    return {
+      ...defaultConfig,
+      ...customConfig,
+    };
+  }
+
   /**
    * Get parent path from given path
    * @param path target path

+ 1 - 1
apps/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slackbot-proxy",
-  "version": "7.3.6-slackbot-proxy.0",
+  "version": "7.3.7-slackbot-proxy.0",
   "license": "MIT",
   "private": "true",
   "scripts": {

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "7.3.6",
+  "version": "7.3.7-RC.0",
   "description": "Team collaboration software using markdown",
   "license": "MIT",
   "private": "true",