@samitouri / QOSami-HFS / commits / fc6f4e37

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) {