admin/accounts: expiration
Massimo Melina committed
Jan 26, 2024 at 01:44 UTC
fc6f4e37a7b1ad4f04d6a44cfcef25b91a70d9c3
3 files changed
+13
-3
admin/src/AccountForm.ts
+3
-1
@@ -5,7 +5,7 @@ import { BoolField, Form, MultiSelectField } from '@hfs/mui-grid-form'
5
import { Alert } from '@mui/material'
6
import { apiCall } from './api'
7
import { alertDialog, toast, useDialogBarColors } from './dialog'
8
-import { HTTP_NOT_ACCEPTABLE, isEqualLax, wantArray } from './misc'
8
+import { isEqualLax, wantArray } from './misc'
9
import { IconBtn, modifiedSx } from './mui'
10
import { Account } from './AccountsPage'
11
import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
@@ -13,6 +13,7 @@ import { AutoDelete, Delete } from '@mui/icons-material'
13
import { isMobile } from './misc'
14
import { state, useSnapState } from './state'
15
import VfsPathField from './VfsPathField'
16
+import { DateTimeField } from './DateTimeField'
17
18
interface FormProps { account: Account, groups: string[], done: (username: string)=>void, reload: ()=>void, addToBar: ReactNode }
19
export default function AccountForm({ account, done, groups, addToBar, reload }: FormProps) {
@@ -78,6 +79,7 @@ export default function AccountForm({ account, done, groups, addToBar, reload }:
79
+ (!group ? '' : ". A group can inherit from another group")
80
+ (belongsOptions.length ? '' : ". Now disabled because there are no groups to select, create one first.")
81
},
82
+ { k: 'expire', label: "Expiration", comp: DateTimeField, toField: x => x && new Date(x) },
83
{ k: 'redirect', comp: VfsPathField,
84
helperText: "If you want this account to be redirected to a specific folder/address at login time" },
85
],
admin/src/DateTimeField.ts
+3
@@ -12,6 +12,9 @@ export function DateTimeField({ onChange, error, helperText, ...rest }: FieldPro
12
sx: { width: '100%', color: 'error.main', ...rest.sx },
13
onChange(v: any) {
14
onChange(v && new Date(v), { was: rest.value, event: undefined })
15
+ },
16
+ slotProps: { // under 400, not all buttons fit, so we sacrifice 'cancel' as you can still tap outside the dialog
17
+ actionBar: { actions: ['clear', ...window.innerWidth < 400 ? [] : ['cancel'] as const, 'today', 'accept'] }
18
}
19
}),
20
helperText && h(FormHelperText, { error }, helperText),
src/perm.ts
+7
-2
@@ -17,6 +17,7 @@ export interface Account {
17
admin?: boolean
18
redirect?: string
19
disabled?: boolean
20
+ expire?: Date
21
}
22
interface Accounts { [username:string]: Account }
23
@@ -100,6 +101,7 @@ accountsConfig.sub(obj => {
101
setHidden(rec, { username: norm })
102
}
103
updateAccount(rec).then() // work password fields
104
+ rec.expire &&= new Date(rec.expire) // parse
105
})
106
})
107
@@ -131,7 +133,7 @@ export function renameAccount(from: string, to: string) {
133
}
134
135
// we consider all the following fields, when falsy, as equivalent to be missing. If this changes in the future, please adjust addAccount and setAccount
134
-const assignableProps: (keyof Account)[] = ['redirect','ignore_limits','belongs','admin','disabled','disable_password_change']
136
+const assignableProps: (keyof Account)[] = ['redirect','ignore_limits','belongs','admin','disabled','disable_password_change','expire']
137
138
export function addAccount(username: string, props: Partial<Account>) {
139
username = normalizeUsername(username)
@@ -150,6 +152,7 @@ export function setAccount(acc: Account, changes: Partial<Account>) {
152
if (!v)
153
rest[k as keyof Account] = undefined
154
Object.assign(acc, rest)
155
+ acc.expire &&= new Date(acc.expire)
156
if (changes.username)
157
renameAccount(acc.username, changes.username)
158
saveAccountsAsap()
@@ -188,7 +191,9 @@ export function accountCanLogin(account: Account) {
191
}
192
193
function allDisabled(account: Account): boolean {
191
- return Boolean(account.disabled || account.belongs?.map(u => getAccount(u, false)).every(a => a && allDisabled(a)))
194
+ return Boolean(account.disabled
195
+ || account.expire as any < Date.now()
196
+ || account.belongs?.map(u => getAccount(u, false)).every(a => a && allDisabled(a)))
197
}
198
199
export function accountCanLoginAdmin(account: Account) {