Browse Source

Merge pull request #7942 from weseek/master

Release v6.1.11
Yuki Takei 2 years ago
parent
commit
52e3c13cd1
47 changed files with 180 additions and 39 deletions
  1. 1 1
      apps/app/package.json
  2. 3 0
      apps/app/public/static/locales/en_US/admin.json
  3. 3 0
      apps/app/public/static/locales/ja_JP/admin.json
  4. 3 0
      apps/app/public/static/locales/zh_CN/admin.json
  5. 17 0
      apps/app/src/components/Admin/ForbiddenPage.tsx
  6. 1 1
      apps/app/src/components/LoginForm.tsx
  7. 2 1
      apps/app/src/components/SearchPage/SearchResultContent.tsx
  8. 5 0
      apps/app/src/pages/admin/[...path].page.tsx
  9. 5 1
      apps/app/src/pages/admin/app.page.tsx
  10. 5 1
      apps/app/src/pages/admin/audit-log.page.tsx
  11. 4 0
      apps/app/src/pages/admin/customize.page.tsx
  12. 5 0
      apps/app/src/pages/admin/data-transfer.page.tsx
  13. 5 0
      apps/app/src/pages/admin/export.page.tsx
  14. 4 0
      apps/app/src/pages/admin/global-notification/[globalNotificationId].page.tsx
  15. 4 0
      apps/app/src/pages/admin/global-notification/new.page.tsx
  16. 5 0
      apps/app/src/pages/admin/importer.page.tsx
  17. 9 1
      apps/app/src/pages/admin/index.page.tsx
  18. 4 0
      apps/app/src/pages/admin/markdown.page.tsx
  19. 5 0
      apps/app/src/pages/admin/notification.page.tsx
  20. 5 0
      apps/app/src/pages/admin/plugins.page.tsx
  21. 5 0
      apps/app/src/pages/admin/search.page.tsx
  22. 5 0
      apps/app/src/pages/admin/security.page.tsx
  23. 5 1
      apps/app/src/pages/admin/slack-integration-legacy.page.tsx
  24. 5 0
      apps/app/src/pages/admin/slack-integration.page.tsx
  25. 5 0
      apps/app/src/pages/admin/user-group-detail/[userGroupId].page.tsx
  26. 5 0
      apps/app/src/pages/admin/user-groups.page.tsx
  27. 4 0
      apps/app/src/pages/admin/users/external-accounts.page.tsx
  28. 4 1
      apps/app/src/pages/admin/users/index.page.tsx
  29. 7 0
      apps/app/src/pages/me/[[...path]].page.tsx
  30. 1 0
      apps/app/src/pages/utils/commons.ts
  31. 0 1
      apps/app/src/server/middlewares/admin-required.js
  32. 11 6
      apps/app/src/server/routes/apiv3/security-settings/index.js
  33. 4 0
      apps/app/src/utils/admin-page-util.ts
  34. 1 1
      apps/slackbot-proxy/package.json
  35. 1 1
      package.json
  36. 1 1
      packages/core/package.json
  37. 1 1
      packages/hackmd/package.json
  38. 1 1
      packages/presentation/package.json
  39. 1 1
      packages/preset-templates/package.json
  40. 1 1
      packages/preset-themes/package.json
  41. 1 1
      packages/remark-attachment-refs/package.json
  42. 1 1
      packages/remark-drawio/package.json
  43. 1 1
      packages/remark-growi-directive/package.json
  44. 1 1
      packages/remark-lsx/package.json
  45. 1 1
      packages/slack/package.json
  46. 1 1
      packages/ui/package.json
  47. 11 11
      yarn.lock

+ 1 - 1
apps/app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/app",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "license": "MIT",
   "scripts": {
     "//// for production": "",

+ 3 - 0
apps/app/public/static/locales/en_US/admin.json

@@ -1055,5 +1055,8 @@
     "activate_plugin_success": "Succeeded to activating {{pluginName}}",
     "deactivate_plugin_success": "Succeeded to deactivate {{pluginName}}",
     "remove_plugin_success": "Succeeded to removing {{pluginName}}"
+  },
+  "forbidden_page": {
+    "do_not_have_admin_permission": "Users without administrative rights cannot access the administration screen."
   }
 }

