HFS console commands
Massimo Melina committed
Jun 23, 2022 at 00:01 UTC
e70cb9246de66c12000e86f7b777298d3272b9af
7 files changed
+80
-25
README.md
+5
-10
@@ -48,17 +48,12 @@ You won't find all previous features here (yet), but still we got:
48
2. click on `Assets`
49
3. **download** the right version for your computer
50
4. launch `hfs` file
51
-5. the browser should automatically open on `localhost` address, so you can configure the rest
51
+5. the browser should automatically open on `localhost` address, so you can configure the rest in the Admin panel.
52
+ - if a browser cannot be opened on the computer where you are installing HFS,
53
+ you should enter this command in HFS console: `create-admin <PASSWORD>`
54
53
-If you access HFS via localhost, by default it won't require your to login.
54
-
55
-### Cloud server?
56
-
57
-If you are installing HFS on "another" machine, then step 5 may not be possible.
58
-In this case you should run hfs with `--create-admin <PASSWORD>`.
59
-This will both
60
-- create an account with username `admin` with the provided password and Admin privilege (granting access to Admin panel).
61
-- disable the unprotected access (no login) to Admin panel
55
+If you access *Admin panel* via localhost, by default HFS **won't** require your to login.
56
+If you don't like this behavior, disable it in the Admin panel or enter this console command `config localhost_admin false`.
57
58
### Other systems
59
server/src/api.accounts.ts
+4
-4
@@ -4,13 +4,13 @@ import { changePasswordHelper, changeSrpHelper } from './api.helpers'
4
import { ApiError, ApiHandlers } from './apiMiddleware'
5
import {
6
Account,
7
- accountCanLogin,
7
+ accountCanLoginAdmin,
8
accountHasPassword,
9
addAccount,
10
delAccount,
11
getAccount,
12
- getAccounts, getCurrentUsername,
13
- getFromAccount,
12
+ getAccounts,
13
+ getCurrentUsername,
14
setAccount
15
} from './perm'
16
import _ from 'lodash'
@@ -21,7 +21,7 @@ function prepareAccount(ac: Account | undefined) {
21
..._.omit(ac, ['password','hashed_password','srp']),
22
username: ac.username, // omit won't copy it because it's a hidden prop
23
hasPassword: accountHasPassword(ac),
24
- adminActualAccess: accountCanLogin(ac) && getFromAccount(ac, a => a.admin),
24
+ adminActualAccess: accountCanLoginAdmin(ac),
25
}
26
}
27
server/src/commands.ts
new
+55
@@ -0,0 +1,55 @@
1
+import { addAccount, getAccount, updateAccount } from './perm'
2
+import { getConfigDefinition, setConfig } from './config'
3
+
4
+console.log(`HINT: type "help" for help`)
5
+require('readline').createInterface({ input: process.stdin }).on('line', (line: string) => {
6
+ const [command, ...params] = line.split(/ +/)
7
+ const fun = (commands as any)[command]
8
+ if (!fun)
9
+ return console.error("cannot understand entered command")
10
+ if (fun.length > params.length) {
11
+ const [args] = /\((.+)\)\s*\{/.exec(fun)!
12
+ return console.error("insufficient parameters, expected: " + args)
13
+ }
14
+ fun(...params).then(() =>console.log("command executed"),
15
+ (err: any) => {
16
+ if (typeof err === 'string')
17
+ console.error("command failed:", err)
18
+ else
19
+ throw err
20
+ })
21
+})
22
+
23
+const commands = {
24
+ async help() {
25
+ console.log("supported commands:", ...Object.keys(commands).map(x => '\n - ' + x))
26
+ },
27
+ async 'create-admin'(password: string, username='admin') {
28
+ if (getAccount(username))
29
+ throw `user ${username} already exists`
30
+ const acc = addAccount(username, { admin: true })
31
+ await updateAccount(acc!, acc => {
32
+ acc.password = password
33
+ })
34
+ },
35
+ async 'change-password'(user: string, password: string) {
36
+ const acc = getAccount(user)
37
+ if (!acc)
38
+ throw "user doesn't exist"
39
+ await updateAccount(acc!, acc => {
40
+ acc.password = password
41
+ })
42
+ },
43
+ async config(key: string, value: string) {
44
+ const conf = getConfigDefinition(key)
45
+ if (!conf)
46
+ throw "specified key doesn't exist"
47
+ let v: any = value
48
+ try { v = JSON.parse(v) }
49
+ catch {}
50
+ setConfig({ [key]: v })
51
+ },
52
+ async quit() {
53
+ process.exit(0)
54
+ }
55
+}
server/src/config.ts
+4
@@ -60,6 +60,10 @@ export function defineConfig<T>(k: string, defaultValue?: T) {
60
}
61
}
62
63
+export function getConfigDefinition(k: string) {
64
+ return configProps[k]
65
+}
66
+
67
const stack: any[] = []
68
function subscribeConfig<T>(k:string, cb: (v:T, was?:T)=>void) {
69
if (started) // initial event already passed, we'll make the first call
server/src/index.ts
+1
@@ -10,6 +10,7 @@ import { pluginsMiddleware } from './plugins'
10
import { throttler } from './throttler'
11
import { headRequests, gzipper, sessions, serveGuiAndSharedFiles, someSecurity, prepareState } from './middlewares'
12
import './listen'
13
+import './commands'
14
import { adminApis } from './adminApis'
15
import { defineConfig } from './config'
16
import { ok } from 'assert'
server/src/listen.ts
+3
@@ -11,6 +11,7 @@ import open from 'open'
11
import { debounceAsync, onlyTruthy, wait } from './misc'
12
import { ADMIN_URI, DEV } from './const'
13
import findProcess from 'find-process'
14
+import { anyAccountCanLoginAdmin } from './perm'
15
16
interface ServerExtra { name: string, error?: string, busy?: Promise<string> }
17
let httpSrv: http.Server & ServerExtra
@@ -33,6 +34,8 @@ portCfg.sub(async port => {
34
console.debug(String(e))
35
console.warn("cannot launch browser on this machine >PLEASE< open your browser and reach one of these (you may need a different address)",
36
...Object.values(getUrls()).flat().map(x => '\n - ' + x + ADMIN_URI))
37
+ if (! anyAccountCanLoginAdmin())
38
+ console.log(`HINT: you can enter command: create-admin YOUR_PASSWORD`)
39
})
40
})
41
server/src/perm.ts
+8
-11
@@ -7,8 +7,6 @@ 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'
10
11
export interface Account {
12
username: string, // we'll have username in it, so we don't need to pass it separately
@@ -100,15 +98,6 @@ accountsConfig.sub(async v => {
98
}))
99
})
100
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
-
101
function normalizeUsername(username: string) {
102
return username.toLocaleLowerCase()
103
}
@@ -195,3 +184,11 @@ export function accountHasPassword(account: Account) {
184
export function accountCanLogin(account: Account) {
185
return accountHasPassword(account)
186
}
187
+
188
+export function accountCanLoginAdmin(account: Account) {
189
+ return accountCanLogin(account) && getFromAccount(account, a => a.admin)
190
+}
191
+
192
+export function anyAccountCanLoginAdmin() {
193
+ return Object.values(accounts).find(accountCanLoginAdmin)
194
+}