From 2cc8eebf378db298084a91013778d18a02ec31ac Mon Sep 17 00:00:00 2001 From: Yinyin Liu Date: Tue, 2 Jun 2026 10:06:04 +0200 Subject: [PATCH] grey out main admin and the user himself in avoidance of deleting the account by accident --- .../App/Backend/DataTypes/Methods/Session.cs | 8 ++-- .../src/content/dashboards/Users/User.tsx | 41 +++++++++++++------ typescript/frontend-marios2/src/lang/de.json | 2 + typescript/frontend-marios2/src/lang/en.json | 2 + typescript/frontend-marios2/src/lang/fr.json | 2 + typescript/frontend-marios2/src/lang/it.json | 2 + 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 5a3edd808..851a5fdca 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -375,7 +375,8 @@ public static class SessionMethods var emailOwner = Db.GetUserByEmail(editedUser.Email); return sessionUser.UserType != 0 - && originalUser.Id != 0 // never edit the root user + && originalUser.Id != 0 // belt: legacy root-id sentinel + && originalUser.ParentId > 0 // never edit the main/root admin (parentless top user; ParentId<=0, Id NOT necessarily 0) && (sessionUser.UserType == 2 || sessionUser.HasAccessTo(originalUser)) // admins may edit any user && (emailOwner is null || emailOwner.Id == editedUser.Id) // email not taken by another user && editedUser @@ -402,8 +403,9 @@ public static class SessionMethods return sessionUser is not null && userToDelete is not null && sessionUser.UserType !=0 - && userToDelete.Id != 0 // never delete the root user - && userToDelete.Id != sessionUser.Id // never self-delete (avoid lockout) + && userToDelete.Id != 0 // belt: legacy root-id sentinel + && userToDelete.ParentId > 0 // never delete the main/root admin (the parentless top user; ParentId<=0, Id NOT necessarily 0) + && userToDelete.Id != sessionUser.Id // never self-delete (avoid lockout) && (sessionUser.UserType == 2 || sessionUser.HasAccessTo(userToDelete)) // admins may delete any user && Db.Delete(userToDelete); } diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx index 074c1c2bd..bec15752a 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Users/User.tsx @@ -16,6 +16,7 @@ import { Tab, Tabs, TextField, + Tooltip, Typography, useTheme } from '@mui/material'; @@ -24,6 +25,7 @@ import Button from '@mui/material/Button'; import axiosConfig from 'src/Resources/axiosConfig'; import { InnovEnergyUser } from 'src/interfaces/UserTypes'; import { TokenContext } from 'src/contexts/tokenContext'; +import { UserContext } from 'src/contexts/userContext'; import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper'; import { FormattedMessage, useIntl } from 'react-intl'; import UserAccess from '../ManageAccess/UserAccess'; @@ -43,6 +45,8 @@ function User(props: singleUserProps) { const [formValues, setFormValues] = useState(props.current_user); const tokencontext = useContext(TokenContext); const { removeToken } = tokencontext; + const userContext = useContext(UserContext); + const loggedInUser = userContext?.currentUser; const tabs = [ { value: 'user', label: intl.formatMessage({ id: 'user' }) }, { value: 'manage', label: intl.formatMessage({ id: 'accessManagement' }) } @@ -161,6 +165,17 @@ function User(props: singleUserProps) { const isMobile = window.innerWidth <= 1490; + // Mirror the backend delete guards: the main/root admin (the parentless top + // user — parentId 0; its id is NOT necessarily 0) and your own account can + // never be deleted, so disable the button and explain why. + const isMainAdmin = formValues.parentId <= 0 || formValues.id === 0; + const deleteDisabledReason = isMainAdmin + ? intl.formatMessage({ id: 'cannotDeleteMainAdmin' }) + : loggedInUser?.id === formValues.id + ? intl.formatMessage({ id: 'cannotDeleteSelf' }) + : ''; + const cannotDelete = deleteDisabledReason !== ''; + return ( <> {openModalDeleteUser && ( @@ -350,18 +365,20 @@ function User(props: singleUserProps) { defaultMessage="Apply Changes" /> - + + + + + {loading && (