hfs 0.26.8 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +15 -2
  2. package/admin/assets/index-509bb1d6.js +415 -0
  3. package/admin/assets/index-60a380a7.css +1 -0
  4. package/admin/assets/sha512-738f0943.js +8 -0
  5. package/admin/index.html +3 -1
  6. package/admin/{public/logo.svg → logo.svg} +0 -0
  7. package/frontend/assets/index-6e178dfd.css +1 -0
  8. package/frontend/assets/index-aea7654e.js +85 -0
  9. package/frontend/assets/sha512-bf915587.js +8 -0
  10. package/frontend/{public/fontello.css → fontello.css} +0 -0
  11. package/frontend/{public/fontello.woff2 → fontello.woff2} +0 -0
  12. package/frontend/index.html +4 -2
  13. package/package.json +2 -6
  14. package/plugins/vhosting/plugin.js +23 -20
  15. package/src/QuickZipStream.js +285 -0
  16. package/src/ThrottledStream.js +93 -0
  17. package/src/adminApis.js +169 -0
  18. package/src/api.accounts.js +59 -0
  19. package/src/api.auth.js +128 -0
  20. package/src/api.file_list.js +110 -0
  21. package/src/api.helpers.js +32 -0
  22. package/src/api.monitor.js +104 -0
  23. package/src/api.plugins.js +128 -0
  24. package/src/api.vfs.js +167 -0
  25. package/src/apiMiddleware.js +123 -0
  26. package/src/block.js +34 -0
  27. package/src/commands.js +125 -0
  28. package/src/config.js +168 -0
  29. package/src/connections.js +57 -0
  30. package/src/const.js +94 -0
  31. package/src/crypt.js +21 -0
  32. package/src/debounceAsync.js +49 -0
  33. package/src/events.js +9 -0
  34. package/src/frontEndApis.js +38 -0
  35. package/src/github.js +104 -0
  36. package/src/index.js +57 -0
  37. package/src/listen.js +235 -0
  38. package/src/log.js +137 -0
  39. package/src/middlewares.js +195 -0
  40. package/src/misc.js +160 -0
  41. package/src/pbkdf2.js +74 -0
  42. package/src/perm.js +183 -0
  43. package/src/plugins.js +343 -0
  44. package/src/serveFile.js +105 -0
  45. package/src/serveGuiFiles.js +113 -0
  46. package/src/sse.js +30 -0
  47. package/src/throttler.js +91 -0
  48. package/src/update.js +70 -0
  49. package/src/util-files.js +163 -0
  50. package/src/util-generators.js +31 -0
  51. package/src/util-http.js +32 -0
  52. package/src/vfs.js +232 -0
  53. package/src/watchLoad.js +73 -0
  54. package/src/zip.js +73 -0
  55. package/admin/.DS_Store +0 -0
  56. package/admin/.eslintrc +0 -8
  57. package/admin/.gitignore +0 -23
  58. package/admin/package.json +0 -67
  59. package/admin/src/AccountForm.ts +0 -92
  60. package/admin/src/AccountsPage.ts +0 -143
  61. package/admin/src/App.ts +0 -83
  62. package/admin/src/ArrayField.ts +0 -84
  63. package/admin/src/ConfigPage.ts +0 -279
  64. package/admin/src/FileField.ts +0 -52
  65. package/admin/src/FileForm.ts +0 -148
  66. package/admin/src/FilePicker.ts +0 -166
  67. package/admin/src/HomePage.ts +0 -96
  68. package/admin/src/InstalledPlugins.ts +0 -158
  69. package/admin/src/LoginRequired.ts +0 -75
  70. package/admin/src/LogoutPage.ts +0 -27
  71. package/admin/src/LogsPage.ts +0 -75
  72. package/admin/src/MainMenu.ts +0 -74
  73. package/admin/src/MenuButton.ts +0 -38
  74. package/admin/src/MonitorPage.ts +0 -200
  75. package/admin/src/OnlinePlugins.ts +0 -101
  76. package/admin/src/PermField.ts +0 -80
  77. package/admin/src/PluginsPage.ts +0 -27
  78. package/admin/src/VfsMenuBar.ts +0 -58
  79. package/admin/src/VfsPage.ts +0 -124
  80. package/admin/src/VfsTree.ts +0 -95
  81. package/admin/src/addFiles.ts +0 -59
  82. package/admin/src/api.ts +0 -246
  83. package/admin/src/dialog.ts +0 -203
  84. package/admin/src/index.css +0 -21
  85. package/admin/src/index.ts +0 -10
  86. package/admin/src/md.ts +0 -31
  87. package/admin/src/misc.ts +0 -141
  88. package/admin/src/react-app-env.d.ts +0 -1
  89. package/admin/src/reportWebVitals.ts +0 -15
  90. package/admin/src/setupTests.ts +0 -5
  91. package/admin/src/state.ts +0 -40
  92. package/admin/src/theme.ts +0 -37
  93. package/admin/tsconfig.json +0 -26
  94. package/admin/vite.config.ts +0 -32
  95. package/frontend/.DS_Store +0 -0
  96. package/frontend/.eslintrc +0 -8
  97. package/frontend/.gitignore +0 -23
  98. package/frontend/package.json +0 -51
  99. package/frontend/src/App.ts +0 -25
  100. package/frontend/src/Breadcrumbs.ts +0 -43
  101. package/frontend/src/BrowseFiles.ts +0 -141
  102. package/frontend/src/Head.ts +0 -45
  103. package/frontend/src/UserPanel.ts +0 -52
  104. package/frontend/src/api.ts +0 -78
  105. package/frontend/src/components.ts +0 -54
  106. package/frontend/src/dialog.css +0 -76
  107. package/frontend/src/dialog.ts +0 -105
  108. package/frontend/src/icons.ts +0 -46
  109. package/frontend/src/index.scss +0 -307
  110. package/frontend/src/index.ts +0 -10
  111. package/frontend/src/login.ts +0 -50
  112. package/frontend/src/menu.ts +0 -188
  113. package/frontend/src/misc.ts +0 -54
  114. package/frontend/src/options.ts +0 -52
  115. package/frontend/src/react-app-env.d.ts +0 -1
  116. package/frontend/src/reportWebVitals.ts +0 -15
  117. package/frontend/src/setupTests.ts +0 -5
  118. package/frontend/src/state.ts +0 -82
  119. package/frontend/src/useAuthorized.ts +0 -17
  120. package/frontend/src/useFetchList.ts +0 -144
  121. package/frontend/src/useTheme.ts +0 -23
  122. package/frontend/tsconfig.json +0 -26
  123. package/frontend/vite.config.ts +0 -21
  124. package/src/QuickZipStream.ts +0 -279
  125. package/src/ThrottledStream.ts +0 -98
  126. package/src/adminApis.ts +0 -161
  127. package/src/api.accounts.ts +0 -78
  128. package/src/api.auth.ts +0 -131
  129. package/src/api.file_list.ts +0 -102
  130. package/src/api.helpers.ts +0 -30
  131. package/src/api.monitor.ts +0 -106
  132. package/src/api.plugins.ts +0 -139
  133. package/src/api.vfs.ts +0 -182
  134. package/src/apiMiddleware.ts +0 -124
  135. package/src/block.ts +0 -35
  136. package/src/commands.ts +0 -122
  137. package/src/config.ts +0 -166
  138. package/src/connections.ts +0 -60
  139. package/src/const.ts +0 -57
  140. package/src/crypt.ts +0 -16
  141. package/src/debounceAsync.ts +0 -51
  142. package/src/events.ts +0 -6
  143. package/src/frontEndApis.ts +0 -17
  144. package/src/github.ts +0 -102
  145. package/src/index.ts +0 -53
  146. package/src/listen.ts +0 -220
  147. package/src/log.ts +0 -128
  148. package/src/middlewares.ts +0 -176
  149. package/src/misc.ts +0 -149
  150. package/src/pbkdf2.ts +0 -83
  151. package/src/perm.ts +0 -194
  152. package/src/plugins.ts +0 -342
  153. package/src/serveFile.ts +0 -104
  154. package/src/serveGuiFiles.ts +0 -95
  155. package/src/sse.ts +0 -29
  156. package/src/throttler.ts +0 -106
  157. package/src/update.ts +0 -67
  158. package/src/util-files.ts +0 -137
  159. package/src/util-generators.ts +0 -29
  160. package/src/util-http.ts +0 -29
  161. package/src/vfs.ts +0 -258
  162. package/src/watchLoad.ts +0 -75
  163. package/src/zip.ts +0 -69