+ 3 - 0
apps/app/public/static/locales/ja_JP/admin.json

@@ -1063,5 +1063,8 @@
     "activate_plugin_success": "{{pluginName}}を有効化しました",
     "deactivate_plugin_success": "{{pluginName}}を無効化しました",
     "remove_plugin_success": "{{pluginName}}を削除しました"
+  },
+  "forbidden_page": {
+    "do_not_have_admin_permission": "管理者権限のないユーザーでは管理画面にはアクセスできません。"
   }
 }

+ 3 - 0
apps/app/public/static/locales/zh_CN/admin.json

@@ -1063,5 +1063,8 @@
     "activate_plugin_success": "Succeeded to activating {{pluginName}}",
     "deactivate_plugin_success": "Succeeded to deactivate {{pluginName}}",
     "remove_plugin_success": "Succeeded to removing {{pluginName}}"
+  },
+  "forbidden_page": {
+    "do_not_have_admin_permission": "没有管理权限的用户无法访问管理屏幕。"
   }
 }

+ 17 - 0
apps/app/src/components/Admin/ForbiddenPage.tsx

@@ -0,0 +1,17 @@
+import React from 'react';
+
+import DefaultErrorPage from 'next/error';
+import { useTranslation } from 'react-i18next';
+
+
+export const ForbiddenPage = (): JSX.Element => {
+  const { t } = useTranslation('admin');
+
+  const errorMessage = t('forbidden_page.do_not_have_admin_permission');
+
+  return (
+    <>
+      <DefaultErrorPage statusCode={403} title={errorMessage} />
+    </>
+  );
+};

+ 1 - 1
apps/app/src/components/LoginForm.tsx

@@ -18,7 +18,6 @@ import { CompleteUserRegistration } from './CompleteUserRegistration';
 
 import styles from './LoginForm.module.scss';
 
