diff --git a/docs/docs/settings/global.md b/docs/docs/settings/global.md index 66dddda681..c729c61bb4 100644 --- a/docs/docs/settings/global.md +++ b/docs/docs/settings/global.md @@ -27,6 +27,8 @@ Configuration of basic server settings: {{ globalsetting("INVENTREE_INSTANCE_TITLE") }} {{ globalsetting("INVENTREE_INSTANCE_ID", default="Randomly generated value") }} {{ globalsetting("INVENTREE_ANNOUNCE_ID") }} +{{ globalsetting("INVENTREE_SHOW_SUPERUSER_BANNER") }} +{{ globalsetting("INVENTREE_SHOW_ADMIN_BANNER") }} {{ globalsetting("INVENTREE_RESTRICT_ABOUT") }} {{ globalsetting("DISPLAY_FULL_NAMES") }} {{ globalsetting("DISPLAY_PROFILE_INFO") }} diff --git a/docs/docs/start/config.md b/docs/docs/start/config.md index b25dfc1a98..37688941f0 100644 --- a/docs/docs/start/config.md +++ b/docs/docs/start/config.md @@ -535,7 +535,6 @@ Set the `INVENTREE_FRONTEND_SETTINGS` Environment variable to a JSON object or u | `url_compatibility` | Support compatibility with "legacy" URLs? | `true` | | `sentry_dsn` | Set a Sentry DSN url | *Not specified* | | `mobile_mode` | Controls if InvenTree web UI can be used by mobile devices. There are 3 options: `default` - does not allow mobile devices; `allow-ignore` - shows a mobile device detected banner with a button to ignore this warning AT THE USERS OWN RISK; `allow-always` - skips the mobile check and allows mobile devices always (of course at the server admins OWN RISK) | `default` | -| `dangerous_hide_evelevated_alert` | Hides the elevated permissions alert in the UI. This is a dangerous option as using the UI with elevated permissions is against the threat model. | `false` | E.g. to allow mobile devices to ignore the mobile check, use the following Environment variable: diff --git a/src/backend/InvenTree/common/setting/system.py b/src/backend/InvenTree/common/setting/system.py index f026bbcd94..bfab416a80 100644 --- a/src/backend/InvenTree/common/setting/system.py +++ b/src/backend/InvenTree/common/setting/system.py @@ -235,6 +235,18 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { 'validator': bool, 'default': False, }, + 'INVENTREE_SHOW_SUPERUSER_BANNER': { + 'name': _('Show superuser banner'), + 'description': _('Show a warning banner in the UI when logged in as superuser'), + 'validator': bool, + 'default': True, + }, + 'INVENTREE_SHOW_ADMIN_BANNER': { + 'name': _('Show admin banner'), + 'description': _('Show a warning banner in the UI when logged in as admin'), + 'validator': bool, + 'default': False, + }, 'INVENTREE_COMPANY_NAME': { 'name': _('Company name'), 'description': _('Internal company name'), diff --git a/src/frontend/src/components/nav/Header.tsx b/src/frontend/src/components/nav/Header.tsx index a70b87819b..c935a388ba 100644 --- a/src/frontend/src/components/nav/Header.tsx +++ b/src/frontend/src/components/nav/Header.tsx @@ -4,6 +4,7 @@ import { Container, Group, Indicator, + Paper, Tabs, Text, Tooltip, @@ -14,7 +15,7 @@ import { useDocumentVisibility, useHotkeys } from '@mantine/hooks'; -import { IconBell, IconSearch } from '@tabler/icons-react'; +import { IconBell, IconSearch, IconUserBolt } from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import { type ReactNode, useEffect, useMemo, useState } from 'react'; import { useMatch, useNavigate } from 'react-router-dom'; @@ -58,8 +59,6 @@ export function Header() { searchDrawerOpened, { open: openSearchDrawer, close: closeSearchDrawer } ] = useDisclosure(false); - const [elevatedAlertClosed, setElevatedAlertClosed] = - useState(false); useHotkeys([ [ @@ -130,13 +129,25 @@ export function Header() { else closeNavDrawer(); }, [navigationOpen]); - const showElevated = useMemo( - () => - (user?.is_staff || user?.is_superuser || false) && - !elevatedAlertClosed && - !window.INVENTREE_SETTINGS.dangerous_hide_evelevated_alert, - [user, elevatedAlertClosed] - ); + const [showSuperuserAlert, setShowSuperuserAlert] = useState(true); + + const showElevated = useMemo(() => { + if ( + user?.is_superuser && + globalSettings.isSet('INVENTREE_SHOW_SUPERUSER_BANNER', true) + ) { + return true; + } + + if ( + user?.is_staff && + globalSettings.isSet('INVENTREE_SHOW_ADMIN_BANNER', true) + ) { + return true; + } + + return false; + }, [user, showSuperuserAlert, globalSettings]); const headerStyle: any = useMemo(() => { const sticky: boolean = userSettings.isSet('STICKY_HEADER', true); @@ -210,18 +221,22 @@ export function Header() { - {showElevated && user && ( - setElevatedAlertClosed(true)} - > - - {t`The current user has elevated privileges and should not be used for regular usage.`} - {errorCodeLink('INVE-W14')} - - + {showElevated && (user?.is_superuser || user?.is_staff) && ( + + } + color={user.is_superuser ? 'red' : 'orange'} + title={user.is_superuser ? t`Superuser Mode` : t`Admin Mode`} + withCloseButton + onClose={() => setShowSuperuserAlert(false)} + p={5} + > + + {t`The current user has elevated privileges and should not be used for regular usage.`}{' '} + {errorCodeLink('INVE-W14')} + + + )} ); diff --git a/src/frontend/src/main.tsx b/src/frontend/src/main.tsx index 54b2ffc7da..d5fc90bd52 100644 --- a/src/frontend/src/main.tsx +++ b/src/frontend/src/main.tsx @@ -36,7 +36,6 @@ declare global { sentry_dsn?: string; environment?: string; mobile_mode?: 'default' | 'allow-ignore' | 'allow-always'; - dangerous_hide_evelevated_alert?: boolean; }; react: typeof React; React: typeof React; diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 27722a4395..3ded92663d 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -50,10 +50,12 @@ export default function SystemSettings() { keys={[ 'INVENTREE_BASE_URL', 'INVENTREE_COMPANY_NAME', - 'INVENTREE_INSTANCE_ID', - 'INVENTREE_ANNOUNCE_ID', 'INVENTREE_INSTANCE', 'INVENTREE_INSTANCE_TITLE', + 'INVENTREE_INSTANCE_ID', + 'INVENTREE_ANNOUNCE_ID', + 'INVENTREE_SHOW_SUPERUSER_BANNER', + 'INVENTREE_SHOW_ADMIN_BANNER', 'INVENTREE_RESTRICT_ABOUT', 'DISPLAY_FULL_NAMES', 'DISPLAY_PROFILE_INFO', diff --git a/src/frontend/tests/pui_permissions.spec.ts b/src/frontend/tests/pui_permissions.spec.ts index 162accc3f4..8bb46e8d5a 100644 --- a/src/frontend/tests/pui_permissions.spec.ts +++ b/src/frontend/tests/pui_permissions.spec.ts @@ -18,6 +18,10 @@ test('Permissions - Admin', async ({ browser }) => { url: '/settings/admin/' }); + // Check for superuser banner + await page.getByText('Superuser Mode').waitFor(); + await page.getByText('The current user has elevated').waitFor(); + // Check for expected tabs await loadTab(page, 'Machines'); await loadTab(page, 'Plugins');