use consistent terminology: username and account, no "user"
Massimo Melina committed
Jan 9, 2022 at 16:53 UTC
58f40e5751da04bd42a42584452b5fb979f428e1
5 files changed
+31
-31
frontend/src/login.ts
+8
-8
@@ -6,20 +6,20 @@ import { working } from './misc'
6
7
let refresher: NodeJS.Timeout
8
9
-export async function login(user:string, password:string) {
9
+export async function login(username:string, password:string) {
10
if (refresher)
11
clearInterval(refresher)
12
const stopWorking = working()
13
try {
14
/* simple login without encryption. Here commented just for example. Please use SRP version.
15
- const res = await apiCall('login', { user, password })
15
+ const res = await apiCall('login', { username, password })
16
*/
17
- const { pubKey, salt } = await apiCall('loginSrp1', { user })
17
+ const { pubKey, salt } = await apiCall('loginSrp1', { username })
18
if (!salt) return
19
20
const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
21
const srp = new SRPClientSession(srp6aNimbusRoutines);
22
- const resStep1 = await srp.step1(user, password)
22
+ const resStep1 = await srp.step1(username, password)
23
const resStep2 = await resStep1.step2(BigInt(salt), BigInt(pubKey))
24
const res = await apiCall('loginSrp2', { pubKey: String(resStep2.A), proof: String(resStep2.M1) }) // bigint-s must be cast to string to be json-ed
25
try {
@@ -32,8 +32,8 @@ export async function login(user:string, password:string) {
32
}
33
34
// login was successful, update state
35
- sessionRefresher({ user, exp:res.exp })
36
- state.username = user
35
+ sessionRefresher({ username, exp:res.exp })
36
+ state.username = username
37
}
38
catch(err) {
39
if (err instanceof ApiError)
@@ -45,8 +45,8 @@ export async function login(user:string, password:string) {
45
}
46
apiCall('refresh_session').then(sessionRefresher, ()=>{})
47
48
-function sessionRefresher({ exp, user }:{ exp:string, user:string }) {
49
- state.username = user
48
+function sessionRefresher({ exp, username }:{ exp:string, username:string }) {
49
+ state.username = username
50
if (!exp) return
51
const delta = new Date(exp).getTime() - Date.now()
52
const every = delta - 30_000
src/api.auth.ts
+12
-12
@@ -13,12 +13,12 @@ function makeExp() {
13
return { exp: new Date(Date.now() + SESSION_DURATION) }
14
}
15
16
-export const login: ApiHandler = async ({ user, password }, ctx) => {
17
- if (!user)
16
+export const login: ApiHandler = async ({ username, password }, ctx) => {
17
+ if (!username)
18
return ctx.status = 400
19
if (!password)
20
return ctx.status = 400
21
- const acc = getAccount(user)
21
+ const acc = getAccount(username)
22
if (!acc)
23
return ctx.status = 401
24
if (!acc.hashedPassword)
@@ -26,12 +26,12 @@ export const login: ApiHandler = async ({ user, password }, ctx) => {
26
if (!await verifyPassword(acc.hashedPassword, password))
27
return ctx.status = 401
28
if (ctx.session)
29
- ctx.session.user = user
29
+ ctx.session.username = username
30
return makeExp()
31
}
32
33
-export const loginSrp1: ApiHandler = async ({ user }, ctx) => {
34
- const account = getAccount(user)
33
+export const loginSrp1: ApiHandler = async ({ username }, ctx) => {
34
+ const account = getAccount(username)
35
if (!ctx.session)
36
return ctx.throw(500)
37
if (!account) // TODO simulate fake account to prevent knowing valid usernames
@@ -40,23 +40,23 @@ export const loginSrp1: ApiHandler = async ({ user }, ctx) => {
40
return ctx.status = 406 // unacceptable
41
42
const [salt, verifier] = account.srp.split('|')
43
- const step1 = await srpSession.step1(account.user, BigInt(salt), BigInt(verifier))
43
+ const step1 = await srpSession.step1(account.username, BigInt(salt), BigInt(verifier))
44
const sid = Math.random()
45
ongoingLogins[sid] = step1
46
setTimeout(()=> delete ongoingLogins[sid], 60_000)
47
48
- ctx.session.login = { user, sid }
48
+ ctx.session.login = { username, sid }
49
return { salt, pubKey: String(step1.B) } // cast to string cause bigint can't be jsonized
50
}
51
52
export const loginSrp2: ApiHandler = async ({ pubKey, proof }, ctx) => {
53
if (!ctx.session)
54
return ctx.throw(500)
55
- const { user, sid } = ctx.session.login
55
+ const { username, sid } = ctx.session.login
56
const step1 = ongoingLogins[sid]
57
try {
58
const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
59
- ctx.session.user = user
59
+ ctx.session.username = username
60
return { proof: String(M2), ...makeExp() }
61
}
62
catch(e) {
@@ -67,13 +67,13 @@ export const loginSrp2: ApiHandler = async ({ pubKey, proof }, ctx) => {
67
68
export const logout: ApiHandler = async ({}, ctx) => {
69
if (ctx.session)
70
- ctx.session.user = undefined
70
+ ctx.session.username = undefined
71
ctx.status = 200
72
return true
73
}
74
75
export const refresh_session: ApiHandler = async ({}, ctx) => {
76
- return { user: ctx.session?.user, ...makeExp() }
76
+ return { username: ctx.session?.username, ...makeExp() }
77
}
78
79
export const change_password: ApiHandler = async ({ newPassword }, ctx) => {
src/perm.ts
+6
-6
@@ -11,7 +11,7 @@ import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
11
let path = ''
12
13
interface Account {
14
- user: string, // we'll have user in it, so we don't need to pass it separately
14
+ username: string, // we'll have username in it, so we don't need to pass it separately
15
password?: string
16
hashedPassword?: string
17
srp?: string
@@ -22,7 +22,7 @@ interface Accounts { [username:string]: Account }
22
let accounts: Accounts = {}
23
24
export async function getCurrentUsername(ctx: Koa.Context) {
25
- return ctx.session?.user || ''
25
+ return ctx.session?.username || ''
26
}
27
28
// provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
@@ -68,7 +68,7 @@ export async function updateAccount(username: string, changer?:Changer) {
68
}
69
account.belongs = wantArray(account.belongs).filter(b =>
70
b in accounts // at this stage the group record may still be null if specified later in the file
71
- || console.error(`user ${username} belongs to non-existing ${b}`) )
71
+ || console.error(`account ${username} belongs to non-existing ${b}`) )
72
if (was !== JSON.stringify(account))
73
saveAccountsAsap()
74
}
@@ -100,10 +100,10 @@ subscribeConfig({ k:'accounts', defaultValue:'accounts.yaml' }, v => {
100
async function applyAccounts(newAccounts:Accounts) {
101
// we should validate content here
102
accounts = newAccounts
103
- await Promise.all(_.map(newAccounts, async (rec,k) => {
103
+ await Promise.all(_.map(accounts, async (rec,k) => {
104
if (!rec) // an empty object in yaml is stored as null
105
- rec = accounts[k] = { user: k, srp:'' }
106
- setHidden(rec, { user: k })
105
+ rec = accounts[k] = { username: k, srp:'' }
106
+ setHidden(rec, { username: k })
107
await updateAccount(k)
108
}))
109
}
tests/test.ts
+3
-3
@@ -8,7 +8,7 @@ const appStarted = new Promise(resolve =>
8
srv.on( 'app_started', resolve) )
9
*/
10
11
-const user = 'rejetto'
11
+const username = 'rejetto'
12
const password = 'password'
13
14
describe('basics', () => {
@@ -28,14 +28,14 @@ describe('basics', () => {
28
it('missing perm', req('/for-admins/', 404))
29
it('proxy', req('/proxy', s => s.includes('github')))
30
it('login', req('/~/api/login', 200, {
31
- data: { user, password }
31
+ data: { username, password }
32
}))
33
})
34
35
let cookie:any
36
describe('after-login', () => {
37
before(req('/~/api/login', (data, res) => Boolean(cookie = res.headers['set-cookie']), {
38
- data: { user, password }
38
+ data: { username, password }
39
}))
40
it('list protected', done => // defer execution of req() to have cookie set
41
req('/~/api/file_list', data => inList(data, 'alfa.txt'), {
todo.md
+2
-2
@@ -7,8 +7,8 @@
7
- search and login dialogs should push to history so that mobile can use back button to close them
8
- node.comment
9
- config: max connections (total/per-ip)
10
-- user.ignoreLimits
11
-- user.redirect
10
+- account.ignoreLimits
11
+- account.redirect
12
- config: bans
13
- config: min disk space
14
- link to parent folder in the list (as an option of the frontend? plugin?)