admin/accounts: show when an account is disabled because of its groups
Massimo Melina committed
Dec 14, 2024 at 11:40 UTC
c6f7786c1a9c8e38a457005c15c99d6cff0580a0
3 files changed
+22
-8
admin/src/AccountForm.ts
+3
-2
@@ -2,7 +2,7 @@
2
3
import { createElement as h, ReactNode, useEffect, useRef, useState } from 'react'
4
import { BoolField, Form, MultiSelectField, NumberField } from '@hfs/mui-grid-form'
5
-import { Alert } from '@mui/material'
5
+import { Alert, Box } from '@mui/material'
6
import { apiCall } from './api'
7
import { alertDialog, useDialogBarColors } from './dialog'
8
import { formatTimestamp, isEqualLax, prefix, useIsMobile, wantArray } from './misc'
@@ -65,7 +65,8 @@ export default function AccountForm({ account, done, groups, addToBar, reload }:
65
!group && { k: 'password2', md: 6, lg: 4, type: 'password', autoComplete: 'new-password', label: 'Repeat password',
66
getError: (x, { values }) => (x||'') !== (values.password||'') && "Enter same password" },
67
{ k: 'disabled', comp: BoolField, fromField: x=>!x, toField: x=>!x, label: "Enabled", xs: 12, sm: 6, lg: 8,
68
- helperText: "Login is prevented if account is disabled, or if all its groups are disabled"},
68
+ helperText: !values.disabled && values.canLogin === false ? h(Box, { color: 'warning.main', component: 'span' }, "Login is prevented because all of its groups are disabled")
69
+ : "Login is prevented if account is disabled, or all its groups are disabled" },
70
{ k: 'ignore_limits', comp: BoolField, xs: true,
71
helperText: values.ignore_limits ? "Speed limits don't apply to this account" : "Speed limits apply to this account" },
72
{ k: 'admin', comp: BoolField, fromField: (v:boolean) => v||null, label: "Admin-panel access", xs: 12, sm: 6, lg: 8,
admin/src/AccountsPage.ts
+14
-3
@@ -42,7 +42,7 @@ export default function AccountsPage() {
42
h(ListItem, { key: username },
43
h(ListItemText, {}, username))))
44
)
45
- : with_(selectedAccount || { username: '', hasPassword: sel === 'new-user', adminActualAccess: false, invalidated: undefined }, a =>
45
+ : with_(selectedAccount || newAccount(), a =>
46
h(AccountForm, {
47
account: a,
48
groups: list.filter(x => !x.hasPassword).map( x => x.username ),
@@ -74,7 +74,7 @@ export default function AccountsPage() {
74
75
const scrollProps = { height: '100%', display: 'flex', flexDirection: 'column', overflow: 'auto' } as const
76
return element || h(Grid, { container: true, rowSpacing: 1, columnSpacing: 2, top: 0, flex: '1 1 auto', height: 0 },
77
- h(Grid, { item: true, xs: 12, [sideBreakpoint]: 5, lg: 4, xl: 5, ...scrollProps },
77
+ h(Grid, { item: true, xs: 12, [sideBreakpoint]: 5, lg: 4, xl: 5, ...scrollProps },
78
h(Box, {
79
display: 'flex',
80
flexWrap: 'wrap',
@@ -124,7 +124,8 @@ export default function AccountsPage() {
124
}
125
},
126
account2icon(ac),
127
- ac.disabled && h(DoNotDisturb),
127
+ (ac.disabled || ac.canLogin === false)
128
+ && iconTooltip(DoNotDisturb, ac.disabled ? "Disabled" : "Disabled by its groups", ac.disabled ? undefined : { color: 'text.secondary' }),
129
(ac.expire || ac.days_to_live) && h(Schedule),
130
ac.adminActualAccess && iconTooltip(MilitaryTech, "Can login into Admin"),
131
ac.username,
@@ -140,6 +141,16 @@ export default function AccountsPage() {
141
h(CardContent, {}, sideContent)) )
142
)
143
144
+ function newAccount() {
145
+ return {
146
+ username: '',
147
+ hasPassword: sel === 'new-user',
148
+ adminActualAccess: false,
149
+ invalidated: undefined,
150
+ canLogin: true
151
+ } satisfies Account
152
+ }
153
+
154
function selectNone() {
155
setSel([])
156
}
src/api.accounts.ts
+5
-3
@@ -1,20 +1,22 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import { ApiError, ApiHandlers } from './apiMiddleware'
4
-import { Account, accountCanLoginAdmin, accountHasPassword, accountsConfig, addAccount, delAccount, getAccount,
5
- changeSrpHelper, updateAccount } from './perm'
4
+import {
5
+ Account, accountCanLoginAdmin, accountHasPassword, accountsConfig, addAccount, delAccount, getAccount,
6
+ changeSrpHelper, updateAccount, accountCanLogin
7
+} from './perm'
8
import _ from 'lodash'
9
import { HTTP_BAD_REQUEST, HTTP_CONFLICT, HTTP_NOT_FOUND } from './const'
10
import { getCurrentUsername, invalidateSessionBefore } from './auth'
11
import { apiAssertTypes, onlyTruthy } from './misc'
12
11
-export type AccountAdminSend = NonNullable<ReturnType<typeof prepareAccount>>
13
function prepareAccount(ac: Account | undefined) {
14
return ac && {
15
..._.omit(ac, ['password','hashed_password','srp']),
16
username: ac.username, // omit won't copy it because it's a hidden prop
17
hasPassword: accountHasPassword(ac),
18
adminActualAccess: accountCanLoginAdmin(ac),
19
+ canLogin: accountHasPassword(ac) ? accountCanLogin(ac) : undefined,
20
invalidated: invalidateSessionBefore.get(ac.username),
21
}
22
}