feat(ui): prevent self-modification in People management and highlight current user (#5441)

# Description of Changes

This PR improves the People management UI by preventing users from
modifying or deleting their own account and by visually highlighting the
currently logged-in user.

<img width="675" height="196" alt="image"
src="https://github.com/user-attachments/assets/c45fb0b6-c766-412c-a53b-b72aed2925d2"
/>


### What was changed
- Integrated session-based authentication context to identify the
currently logged-in user.
- Added a helper to detect the current user in the user list.
- Highlighted the current user's row with a subtle background color.
- Disabled self-actions:
  - Editing own role
  - Enabling/disabling own account
  - Deleting own account
- Kept password change available for the current user.

### Why the change was made
- Prevents accidental self-lockout or privilege removal.
- Aligns UI behavior with common security best practices.
- Improves clarity by visually distinguishing the active user account.

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
Ludy
2026-01-13 16:13:28 +00:00
committed by GitHub
parent 8632ed9875
commit 65a5d05713
@@ -31,11 +31,13 @@ import { useNavigate } from 'react-router-dom';
import UpdateSeatsButton from '@app/components/shared/UpdateSeatsButton'; import UpdateSeatsButton from '@app/components/shared/UpdateSeatsButton';
import { useLicense } from '@app/contexts/LicenseContext'; import { useLicense } from '@app/contexts/LicenseContext';
import ChangeUserPasswordModal from '@app/components/shared/ChangeUserPasswordModal'; import ChangeUserPasswordModal from '@app/components/shared/ChangeUserPasswordModal';
import { useAuth } from '@app/auth/UseSession';
export default function PeopleSection() { export default function PeopleSection() {
const { t } = useTranslation(); const { t } = useTranslation();
const { config } = useAppConfig(); const { config } = useAppConfig();
const { loginEnabled } = useLoginRequired(); const { loginEnabled } = useLoginRequired();
const { user: currentUser } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const { licenseInfo: globalLicenseInfo } = useLicense(); const { licenseInfo: globalLicenseInfo } = useLicense();
const [users, setUsers] = useState<User[]>([]); const [users, setUsers] = useState<User[]>([]);
@@ -77,6 +79,7 @@ export default function PeopleSection() {
? t('workspace.people.license.noSlotsAvailable', 'No user slots available') ? t('workspace.people.license.noSlotsAvailable', 'No user slots available')
: null; : null;
const isCurrentUser = (user: User) => currentUser?.username === user.username;
// Form state for edit user modal // Form state for edit user modal
const [editForm, setEditForm] = useState({ const [editForm, setEditForm] = useState({
@@ -463,7 +466,10 @@ export default function PeopleSection() {
</Table.Tr> </Table.Tr>
) : ( ) : (
filteredUsers.map((user) => ( filteredUsers.map((user) => (
<Table.Tr key={user.id}> <Table.Tr
key={user.id}
style={isCurrentUser(user) ? { backgroundColor: 'rgba(34, 139, 230, 0.08)' } : undefined}
>
<Table.Td> <Table.Td>
<Group gap="xs" wrap="nowrap"> <Group gap="xs" wrap="nowrap">
<Tooltip <Tooltip
@@ -577,6 +583,7 @@ export default function PeopleSection() {
</ActionIcon> </ActionIcon>
</Menu.Target> </Menu.Target>
<Menu.Dropdown style={{ zIndex: Z_INDEX_OVER_CONFIG_MODAL }}> <Menu.Dropdown style={{ zIndex: Z_INDEX_OVER_CONFIG_MODAL }}>
{!isCurrentUser(user) && (
<Menu.Item <Menu.Item
leftSection={<LocalIcon icon="edit" width="1rem" height="1rem" />} leftSection={<LocalIcon icon="edit" width="1rem" height="1rem" />}
onClick={() => openEditModal(user)} onClick={() => openEditModal(user)}
@@ -584,6 +591,7 @@ export default function PeopleSection() {
> >
{t('workspace.people.editRole')} {t('workspace.people.editRole')}
</Menu.Item> </Menu.Item>
)}
<Menu.Item <Menu.Item
leftSection={<LocalIcon icon="lock" width="1rem" height="1rem" />} leftSection={<LocalIcon icon="lock" width="1rem" height="1rem" />}
onClick={() => openChangePasswordModal(user)} onClick={() => openChangePasswordModal(user)}
@@ -591,6 +599,7 @@ export default function PeopleSection() {
> >
{t('workspace.people.changePassword.action', 'Change password')} {t('workspace.people.changePassword.action', 'Change password')}
</Menu.Item> </Menu.Item>
{!isCurrentUser(user) && (
<Menu.Item <Menu.Item
leftSection={user.enabled ? <LocalIcon icon="person-off" width="1rem" height="1rem" /> : <LocalIcon icon="person-check" width="1rem" height="1rem" />} leftSection={user.enabled ? <LocalIcon icon="person-off" width="1rem" height="1rem" /> : <LocalIcon icon="person-check" width="1rem" height="1rem" />}
onClick={() => handleToggleEnabled(user)} onClick={() => handleToggleEnabled(user)}
@@ -598,10 +607,15 @@ export default function PeopleSection() {
> >
{user.enabled ? t('workspace.people.disable') : t('workspace.people.enable')} {user.enabled ? t('workspace.people.disable') : t('workspace.people.enable')}
</Menu.Item> </Menu.Item>
)}
{!isCurrentUser(user) && (
<>
<Menu.Divider /> <Menu.Divider />
<Menu.Item color="red" leftSection={<LocalIcon icon="delete" width="1rem" height="1rem" />} onClick={() => handleDeleteUser(user)} disabled={!loginEnabled}> <Menu.Item color="red" leftSection={<LocalIcon icon="delete" width="1rem" height="1rem" />} onClick={() => handleDeleteUser(user)} disabled={!loginEnabled}>
{t('workspace.people.deleteUser')} {t('workspace.people.deleteUser')}
</Menu.Item> </Menu.Item>
</>
)}
</Menu.Dropdown> </Menu.Dropdown>
</Menu> </Menu>
</Group> </Group>