package/src/misc.js ADDED
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
+ };
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.tryJson = exports.same = exports.isLocalHost = exports.with_ = exports.typedKeys = exports.objRenameKey = exports.onOff = exports.pendingPromise = exports.onlyTruthy = exports.truthy = exports.pattern2filter = exports.onFirstEvent = exports.onProcessExit = exports.randomId = exports.getOrSet = exports.wantArray = exports.wait = exports.objSameKeys = exports.setHidden = exports.prefix = exports.enforceFinal = exports.debounceAsync = void 0;
22
+ const path_1 = require("path");
23
+ const lodash_1 = __importDefault(require("lodash"));
24
+ const assert_1 = __importDefault(require("assert"));
25
+ __exportStar(require("./util-http"), exports);
26
+ __exportStar(require("./util-generators"), exports);
27
+ __exportStar(require("./util-files"), exports);
28
+ const debounceAsync_1 = __importDefault(require("./debounceAsync"));
29
+ exports.debounceAsync = debounceAsync_1.default;
30
+ function enforceFinal(sub, s) {
31
+ return s.endsWith(sub) ? s : s + sub;
32
+ }
33
+ exports.enforceFinal = enforceFinal;
34
+ function prefix(pre, v, post = '') {
35
+ return v ? pre + v + post : '';
36
+ }
37
+ exports.prefix = prefix;
38
+ function setHidden(dest, src) {
39
+ return Object.defineProperties(dest, objSameKeys(src, value => ({
40
+ enumerable: false,
41
+ writable: true,
42
+ value,
43
+ })));
44
+ }
45
+ exports.setHidden = setHidden;
46
+ function objSameKeys(src, newValue) {
47
+ return Object.fromEntries(Object.entries(src).map(([k, v]) => [k, newValue(v, k)]));
48
+ }
49
+ exports.objSameKeys = objSameKeys;
50
+ function wait(ms) {
51
+ return new Promise(res => setTimeout(res, ms));
52
+ }
53
+ exports.wait = wait;
54
+ function wantArray(x) {
55
+ return x == null ? [] : Array.isArray(x) ? x : [x];
56
+ }
57
+ exports.wantArray = wantArray;
58
+ function getOrSet(o, k, creator) {
59
+ return k in o ? o[k]
60
+ : (o[k] = creator());
61
+ }
62
+ exports.getOrSet = getOrSet;
63
+ // 10 chars is 51+bits, 8 is 41+bits
64
+ function randomId(len = 10) {
65
+ if (len > 10)
66
+ return randomId(10) + randomId(len - 10);
67
+ return Math.random()
68
+ .toString(36)
69
+ .substring(2, 2 + len)
70
+ .replace(/l/g, 'L'); // avoid confusion reading l1
71
+ }
72
+ exports.randomId = randomId;
73
+ const cbs = new Set();
74
+ function onProcessExit(cb) {
75
+ cbs.add(cb);
76
+ return () => cbs.delete(cb);
77
+ }
78
+ exports.onProcessExit = onProcessExit;
79
+ onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP'], signal => Promise.allSettled(Array.from(cbs).map(cb => cb(signal))).then(() => process.exit(0)));
80
+ function onFirstEvent(emitter, events, cb) {
81
+ let already = false;
82
+ for (const e of events)
83
+ emitter.on(e, (...args) => {
84
+ if (already)
85
+ return;
86
+ already = true;
87
+ cb(...args);
88
+ });
89
+ }
90
+ exports.onFirstEvent = onFirstEvent;
91
+ function pattern2filter(pattern) {
92
+ const re = new RegExp(lodash_1.default.escapeRegExp(pattern), 'i');
93
+ return (s) => !s || !pattern || re.test((0, path_1.basename)(s));
94
+ }
95
+ exports.pattern2filter = pattern2filter;
96
+ function truthy(value) {
97
+ return Boolean(value);
98
+ }
99
+ exports.truthy = truthy;
100
+ function onlyTruthy(arr) {
101
+ return arr.filter(truthy);
102
+ }
103
+ exports.onlyTruthy = onlyTruthy;
104
+ function pendingPromise() {
105
+ let takeOut;
106
+ const ret = new Promise((resolve, reject) => takeOut = { resolve, reject });
107
+ return Object.assign(ret, takeOut);
108
+ }
109
+ exports.pendingPromise = pendingPromise;
110
+ // install multiple handlers and returns a handy 'uninstall' function which requires no parameter. Pass a map {event:handler}
111
+ function onOff(em, events) {
112
+ events = { ...events }; // avoid later modifications, as we need this later for uninstallation
113
+ for (const [k, cb] of Object.entries(events))
114
+ for (const e of k.split(' '))
115
+ em.on(e, cb);
116
+ return () => {
117
+ for (const [k, cb] of Object.entries(events))
118
+ for (const e of k.split(' '))
119
+ em.off(e, cb);
120
+ };
121
+ }
122
+ exports.onOff = onOff;
123
+ function objRenameKey(o, from, to) {
124
+ if (!o || !o.hasOwnProperty(from) || from === to)
125
+ return;
126
+ o[to] = o[from];
127
+ delete o[from];
128
+ return true;
129
+ }
130
+ exports.objRenameKey = objRenameKey;
131
+ function typedKeys(o) {
132
+ return Object.keys(o);
133
+ }
134
+ exports.typedKeys = typedKeys;
135
+ function with_(par, cb) {
136
+ return cb(par);
137
+ }
138
+ exports.with_ = with_;
139
+ function isLocalHost(c) {
140
+ const ip = c.socket.remoteAddress; // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
141
+ return ip && (ip === '::1' || ip.endsWith('127.0.0.1'));
142
+ }
143
+ exports.isLocalHost = isLocalHost;
144
+ function same(a, b) {
145
+ try {
146
+ assert_1.default.deepStrictEqual(a, b);
147
+ return true;
148
+ }
149
+ catch (_a) {
150
+ return false;
151
+ }
152
+ }
153
+ exports.same = same;
154
+ function tryJson(s) {
155
+ try {
156
+ return s && JSON.parse(s);
157
+ }
158
+ catch (_a) { }
159
+ }
160
+ exports.tryJson = tryJson;
package/src/pbkdf2.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pbkdf2Verify = exports.pbkdf2 = void 0;
4
+ // @ts-nocheck
5
+ const node_crypto_1 = require("node:crypto");
6
+ // FROM https://gist.github.com/chrisveness/770ee96945ec12ac84f134bf538d89fb
7
+ /**
8
+ * Returns PBKDF2 derived key from supplied password.
9
+ *
10
+ * Stored key can subsequently be used to verify that a password matches the original password used
11
+ * to derive the key, using pbkdf2Verify().
12
+ *
13
+ * @param {String} password - Password to be hashed using key derivation function.
14
+ * @param {Number} [iterations=1e6] - Number of iterations of HMAC function to apply.
15
+ * @returns {String} Derived key as base64 string.
16
+ *
17
+ * @example
18
+ * const key = await pbkdf2('pāşšŵōřđ'); // eg 'djAxBRKXWNWPyXgpKWHld8SWJA9CQFmLyMbNet7Rle5RLKJAkBCllLfM6tPFa7bAis0lSTiB'
19
+ */
20
+ async function pbkdf2(password, iterations = 1e6) {
21
+ const pwUtf8 = new TextEncoder().encode(password); // encode pw as UTF-8
22
+ const pwKey = await node_crypto_1.webcrypto.subtle.importKey('raw', pwUtf8, 'PBKDF2', false, ['deriveBits']); // create pw key
23
+ const saltUint8 = node_crypto_1.webcrypto.getRandomValues(new Uint8Array(16)); // get random salt
24
+ const params = { name: 'PBKDF2', hash: 'SHA-256', salt: saltUint8, iterations: iterations }; // pbkdf2 params
25
+ const keyBuffer = await node_crypto_1.webcrypto.subtle.deriveBits(params, pwKey, 256); // derive key
26
+ const keyArray = Array.from(new Uint8Array(keyBuffer)); // key as byte array
27
+ const saltArray = Array.from(new Uint8Array(saltUint8)); // salt as byte array
28
+ const iterHex = ('000000' + iterations.toString(16)).slice(-6); // iter’n count as hex
29
+ const iterArray = iterHex.match(/.{2}/g).map(byte => parseInt(byte, 16)); // iter’ns as byte array
30
+ const compositeArray = [].concat(saltArray, iterArray, keyArray); // combined array
31
+ const compositeStr = compositeArray.map(byte => String.fromCharCode(byte)).join(''); // combined as string
32
+ // encode as base64
33
+ return btoa('v01' + compositeStr); // return composite key
34
+ }
35
+ exports.pbkdf2 = pbkdf2;
36
+ /**
37
+ * Verifies whether the supplied password matches the password previously used to generate the key.
38
+ *
39
+ * @param {String} key - Key previously generated with pbkdf2().
40
+ * @param {String} password - Password to be matched against previously derived key.
41
+ * @returns {boolean} Whether password matches key.
42
+ *
43
+ * @example
44
+ * const match = await pbkdf2Verify(key, 'pāşšŵōřđ'); // true
45
+ */
46
+ async function pbkdf2Verify(key, password) {
47
+ let compositeStr = null; // composite key is salt, iteration count, and derived key
48
+ try {
49
+ compositeStr = atob(key);
50
+ }
51
+ catch (e) {
52
+ throw new Error('Invalid key');
53
+ } // decode from base64
54
+ const version = compositeStr.slice(0, 3); // 3 bytes
55
+ const saltStr = compositeStr.slice(3, 19); // 16 bytes (128 bits)
56
+ const iterStr = compositeStr.slice(19, 22); // 3 bytes
57
+ const keyStr = compositeStr.slice(22, 54); // 32 bytes (256 bits)
58
+ if (version !== 'v01')
59
+ throw new Error('Invalid key');
60
+ // -- recover salt & iterations from stored (composite) key
61
+ const saltUint8 = new Uint8Array(saltStr.match(/./g).map(ch => ch.charCodeAt(0))); // salt as Uint8Array
62
+ // note: cannot use TextEncoder().encode(saltStr) as it generates UTF-8
63
+ const iterHex = iterStr.match(/./g).map(ch => ch.charCodeAt(0).toString(16)).join(''); // iter’n count as hex
64
+ const iterations = parseInt(iterHex, 16); // iter’ns
65
+ // -- generate new key from stored salt & iterations and supplied password
66
+ const pwUtf8 = new TextEncoder().encode(password); // encode pw as UTF-8
67
+ const pwKey = await node_crypto_1.webcrypto.subtle.importKey('raw', pwUtf8, 'PBKDF2', false, ['deriveBits']); // create pw key
68
+ const params = { name: 'PBKDF2', hash: 'SHA-256', salt: saltUint8, iterations: iterations }; // pbkdf params
69
+ const keyBuffer = await node_crypto_1.webcrypto.subtle.deriveBits(params, pwKey, 256); // derive key
70
+ const keyArray = Array.from(new Uint8Array(keyBuffer)); // key as byte array
71
+ const keyStrNew = keyArray.map(byte => String.fromCharCode(byte)).join(''); // key as string
72
+ return keyStrNew === keyStr; // test if newly generated key matches stored key
73
+ }
74
+ exports.pbkdf2Verify = pbkdf2Verify;
package/src/perm.js ADDED
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.anyAccountCanLoginAdmin = exports.accountCanLoginAdmin = exports.accountCanLogin = exports.accountHasPassword = exports.getFromAccount = exports.delAccount = exports.setAccount = exports.addAccount = exports.renameAccount = exports.normalizeUsername = exports.accountsConfig = exports.updateAccount = exports.allowClearTextLogin = exports.saveSrpInfo = exports.getAccount = exports.getCurrentUsernameExpanded = exports.getCurrentUsername = void 0;
8
+ const lodash_1 = __importDefault(require("lodash"));
9
+ const crypt_1 = require("./crypt");
10
+ const misc_1 = require("./misc");
11
+ const config_1 = require("./config");
12
+ const tssrp6a_1 = require("tssrp6a");
13
+ const events_1 = __importDefault(require("./events"));
14
+ let accounts = {};
15
+ function getCurrentUsername(ctx) {
16
+ var _a;
17
+ return ((_a = ctx.state.account) === null || _a === void 0 ? void 0 : _a.username) || '';
18
+ }
19
+ exports.getCurrentUsername = getCurrentUsername;
20
+ // provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
21
+ function getCurrentUsernameExpanded(ctx) {
22
+ const who = getCurrentUsername(ctx);
23
+ if (!who)
24
+ return [];
25
+ const ret = [who];
26
+ for (const u of ret) {
27
+ const a = getAccount(u);
28
+ if (a === null || a === void 0 ? void 0 : a.belongs)
29
+ ret.push(...a.belongs);
30
+ }
31
+ return ret;
32
+ }
33
+ exports.getCurrentUsernameExpanded = getCurrentUsernameExpanded;
34
+ function getAccount(username, normalize = true) {
35
+ if (normalize)
36
+ username = normalizeUsername(username);
37
+ return username ? accounts[username] : undefined;
38
+ }
39
+ exports.getAccount = getAccount;
40
+ function saveSrpInfo(account, salt, verifier) {
41
+ account.srp = String(salt) + '|' + String(verifier);
42
+ }
43
+ exports.saveSrpInfo = saveSrpInfo;
44
+ exports.allowClearTextLogin = (0, config_1.defineConfig)('allow_clear_text_login');
45
+ const srp6aNimbusRoutines = new tssrp6a_1.SRPRoutines(new tssrp6a_1.SRPParameters());
46
+ async function updateAccount(account, changer) {
47
+ const was = JSON.stringify(account);
48
+ await (changer === null || changer === void 0 ? void 0 : changer(account));
49
+ const { username } = account;
50
+ if (account.password) {
51
+ console.debug('hashing password for', username);
52
+ if (exports.allowClearTextLogin.get())
53
+ account.hashed_password = await (0, crypt_1.hashPassword)(account.password);
54
+ const res = await (0, tssrp6a_1.createVerifierAndSalt)(srp6aNimbusRoutines, username, account.password);
55
+ saveSrpInfo(account, res.s, res.v);
56
+ delete account.password;
57
+ }
58
+ else if (!account.srp && account.hashed_password) {
59
+ console.log('please reset password for account', username);
60
+ process.exit(1);
61
+ }
62
+ if (account.belongs) {
63
+ account.belongs = (0, misc_1.wantArray)(account.belongs);
64
+ lodash_1.default.remove(account.belongs, b => {
65
+ if (b in accounts)
66
+ return;
67
+ console.error(`account ${username} belongs to non-existing ${b}`);
68
+ return true;
69
+ });
70
+ }
71
+ if (was !== JSON.stringify(account))
72
+ saveAccountsAsap();
73
+ }
74
+ exports.updateAccount = updateAccount;
75
+ const saveAccountsAsap = () => { (0, config_1.saveConfigAsap)().then(); };
76
+ exports.accountsConfig = (0, config_1.defineConfig)('accounts', {});
77
+ exports.accountsConfig.sub(obj => {
78
+ // consider some validation here
79
+ lodash_1.default.each(accounts = obj, (rec, k) => {
80
+ const norm = normalizeUsername(k);
81
+ if ((rec === null || rec === void 0 ? void 0 : rec.username) !== norm) {
82
+ if (!rec) // an empty object in yaml is parsed as null
83
+ rec = obj[norm] = { username: norm };
84
+ else if ((0, misc_1.objRenameKey)(obj, k, norm))
85
+ saveAccountsAsap();
86
+ (0, misc_1.setHidden)(rec, { username: norm });
87
+ }
88
+ updateAccount(rec).then(); // work password fields
89
+ });
90
+ });
91
+ function normalizeUsername(username) {
92
+ return username.toLocaleLowerCase();
93
+ }
94
+ exports.normalizeUsername = normalizeUsername;
95
+ function renameAccount(from, to) {
96
+ from = normalizeUsername(from);
97
+ to = normalizeUsername(to);
98
+ if (!to || !accounts[from] || accounts[to])
99
+ return false;
100
+ if (to === from)
101
+ return true;
102
+ (0, misc_1.objRenameKey)(accounts, from, to);
103
+ updateReferences();
104
+ saveAccountsAsap();
105
+ return true;
106
+ function updateReferences() {
107
+ var _a;
108
+ (0, misc_1.setHidden)(accounts[to], { username: to });
109
+ for (const a of Object.values(accounts)) {
110
+ const idx = (_a = a.belongs) === null || _a === void 0 ? void 0 : _a.indexOf(from);
111
+ if (idx !== undefined && idx >= 0)
112
+ a.belongs[idx] = to;
113
+ }
114
+ events_1.default.emit('accountRenamed', from, to); // everybody, take care of your stuff
115
+ }
116
+ }
117
+ exports.renameAccount = renameAccount;
118
+ // we consider all the following fields, when falsy, as equivalent to be missing. If this changes in the future, please adjust addAccount and setAccount
119
+ const assignableProps = ['redirect', 'ignore_limits', 'belongs', 'admin'];
120
+ function addAccount(username, props) {
121
+ username = normalizeUsername(username);
122
+ if (!username || getAccount(username, false))
123
+ return;
124
+ const filteredProps = lodash_1.default.pickBy(lodash_1.default.pick(props, assignableProps), Boolean);
125
+ const copy = (0, misc_1.setHidden)(filteredProps, { username }); // have the field in the object but hidden so that stringification won't include it
126
+ exports.accountsConfig.set(accounts => Object.assign(accounts, { [username]: copy }));
127
+ return copy;
128
+ }
129
+ exports.addAccount = addAccount;
130
+ function setAccount(username, changes) {
131
+ const acc = getAccount(username);
132
+ if (!acc)
133
+ return false;
134
+ const rest = lodash_1.default.pick(changes, assignableProps);
135
+ for (const [k, v] of Object.entries(rest))
136
+ if (!v)
137
+ rest[k] = undefined;
138
+ Object.assign(acc, rest);
139
+ if (changes.username)
140
+ renameAccount(username, changes.username);
141
+ saveAccountsAsap();
142
+ return acc;
143
+ }
144
+ exports.setAccount = setAccount;
145
+ function delAccount(username) {
146
+ if (!getAccount(username))
147
+ return false;
148
+ exports.accountsConfig.set(accounts => Object.assign(accounts, { [normalizeUsername(username)]: undefined }));
149
+ saveAccountsAsap();
150
+ return true;
151
+ }
152
+ exports.delAccount = delAccount;
153
+ // get some property from account, searching in its groups if necessary. Search is breadth-first, and this determines priority of inheritance.
154
+ function getFromAccount(account, getter) {
155
+ const search = [account];
156
+ for (const accountOrUsername of search) {
157
+ const a = typeof accountOrUsername === 'string' ? getAccount(accountOrUsername) : accountOrUsername;
158
+ if (!a)
159
+ continue;
160
+ const res = getter(a);
161
+ if (res !== undefined)
162
+ return res;
163
+ if (a.belongs)
164
+ search.push(...a.belongs);
165
+ }
166
+ }
167
+ exports.getFromAccount = getFromAccount;
168
+ function accountHasPassword(account) {
169
+ return Boolean(account.password || account.hashed_password || account.srp);
170
+ }
171
+ exports.accountHasPassword = accountHasPassword;
172
+ function accountCanLogin(account) {
173
+ return accountHasPassword(account);
174
+ }
175
+ exports.accountCanLogin = accountCanLogin;
176
+ function accountCanLoginAdmin(account) {
177
+ return accountCanLogin(account) && Boolean(getFromAccount(account, a => a.admin));
178
+ }
179
+ exports.accountCanLoginAdmin = accountCanLoginAdmin;
180
+ function anyAccountCanLoginAdmin() {
181
+ return Boolean(lodash_1.default.find(exports.accountsConfig.get(), accountCanLoginAdmin));
182
+ }
183
+ exports.anyAccountCanLoginAdmin = anyAccountCanLoginAdmin;