main
ts 79 lines 3.24 KB
Raw
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 { state, useSnapState } from './state'
4 import { createElement as h, Fragment, useEffect, useRef, useState } from 'react'
5 import { ALLOW_SESSION_IP_CHANGE, HTTP_FORBIDDEN, HTTP_UNAUTHORIZED, makeSessionRefresher, withSrpLib } from './misc'
6 import { BoolField, Form } from '@hfs/mui-grid-form'
7 import { apiCall } from './api'
8 import { srpClientSequence } from '../../src/srp'
9 import { Alert, Box } from '@mui/material'
10 import { Center } from './mui'
11
12 export function LoginRequired({ children }: any) {
13 const { loginRequired } = useSnapState()
14 if (loginRequired === HTTP_FORBIDDEN)
15 return h(Center, {},
16 h(Alert, { severity: 'error' }, "Admin-panel only for localhost"),
17 h(Box, { sx: { mt: 2, fontSize: 'small' } }, "because no admin account was configured")
18 )
19 if (loginRequired)
20 return h(LoginForm)
21 return h(Fragment, {}, children)
22 }
23
24 function LoginForm() {
25 const [values, setValues] = useState({ username: '', password: '', [ALLOW_SESSION_IP_CHANGE]: false })
26 const [error, setError] = useState('')
27 const formRef = useRef<HTMLFormElement>()
28 const empty = formRef.current?.querySelector('input[value=""]')
29 useEffect(() => (empty as any)?.focus?.(), [empty])
30 return h(Center, {},
31 h(Form, {
32 formRef,
33 values,
34 sx: { m: 2, maxWidth: '25em' },
35 set(v, k) {
36 setValues(values => ({ ...values, [k]: v }))
37 },
38 fields: [
39 { k: 'username', autoComplete: 'username', autoFocus: true, required: true },
40 { k: 'password', type: 'password', autoComplete: 'current-password', required: true },
41 { k: ALLOW_SESSION_IP_CHANGE, comp: BoolField, label: "Allow IP change during this session" },
42 ],
43 addToBar: [ error && h(Alert, { severity: 'error', sx: { flex: 1 } }, error) ],
44 saveOnEnter: true,
45 save: {
46 children: "Enter",
47 startIcon: null,
48 async onClick() {
49 try {
50 setError('')
51 await login(values.username, values.password, {
52 [ALLOW_SESSION_IP_CHANGE]: values[ALLOW_SESSION_IP_CHANGE]
53 })
54 }
55 catch(e) {
56 setError(String(e))
57 }
58 }
59 }
60 })
61 )
62 }
63
64 async function login(username: string, password: string, extra?: object) {
65 const res = await withSrpLib(srpClientSequence)(username, password, apiCall, extra).catch(err => {
66 throw err?.code === HTTP_UNAUTHORIZED ? err.message || "Wrong username or password"
67 : err === 'trust' ? "Login aborted: server identity cannot be trusted"
68 : err?.name === 'AbortError' ? "Server didn't respond"
69 : (err?.message || "Unknown error")
70 })
71 if (!res.isAdmin)
72 throw "This account has no Admin access"
73
74 // login was successful, update state
75 state.loginRequired = false
76 refreshSession(res)
77 }
78
79 const refreshSession = makeSessionRefresher(state)