-
 type LoginFormProps = {
   username?: string,
   name?: string,
@@ -335,6 +334,7 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
       resetRegisterErrors();
 
       const { redirectTo } = res.data;
+
       if (redirectTo != null) {
         router.push(redirectTo);
       }

+ 2 - 1
apps/app/src/components/SearchPage/SearchResultContent.tsx

@@ -77,7 +77,8 @@ const scrollToFirstHighlightedKeyword = (scrollElement: HTMLElement): void => {
     return;
   }
 
-  animateScroll.scrollTo(toElem.offsetTop - SCROLL_OFFSET_TOP, {
+  const distance = toElem.getBoundingClientRect().top - scrollElement.getBoundingClientRect().top - SCROLL_OFFSET_TOP;
+  animateScroll.scrollMore(distance, {
     containerId: scrollElement.id,
     duration: 200,
   });

+ 5 - 0
apps/app/src/pages/admin/[...path].page.tsx

@@ -11,12 +11,17 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const AdminNotFoundPage = dynamic(() => import('~/components/Admin/NotFoundPage').then(mod => mod.AdminNotFoundPage), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminAppPage: NextPage<CommonProps> = (props) => {
   useIsMaintenanceMode(props.isMaintenanceMode);
   useCurrentUser(props.currentUser ?? null);
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <AdminLayout>
       <AdminNotFoundPage />

+ 5 - 1
apps/app/src/pages/admin/app.page.tsx

@@ -7,7 +7,6 @@ import dynamic from 'next/dynamic';
 import Head from 'next/head';
 import { Container, Provider } from 'unstated';
 
-
 import AdminAppContainer from '~/client/services/AdminAppContainer';
 import { CommonProps, generateCustomTitle } from '~/pages/utils/commons';
 import { useCurrentUser } from '~/stores/context';
@@ -18,6 +17,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const AppSettingsPageContents = dynamic(() => import('~/components/Admin/App/AppSettingsPageContents'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminAppPage: NextPage<CommonProps> = (props) => {
@@ -34,6 +34,10 @@ const AdminAppPage: NextPage<CommonProps> = (props) => {
 
   const title = generateCustomTitle(props, t('headers.app_settings'));
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <Provider inject={[...injectableContainers]}>
       <AdminLayout componentTitle={title}>

+ 5 - 1
apps/app/src/pages/admin/audit-log.page.tsx

@@ -13,8 +13,8 @@ import { useCurrentUser, useAuditLogEnabled, useAuditLogAvailableActions } from
 import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
-
 const AuditLogManagement = dynamic(() => import('~/components/Admin/AuditLogManagement').then(mod => mod.AuditLogManagement), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -32,6 +32,10 @@ const AdminAuditLogPage: NextPage<Props> = (props) => {
   const title = t('audit_log_management.audit_log');
   const headTitle = generateCustomTitle(props, title);
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <AdminLayout componentTitle={title}>
       <Head>

+ 4 - 0
apps/app/src/pages/admin/customize.page.tsx

@@ -16,6 +16,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const CustomizeSettingContents = dynamic(() => import('~/components/Admin/Customize/Customize'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -40,6 +41,9 @@ const AdminCustomizeSettingsPage: NextPage<Props> = (props) => {
     injectableContainers.push(adminCustomizeContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 5 - 0
apps/app/src/pages/admin/data-transfer.page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const G2GDataTransferPage = dynamic(() => import('~/components/Admin/G2GDataTransfer'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps;
@@ -32,6 +33,10 @@ const DataTransferPage: NextPage<Props> = (props) => {
     injectableContainers.push(adminAppContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <Provider inject={[...injectableContainers]}>
       <AdminLayout componentTitle={title}>

+ 5 - 0
apps/app/src/pages/admin/export.page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const ExportArchiveDataPage = dynamic(() => import('~/components/Admin/ExportArchiveDataPage'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminExportDataArchivePage: NextPage<CommonProps> = (props) => {
@@ -30,6 +31,10 @@ const AdminExportDataArchivePage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminAppContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <Provider inject={[...injectableContainers]}>
       <AdminLayout componentTitle={componentTitle}>

+ 4 - 0
apps/app/src/pages/admin/global-notification/[globalNotificationId].page.tsx

@@ -21,6 +21,7 @@ import { retrieveServerSideProps } from '../../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const ManageGlobalNotification = dynamic(() => import('~/components/Admin/Notification/ManageGlobalNotification'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminGlobalNotificationNewPage: NextPage<CommonProps> = (props) => {
@@ -54,6 +55,9 @@ const AdminGlobalNotificationNewPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminNotificationContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    <ForbiddenPage />;
+  }
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 4 - 0
apps/app/src/pages/admin/global-notification/new.page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const ManageGlobalNotification = dynamic(() => import('~/components/Admin/Notification/ManageGlobalNotification'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminGlobalNotificationNewPage: NextPage<CommonProps> = (props) => {
@@ -29,6 +30,9 @@ const AdminGlobalNotificationNewPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminNotificationContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    <ForbiddenPage />;
+  }
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 5 - 0
apps/app/src/pages/admin/importer.page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const DataImportPageContents = dynamic(() => import('~/components/Admin/ImportData/ImportDataPageContents'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminDataImportPage: NextPage<CommonProps> = (props) => {
@@ -30,6 +31,10 @@ const AdminDataImportPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminImportContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 9 - 1
apps/app/src/pages/admin/index.page.tsx

@@ -10,12 +10,16 @@ import { Container, Provider } from 'unstated';
 import AdminHomeContainer from '~/client/services/AdminHomeContainer';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { CommonProps, generateCustomTitle } from '~/pages/utils/commons';
-import { useCurrentUser, useGrowiCloudUri, useGrowiAppIdForGrowiCloud } from '~/stores/context';
+import {
+  useCurrentUser, useGrowiCloudUri, useGrowiAppIdForGrowiCloud,
+} from '~/stores/context';
+
 
 import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const AdminHome = dynamic(() => import('~/components/Admin/AdminHome/AdminHome'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -45,6 +49,10 @@ const AdminHomePage: NextPage<Props> = (props) => {
     injectableContainers.push(adminHomeContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 4 - 0
apps/app/src/pages/admin/markdown.page.tsx

@@ -16,6 +16,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const MarkDownSettingContents = dynamic(() => import('~/components/Admin/MarkdownSetting/MarkDownSettingContents'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminMarkdownPage: NextPage<CommonProps> = (props) => {
@@ -32,6 +33,9 @@ const AdminMarkdownPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminMarkDownContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 5 - 0
apps/app/src/pages/admin/notification.page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const NotificationSetting = dynamic(() => import('~/components/Admin/Notification/NotificationSetting'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminExternalNotificationPage: NextPage<CommonProps> = (props) => {
@@ -31,6 +32,10 @@ const AdminExternalNotificationPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminNotificationContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 5 - 0
apps/app/src/pages/admin/plugins.page.tsx

@@ -21,6 +21,7 @@ const PluginsExtensionPageContents = dynamic(
   () => import('~/features/growi-plugin/client/components/Admin').then(mod => mod.PluginsExtensionPageContents),
   { ssr: false },
 );
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminAppPage: NextPage<CommonProps> = (props) => {
@@ -36,6 +37,10 @@ const AdminAppPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminAppContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <Provider inject={[...injectableContainers]}>
       <AdminLayout componentTitle={title} >

+ 5 - 0
apps/app/src/pages/admin/search.page.tsx

@@ -15,6 +15,7 @@ const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { s
 const FullTextSearchManagement = dynamic(
   () => import('~/components/Admin//FullTextSearchManagement').then(mod => mod.FullTextSearchManagement), { ssr: false },
 );
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -30,6 +31,10 @@ const AdminFullTextSearchManagementPage: NextPage<Props> = (props) => {
   const title = t('full_text_search_management.full_text_search_management');
   const headTitle = generateCustomTitle(props, title);
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <AdminLayout componentTitle={title}>
       <Head>

+ 5 - 0
apps/app/src/pages/admin/security.page.tsx

@@ -22,6 +22,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const SecurityManagement = dynamic(() => import('~/components/Admin/Security/SecurityManagement'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -64,6 +65,10 @@ const AdminSecuritySettingsPage: NextPage<Props> = (props) => {
     }
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <Provider inject={[...adminSecurityContainers]}>
       <AdminLayout componentTitle={componentTitle}>

+ 5 - 1
apps/app/src/pages/admin/slack-integration-legacy.page.tsx

@@ -15,7 +15,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const LegacySlackIntegration = dynamic(() => import('~/components/Admin/LegacySlackIntegration/LegacySlackIntegration'), { ssr: false });
-
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 const AdminLegacySlackIntegrationPage: NextPage<CommonProps> = (props) => {
   const { t } = useTranslation('admin');
@@ -30,6 +30,10 @@ const AdminLegacySlackIntegrationPage: NextPage<CommonProps> = (props) => {
     injectableContainers.push(adminSlackIntegrationLegacyContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 5 - 0
apps/app/src/pages/admin/slack-integration.page.tsx

@@ -14,6 +14,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const SlackIntegration = dynamic(() => import('~/components/Admin/SlackIntegration/SlackIntegration'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -29,6 +30,10 @@ const AdminSlackIntegrationPage: NextPage<Props> = (props) => {
   const componentTitle = t('slack_integration.slack_integration');
   const pageTitle = generateCustomTitle(props, componentTitle);
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <AdminLayout componentTitle={componentTitle}>
       <Head>

+ 5 - 0
apps/app/src/pages/admin/user-group-detail/[userGroupId].page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const UserGroupDetailPage = dynamic(() => import('~/components/Admin/UserGroupDetail/UserGroupDetailPage'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 type Props = CommonProps & {
   isAclEnabled: boolean
@@ -34,6 +35,10 @@ const AdminUserGroupDetailPage: NextPage<Props> = (props: Props) => {
 
   useIsAclEnabled(props.isAclEnabled);
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <AdminLayout componentTitle={title}>
       <Head>

+ 5 - 0
apps/app/src/pages/admin/user-groups.page.tsx

@@ -13,6 +13,7 @@ import { retrieveServerSideProps } from '../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const UserGroupPage = dynamic(() => import('~/components/Admin/UserGroup/UserGroupPage').then(mod => mod.UserGroupPage), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -28,6 +29,10 @@ const AdminUserGroupPage: NextPage<Props> = (props) => {
   const title = t('user_group_management.user_group_management');
   const headTitle = generateCustomTitle(props, title);
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
+
   return (
     <AdminLayout componentTitle={title}>
       <Head>

+ 4 - 0
apps/app/src/pages/admin/users/external-accounts.page.tsx

@@ -15,6 +15,7 @@ import { retrieveServerSideProps } from '../../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
 const ManageExternalAccount = dynamic(() => import('~/components/Admin/ManageExternalAccount'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 const AdminUserManagementPage: NextPage<CommonProps> = (props) => {
@@ -32,6 +33,9 @@ const AdminUserManagementPage: NextPage<CommonProps> = (props) => {
     );
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 4 - 1
apps/app/src/pages/admin/users/index.page.tsx

@@ -15,8 +15,8 @@ import { useCurrentUser, useIsMailerSetup } from '~/stores/context';
 import { retrieveServerSideProps } from '../../../utils/admin-page-util';
 
 const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
-
 const UserManagement = dynamic(() => import('~/components/Admin/UserManagement'), { ssr: false });
+const ForbiddenPage = dynamic(() => import('~/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
 
 
 type Props = CommonProps & {
@@ -39,6 +39,9 @@ const AdminUserManagementPage: NextPage<Props> = (props) => {
     injectableContainers.push(adminUsersContainer);
   }
 
+  if (props.isAccessDeniedForNonAdminUser) {
+    return <ForbiddenPage />;
+  }
 
   return (
     <Provider inject={[...injectableContainers]}>

+ 7 - 0
apps/app/src/pages/me/[[...path]].page.tsx

@@ -72,6 +72,13 @@ const MePage: NextPageWithLayout<Props> = (props: Props) => {
 
   const getTargetPageToRender = (pagesMap, keys): {title: string, component: JSX.Element} => {
     return keys.reduce((pagesMap, key) => {
+      const page = pagesMap[key];
+      if (page == null) {
+        return {
+          title: 'NotFoundPage',
+          component: <h2>{t('commons:not_found_page.page_not_exist')}</h2>,
+        };
+      }
       return pagesMap[key];
     }, pagesMap);
   };

+ 1 - 0
apps/app/src/pages/utils/commons.ts

@@ -31,6 +31,7 @@ export type CommonProps = {
   redirectDestination: string | null,
   isDefaultLogo: boolean,
   growiCloudUri: string,
+  isAccessDeniedForNonAdminUser?: boolean,
   currentUser?: IUserHasId,
   forcedColorScheme?: ColorScheme,
   sidebarConfig: ISidebarConfig,

+ 0 - 1
apps/app/src/server/middlewares/admin-required.js

@@ -26,5 +26,4 @@ module.exports = (crowi, fallback = null) => {
     }
     return res.redirect('/login');
   };
-
 };

+ 11 - 6
apps/app/src/server/routes/apiv3/security-settings/index.js

@@ -1,4 +1,5 @@
 import { ErrorV3 } from '@growi/core';
+import xss from 'xss';
 
 import { SupportedAction } from '~/interfaces/activity';
 import { PageDeleteConfigValue } from '~/interfaces/page-delete-config';
@@ -799,13 +800,17 @@ module.exports = (crowi) => {
    *                  $ref: '#/components/schemas/LocalSetting'
    */
   router.put('/local-setting', loginRequiredStrictly, adminRequired, addActivity, validator.localSetting, apiV3FormValidator, async(req, res) => {
-    const requestParams = {
-      'security:registrationMode': req.body.registrationMode,
-      'security:registrationWhitelist': req.body.registrationWhitelist,
-      'security:passport-local:isPasswordResetEnabled': req.body.isPasswordResetEnabled,
-      'security:passport-local:isEmailAuthenticationEnabled': req.body.isEmailAuthenticationEnabled,
-    };
     try {
+      const sanitizedRegistrationWhitelist = req.body.registrationWhitelist
+        .map(line => xss(line, { stripIgnoreTag: true }));
+
+      const requestParams = {
+        'security:registrationMode': req.body.registrationMode,
+        'security:registrationWhitelist': sanitizedRegistrationWhitelist,
+        'security:passport-local:isPasswordResetEnabled': req.body.isPasswordResetEnabled,
+        'security:passport-local:isEmailAuthenticationEnabled': req.body.isEmailAuthenticationEnabled,
+      };
+
       await updateAndReloadStrategySettings('local', requestParams);
 
       const localSettingParams = {

+ 4 - 0
apps/app/src/utils/admin-page-util.ts

@@ -44,6 +44,10 @@ export const retrieveServerSideProps: any = async(
     props.currentUser = user.toObject();
   }
 
+  props.isAccessDeniedForNonAdminUser = props.currentUser == null
+    ? true
+    : !props.currentUser.admin;
+
   await injectNextI18NextConfigurations(context, props, ['admin', 'commons']);
 
   return {

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

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slackbot-proxy",
-  "version": "6.1.10-slackbot-proxy.0",
+  "version": "6.1.11-slackbot-proxy.0",
   "license": "MIT",
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

+ 1 - 1
packages/core/package.json

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

+ 1 - 1
packages/hackmd/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/hackmd",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "description": "GROWI js and css files to use hackmd",
   "license": "MIT",
   "type": "module",

+ 1 - 1
packages/presentation/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/presentation",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "description": "GROWI plugin for presentation",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/preset-templates/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/preset-templates",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "scripts": {
     "test": "vitest run",
     "version": "yarn version --no-git-tag-version --preid=RC"

+ 1 - 1
packages/preset-themes/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@growi/preset-themes",
   "description": "GROWI preset themes",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "license": "MIT",
   "main": "dist/libs/preset-themes.umd.js",
   "module": "dist/libs/preset-themes.mjs",

+ 1 - 1
packages/remark-attachment-refs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/remark-attachment-refs",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/remark-drawio/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/remark-drawio",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "description": "remark plugin to draw diagrams with draw.io (diagrams.net)",
   "license": "MIT",
   "keywords": [

+ 1 - 1
packages/remark-growi-directive/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/remark-growi-directive",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "description": "remark plugin to support GROWI plugin (forked from remark-directive@2.0.1)",
   "license": "MIT",
   "keywords": [

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

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

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@growi/slack",
-  "version": "6.1.10",
+  "version": "6.1.11-RC.0",
   "license": "MIT",
   "main": "dist/index.js",
   "module": "dist/index.mjs",

+ 1 - 1
packages/ui/package.json

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

+ 11 - 11
yarn.lock

@@ -2495,13 +2495,13 @@
     xdg-basedir "^4.0.0"
 
 "@growi/core@link:packages/core":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     bson-objectid "^2.0.4"
     escape-string-regexp "^4.0.0"
 
 "@growi/hackmd@link:packages/hackmd":
-  version "6.1.10"
+  version "6.1.11-RC.0"
 
 "@growi/pluginkit@link:packages/pluginkit":
   version "0.1.0"
@@ -2510,18 +2510,18 @@
     extensible-custom-error "^0.0.7"
 
 "@growi/presentation@link:packages/presentation":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     "@growi/core" "link:packages/core"
 
 "@growi/preset-templates@link:packages/preset-templates":
-  version "6.1.10"
+  version "6.1.11-RC.0"
 
 "@growi/preset-themes@link:packages/preset-themes":
-  version "6.1.10"
+  version "6.1.11-RC.0"
 
 "@growi/remark-attachment-refs@link:packages/remark-attachment-refs":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     "@growi/core" "link:packages/core"
     "@growi/remark-growi-directive" "link:packages/remark-growi-directive"
@@ -2530,12 +2530,12 @@
     universal-bunyan "^0.9.2"
 
 "@growi/remark-drawio@link:packages/remark-drawio":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     pako "^2.1.0"
 
 "@growi/remark-growi-directive@link:packages/remark-growi-directive":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     "@types/mdast" "^3.0.0"
     "@types/unist" "^2.0.0"
@@ -2552,7 +2552,7 @@
     uvu "^0.5.0"
 
 "@growi/remark-lsx@link:packages/remark-lsx":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     "@growi/core" "link:packages/core"
     "@growi/remark-growi-directive" "link:packages/remark-growi-directive"
@@ -2563,7 +2563,7 @@
     swr "^2.0.3"
 
 "@growi/slack@link:packages/slack":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     "@slack/oauth" "^2.0.1"
     axios "^0.24.0"
@@ -2576,7 +2576,7 @@
     url-join "^4.0.0"
 
 "@growi/ui@link:packages/ui":
-  version "6.1.10"
+  version "6.1.11-RC.0"
   dependencies:
     "@growi/core" "link:packages/core"