--create-admin <password>

Massimo Melina committed Jun 8, 2022 at 22:31 UTC 0e836a05d0e1afb98d135a4af4aa0b8c327ab4cb
5 files changed +30 -21
README.md
+6 -12
@@ -39,21 +39,15 @@ You won't find all previous features here (yet), but still we got:
39 4. launch `hfs` file
40 5. the browser should automatically open on `localhost` address, so you can configure the rest
41
42 +If you access HFS via localhost, by default it won't require your to login.
43 +
44 ### Cloud server?
45
46 If you are installing HFS on "another" machine, then step 5 may not be possible.
45 -In this case you should create a `config.yaml` file with this content:
46 -```
47 -accounts:
48 - YOUR-USERNAME:
49 - admin: true
50 - password: YOUR-PASSWORD
51 -```
52 -
53 -This way you'll have your admin account.
54 -Be sure to respect spaces/indentation.
55 -
56 -Don't worry about the password, it will be encrypted at first run.
47 +In this case you should run hfs with `--create-admin <PASSWORD>`.
48 +This will both
49 +- create an account with username `admin` with the provided password and Admin privilege (granting access to Admin panel).
50 +- disable the unprotected access (no login) to Admin panel
51
52 ### Other systems
53
server/src/config.ts
+2
@@ -9,6 +9,7 @@ import { debounceAsync, same, objSameKeys, onOff, wait, with_ } from './misc'
9 import { exists, statSync } from 'fs'
10 import { promisify } from 'util'
11 import { join } from 'path'
12 +import events from './events'
13
14 const PATH = 'config.yaml'
15
@@ -115,6 +116,7 @@ export function setConfig(newCfg: Record<string,any>, save?: boolean) {
116 if (!newCfg.hasOwnProperty(k))
117 apply(k, newCfg[k])
118 started = true
119 + events.emit('config ready')
120
121 function apply(k: string, newV: any) {
122 return setConfig1(k, newV, save === undefined)
server/src/misc.ts
+2 -2
@@ -37,12 +37,12 @@ export function prefix(pre:string, v:string|number, post:string='') {
37 return v ? pre+v+post : ''
38 }
39
40 -export function setHidden(dest: object, src:object) {
40 +export function setHidden<T, ADD>(dest: T, src: ADD) {
41 return Object.defineProperties(dest, objSameKeys(src as any, value => ({
42 enumerable: false,
43 writable: true,
44 value,
45 - })))
45 + }))) as T & ADD
46 }
47
48 export function objSameKeys<S extends object,VR=any>(src: S, newValue:(value:Truthy<S[keyof S]>, key:keyof S)=>any) {
server/src/perm.ts
+20 -6
@@ -7,6 +7,8 @@ import Koa from 'koa'
7 import { defineConfig, saveConfigAsap } from './config'
8 import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
9 import events from './events'
10 +import { argv } from './const'
11 +import { localhostAdmin } from './adminApis'
12
13 export interface Account {
14 username: string, // we'll have username in it, so we don't need to pass it separately
@@ -98,6 +100,15 @@ accountsConfig.sub(async v => {
100 }))
101 })
102
103 +events.once('config ready', async () => {
104 + const pwd = argv['create-admin']
105 + if (!pwd) return
106 + const acc = getAccount('admin') || addAccount('admin', { admin: true })
107 + await updateAccount(acc!, acc => acc.password = pwd)
108 + localhostAdmin.set(false)
109 + console.log("account 'admin' created while unprotected admin access on localhost is now disabled")
110 +})
111 +
112 function normalizeUsername(username: string) {
113 return username.toLocaleLowerCase()
114 }
@@ -131,23 +142,26 @@ const assignableProps: (keyof Account)[] = ['redirect','ignore_limits','belongs'
142 export function addAccount(username: string, props: Partial<Account>) {
143 if (!username || accounts[username])
144 return
134 - const copy = _.pickBy(_.pick(props, assignableProps), Boolean)
135 - setHidden(copy, { username })
145 + const copy: Account = setHidden(_.pickBy(_.pick(props, assignableProps), Boolean),
146 + { username }) // have the field in the object but hidden so that stringification won't include it
147 accountsConfig.set(accounts =>
148 Object.assign(accounts, { [username]: copy }))
138 - saveAccountsAsap()
149 + saveAccountsAsap().then()
150 return copy
151 }
152
153 export function setAccount(username: string, changes: Partial<Account>) {
154 + const acc = getAccount(username)
155 + if (!acc)
156 + return false
157 const rest = _.pick(changes, assignableProps)
158 for (const [k,v] of Object.entries(rest))
159 if (!v)
160 rest[k as keyof Account] = undefined
147 - Object.assign(getAccount(username), rest)
161 + Object.assign(acc, rest)
162 if (changes.username)
163 renameAccount(username, changes.username)
150 - saveAccountsAsap()
164 + saveAccountsAsap().then()
165 return true
166 }
167
@@ -156,7 +170,7 @@ export function delAccount(username: string) {
170 return false
171 accountsConfig.set(accounts =>
172 Object.assign(accounts, { [username]: undefined }))
159 - saveAccountsAsap()
173 + saveAccountsAsap().then()
174 return true
175 }
176
todo.md
-1
@@ -1,7 +1,6 @@
1 # To do
2 - fix: root without can_download breaks
3 - use dialogs instead of side-forms on mobile (admin/fs+accounts)
4 -- easier deploy on cloud server: --create-admin <password>
4 - admin/fs: sort items
5 - admin/fs: render virtual folders differently
6 - admin/config: hide advanced settings