better code: logic separated from UI

Massimo Melina committed May 23, 2022 at 19:59 UTC b1e3f2259602575a2e2c8abcc059e3e5fc99452b
4 files changed +33 -48
admin/src/LoginRequired.ts
+5 -15
@@ -3,7 +3,7 @@ import { createElement as h, Fragment, useState } from 'react'
3 import { Center } from './misc'
4 import { Form } from './Form'
5 import { apiCall } from './api'
6 -import { SRPClientSession, SRPParameters, SRPRoutines } from 'tssrp6a'
6 +import { srpSequence } from '@hfs/shared'
7 import { Alert } from '@mui/material'
8
9 export function LoginRequired({ children }: any) {
@@ -47,20 +47,10 @@ function LoginForm() {
47 }
48
49 async function login(username: string, password: string) {
50 - const WRONG = "Wrong username or password"
51 - const { pubKey, salt } = await apiCall('loginSrp1', { username })
52 - .catch(() => { throw WRONG })
53 - if (!salt)
54 - throw "Bad response from server"
55 -
56 - const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
57 - const srp = new SRPClientSession(srp6aNimbusRoutines);
58 - const resStep1 = await srp.step1(username, password)
59 - const resStep2 = await resStep1.step2(BigInt(salt), BigInt(pubKey))
60 - const res = await apiCall('loginSrp2', { pubKey: String(resStep2.A), proof: String(resStep2.M1) }) // bigint-s must be cast to string to be json-ed
61 - .catch(() => { throw WRONG })
62 - await resStep2.step3(BigInt(res.proof))
63 - .catch(() => { throw "Login aborted: server identity cannot be trusted" })
50 + const res = await srpSequence(username, password, apiCall).catch(err => {
51 + throw err === 'trust' ? "Login aborted: server identity cannot be trusted"
52 + : "Wrong username or password"
53 + })
54 if (!res.adminUrl)
55 throw "This account has no Admin access"
56
frontend/src/login.ts
+11 -33
@@ -3,47 +3,25 @@
3 import { apiCall, ApiError } from './api'
4 import { state } from './state'
5 import { alertDialog } from './dialog'
6 -import { SRPClientSession, SRPParameters, SRPRoutines } from 'tssrp6a'
7 -import { working } from './misc'
6 +import { srpSequence, working } from './misc'
7
8 export async function login(username:string, password:string) {
9 const stopWorking = working()
11 - try {
12 -/* simple login without encryption. Here commented just for example. Please use SRP version.
13 - const res = await apiCall('login', { username, password })
14 -*/
15 - const { pubKey, salt } = await apiCall('loginSrp1', { username })
16 - if (!salt) return
17 -
18 - const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
19 - const srp = new SRPClientSession(srp6aNimbusRoutines);
20 - const resStep1 = await srp.step1(username, password)
21 - const resStep2 = await resStep1.step2(BigInt(salt), BigInt(pubKey))
22 - const res = await apiCall('loginSrp2', { pubKey: String(resStep2.A), proof: String(resStep2.M1) }) // bigint-s must be cast to string to be json-ed
23 - try {
24 - await resStep2.step3(BigInt(res.proof))
25 - }
26 - catch(e){
27 - console.debug(String(e))
28 - stopWorking()
29 - await alertDialog("Login aborted: server identity cannot be trusted", 'error')
30 - return
31 - }
32 -
33 - // login was successful, update state
10 + return srpSequence(username, password, apiCall).then(res => {
11 + stopWorking()
12 sessionRefresher(res)
13 return res
36 - }
37 - catch(err) {
14 + }, (err: Error) => {
15 stopWorking()
39 - if (err instanceof ApiError)
16 + if (err.message === 'trust')
17 + err = Error("Login aborted: server identity cannot be trusted")
18 + else if (err instanceof ApiError)
19 if (err.code === 401)
41 - err = 'Invalid credentials'
20 + err = Error("Invalid credentials")
21 else if (err.code === 409)
43 - err = 'Cookies not working - login failed'
44 - await alertDialog(err as Error, 'error')
45 - }
46 - finally { stopWorking() }
22 + err = Error("Cookies not working - login failed")
23 + return alertDialog(err)
24 + })
25 }
26
27 // @ts-ignore
shared/package.json
+3
@@ -6,6 +6,9 @@
6 "test": "test",
7 "build": "tsc && yarn sass src/:lib/"
8 },
9 + "dependencies": {
10 + "tssrp6a": "^3.0.0"
11 + },
12 "devDependencies": {
13 "react": "*",
14 "react-dom": "*",
shared/src/index.ts
+14
@@ -1,6 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import _ from 'lodash'
4 +import { SRPClientSession, SRPParameters, SRPRoutines } from 'tssrp6a'
5 export * from './react'
6
7 export type Dict<T=any> = Record<string, T>
@@ -66,3 +67,16 @@ export function setHidden(dest: object, src:object) {
67 value,
68 })))
69 }
70 +
71 +export async function srpSequence(username:string, password:string, apiCall: (cmd:string, params:any) => any) {
72 + const { pubKey, salt } = await apiCall('loginSrp1', { username })
73 + if (!salt) throw Error('salt')
74 + const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
75 + const srp = new SRPClientSession(srp6aNimbusRoutines);
76 + const resStep1 = await srp.step1(username, password)
77 + const resStep2 = await resStep1.step2(BigInt(salt), BigInt(pubKey))
78 + const res = await apiCall('loginSrp2', { pubKey: String(resStep2.A), proof: String(resStep2.M1) }) // bigint-s must be cast to string to be json-ed
79 + await resStep2.step3(BigInt(res.proof)).catch(() => Promise.reject('trust'))
80 + return res
81 +}
82 +