@ti-engine/web-framework 1.20.1 → 1.23.0
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.
- package/CHANGELOG.md +92 -0
- package/README.md +52 -0
- package/bin/build/hash-password.js +36 -0
- package/bin/config/local-users.example.json +8 -0
- package/bin/localization/web-server-labels.json +165 -1
- package/bin/static/fragments/components/component-sidebar.html +25 -0
- package/bin/static/fragments/frame-about.html +96 -0
- package/bin/static/fragments/frame-login.html +6 -1
- package/bin/static/fragments/frame-profile.html +103 -3
- package/bin/static/scripts/ti-framework.css +143 -0
- package/bin/static/scripts/ti-framework.js +339 -0
- package/bin/web-app-manager.js +235 -2
- package/bin/web-server.js +18 -1
- package/bin/web-server.json +3 -0
- package/components/application-info.js +190 -0
- package/components/auth-manager.js +159 -18
- package/components/definitions.types.js +65 -0
- package/components/local-user-directory.js +428 -0
- package/components/web-config-env.js +6 -1
- package/components/web-handlers.js +10 -2
- package/package.json +15 -2
- package/types/bin/web-app-manager.d.ts +95 -1
- package/types/bin/web-server.d.ts +19 -1
- package/types/components/application-info.d.ts +53 -0
- package/types/components/auth-manager.d.ts +7 -0
- package/types/components/definitions.types.d.ts +166 -0
- package/types/components/local-user-directory.d.ts +111 -0
- package/types/components/web-config-env.d.ts +1 -1
|
@@ -13,12 +13,19 @@ export type ApiConfig = {
|
|
|
13
13
|
};
|
|
14
14
|
export type SettingsAuth = {
|
|
15
15
|
enabledMethods: string[];
|
|
16
|
-
local:
|
|
16
|
+
local: SettingsAuthLocal;
|
|
17
17
|
oauth2: {
|
|
18
18
|
azure?: SettingsOAuth2Client;
|
|
19
19
|
google?: SettingsOAuth2Client;
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
+
export type SettingsAuthLocal = {
|
|
23
|
+
/**
|
|
24
|
+
* Path to the JSON file of local user records (see `TI_WEB_AUTH_LOCAL_USERS_PATH`).
|
|
25
|
+
* Local sign-in refuses everyone whenever this is absent, unreadable, or yields no usable records.
|
|
26
|
+
*/
|
|
27
|
+
usersPath?: string;
|
|
28
|
+
};
|
|
22
29
|
export type SettingsOAuth2Client = {
|
|
23
30
|
clientID?: string;
|
|
24
31
|
clientSecret?: string;
|
|
@@ -179,6 +186,11 @@ declare class TiWebServer extends ServiceConsumer {
|
|
|
179
186
|
* Hook for the application to augment the freshly-authenticated session (e.g. derive domain roles from an
|
|
180
187
|
* identity store or the org chart). Runs synchronously, once per login, before the framework's additive `admin`
|
|
181
188
|
* role is applied. The default is a no-op. Any test-user role injection is an override of whatever the app derives.
|
|
189
|
+
* <br/>
|
|
190
|
+
* **Refusing a login.** Throwing from this hook refuses the sign-in: the framework destroys the freshly regenerated
|
|
191
|
+
* session (so no usable session survives the refusal), the login handler raises `401`, and the error handler
|
|
192
|
+
* redirects the browser to the login page with the exception code in `?error=`. Throw when the authenticated
|
|
193
|
+
* identity cannot be mapped to an application principal; return the session unchanged to accept it.
|
|
182
194
|
*
|
|
183
195
|
* @method
|
|
184
196
|
* @virtual
|
|
@@ -200,6 +212,12 @@ declare class TiWebServer extends ServiceConsumer {
|
|
|
200
212
|
authenticate(authMethod: TiAuthMethod, authDetails?: Object): Promise<any>;
|
|
201
213
|
/**
|
|
202
214
|
* Used to set up user authorization according to the specified auth method.
|
|
215
|
+
* <br/>
|
|
216
|
+
* NOTE: This presupposes a successful, immediately preceding {@link TiWebServer#authenticate} call for the same
|
|
217
|
+
* credentials — it is **not** an independent authentication check. For the `local` method it builds the session
|
|
218
|
+
* user from the directory record named by `oidc.username`, verifying that the record exists and is not disabled
|
|
219
|
+
* but performing no password comparison of its own; the framework's own login route calls `authenticate` first.
|
|
220
|
+
* Calling this directly without that preceding step would mint a session for any known username.
|
|
203
221
|
*
|
|
204
222
|
* @method
|
|
205
223
|
* @param {TiAuthMethod} authMethod
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
declare const _exports: {
|
|
2
|
+
buildApplicationInfo: typeof buildApplicationInfo;
|
|
3
|
+
readApplicationManifest: typeof readApplicationManifest;
|
|
4
|
+
};
|
|
5
|
+
export = _exports;
|
|
6
|
+
import type { TiApplicationInfo } from "#definitions";
|
|
7
|
+
/**
|
|
8
|
+
* Builds the normalized application-information descriptor that backs the framework "About" screen.
|
|
9
|
+
* <br/>
|
|
10
|
+
* The function is PURE — everything it needs is injected — so the whole resolution order (manifest → environment
|
|
11
|
+
* override) is unit-testable without touching the filesystem or `process.env`. The impure half, reading the
|
|
12
|
+
* consuming application's manifest, is {@link readApplicationManifest}.
|
|
13
|
+
* <br/>
|
|
14
|
+
* Resolution order for the three overridable fields is manifest first, environment last:
|
|
15
|
+
* - `TI_WEB_APP_NAME` overrides `manifest.displayName` / a display name derived from `manifest.name`;
|
|
16
|
+
* - `TI_WEB_APP_VERSION` overrides `manifest.version`;
|
|
17
|
+
* - `TI_WEB_APP_RELEASE_DATE` overrides `manifest.releaseDate`.
|
|
18
|
+
* <br/>
|
|
19
|
+
* The environment wins because it is how a container image stamps facts that its baked-in manifest cannot know —
|
|
20
|
+
* most importantly the build/release date, for which `package.json` has no standard field at all.
|
|
21
|
+
*
|
|
22
|
+
* @method
|
|
23
|
+
* @param {Object} [options]
|
|
24
|
+
* @param {Object} [options.manifest] A `package.json`-shaped object for the consuming application.
|
|
25
|
+
* @param {Object} [options.env] The environment source (injectable for testing).
|
|
26
|
+
* @param {Array<{name: string, version: string}>} [options.components] Framework component versions to list.
|
|
27
|
+
* @param {Object} [options.runtime] Runtime facts (node/platform/instance). Included verbatim when present; the
|
|
28
|
+
* caller decides whether the current session is allowed to see them.
|
|
29
|
+
* @returns {TiApplicationInfo}
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
declare function buildApplicationInfo(options?: {
|
|
33
|
+
manifest?: Object;
|
|
34
|
+
env?: Object;
|
|
35
|
+
components?: Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
version: string;
|
|
38
|
+
}>;
|
|
39
|
+
runtime?: Object;
|
|
40
|
+
}): TiApplicationInfo;
|
|
41
|
+
/**
|
|
42
|
+
* Reads the consuming application's `package.json`. This is the one impure function in this module.
|
|
43
|
+
* <br/>
|
|
44
|
+
* NOTE: A missing or malformed manifest resolves to an empty object rather than throwing — an informational screen
|
|
45
|
+
* must never be the reason a request fails, and {@link buildApplicationInfo} produces a usable (if sparse)
|
|
46
|
+
* descriptor from `{}`.
|
|
47
|
+
*
|
|
48
|
+
* @method
|
|
49
|
+
* @param {string} [directory=process.cwd()] The directory holding the manifest.
|
|
50
|
+
* @returns {Object}
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
declare function readApplicationManifest(directory?: string): Object;
|
|
@@ -70,6 +70,13 @@ declare class AuthManager {
|
|
|
70
70
|
authenticate(authMethod: TiAuthMethod, authDetails: Object): Promise<Object>;
|
|
71
71
|
/**
|
|
72
72
|
* Used to set up user authorization according to the specified authentication method.
|
|
73
|
+
* <br/>
|
|
74
|
+
* NOTE: This presupposes a successful, immediately preceding {@link AuthManager#authenticate} call for the
|
|
75
|
+
* same credentials and is NOT an independent authentication check on its own — for `LOCAL` it performs no
|
|
76
|
+
* password verification. It refuses an absent, disabled, or (for `LOCAL`) not-yet-usable-directory record,
|
|
77
|
+
* but a caller that invokes it without having just authenticated bypasses password verification entirely.
|
|
78
|
+
* The framework's own login route always calls `authenticate()` first (see `web-handlers.js`); this method
|
|
79
|
+
* is public on both `AuthManager` and `TiWebServer`, so any other caller must preserve that ordering itself.
|
|
73
80
|
*
|
|
74
81
|
* @method
|
|
75
82
|
* @param {TiAuthMethod} authMethod
|
|
@@ -11,6 +11,112 @@ export type TiSession = {
|
|
|
11
11
|
destroy: (callback: TiSessionCallback) => TiSession;
|
|
12
12
|
save: (callback?: TiSessionCallback) => TiSession;
|
|
13
13
|
};
|
|
14
|
+
export type TiInfoItem = {
|
|
15
|
+
label: string;
|
|
16
|
+
value?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Renders the value as a link to this target. Only `http:`, `https:` and `mailto:` are
|
|
19
|
+
* honoured — any other scheme is dropped client-side and the item degrades to plain text.
|
|
20
|
+
*/
|
|
21
|
+
href?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Span the full width of the section grid instead of one column.
|
|
24
|
+
*/
|
|
25
|
+
wide?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Render the value in the monospaced face (IDs, versions, hashes).
|
|
28
|
+
*/
|
|
29
|
+
mono?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Render the value as a dimmed hint rather than primary text.
|
|
32
|
+
*/
|
|
33
|
+
muted?: boolean;
|
|
34
|
+
};
|
|
35
|
+
export type TiInfoSection = {
|
|
36
|
+
title: string;
|
|
37
|
+
/**
|
|
38
|
+
* Optional intro line under the section title.
|
|
39
|
+
*/
|
|
40
|
+
description?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional `ti-icon` variant name for the section head.
|
|
43
|
+
*/
|
|
44
|
+
icon?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Claim the full row of the two-up section grid instead of one column.
|
|
47
|
+
*/
|
|
48
|
+
wide?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A section with no items is dropped rather than rendered empty.
|
|
51
|
+
*/
|
|
52
|
+
items: TiInfoItem[];
|
|
53
|
+
};
|
|
54
|
+
export type TiProfileIdentity = {
|
|
55
|
+
name: string;
|
|
56
|
+
/**
|
|
57
|
+
* Meta line under the name (e.g. `role family · specialization · unit`).
|
|
58
|
+
*/
|
|
59
|
+
subtitle?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Secondary line under the subtitle (e.g. the corporate e-mail).
|
|
62
|
+
*/
|
|
63
|
+
caption?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Stable seed for the deterministic avatar colour; defaults to the name.
|
|
66
|
+
*/
|
|
67
|
+
avatarSeed?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Small qualifier rendered inside the meta line.
|
|
70
|
+
*/
|
|
71
|
+
badge?: {
|
|
72
|
+
text: string;
|
|
73
|
+
tone?: string;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Pills beside the name.
|
|
77
|
+
*/
|
|
78
|
+
tags?: Array<{
|
|
79
|
+
text: string;
|
|
80
|
+
tone?: string;
|
|
81
|
+
dot?: boolean;
|
|
82
|
+
mono?: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
};
|
|
85
|
+
export type TiProfileInfo = {
|
|
86
|
+
identity: TiProfileIdentity;
|
|
87
|
+
sections: TiInfoSection[];
|
|
88
|
+
};
|
|
89
|
+
export type TiApplicationInfo = {
|
|
90
|
+
/**
|
|
91
|
+
* Display name of the application.
|
|
92
|
+
*/
|
|
93
|
+
name: string;
|
|
94
|
+
/**
|
|
95
|
+
* The npm package name it was resolved from.
|
|
96
|
+
*/
|
|
97
|
+
packageName: string;
|
|
98
|
+
version: string;
|
|
99
|
+
releaseDate: string;
|
|
100
|
+
description: string;
|
|
101
|
+
license: string;
|
|
102
|
+
homepage: string;
|
|
103
|
+
author: string;
|
|
104
|
+
/**
|
|
105
|
+
* Framework component versions.
|
|
106
|
+
*/
|
|
107
|
+
components: Array<{
|
|
108
|
+
name: string;
|
|
109
|
+
version: string;
|
|
110
|
+
}>;
|
|
111
|
+
/**
|
|
112
|
+
* Runtime facts (node/platform/instance), or `null` when withheld.
|
|
113
|
+
*/
|
|
114
|
+
runtime: Object | null;
|
|
115
|
+
/**
|
|
116
|
+
* Application-contributed extra sections.
|
|
117
|
+
*/
|
|
118
|
+
sections: TiInfoSection[];
|
|
119
|
+
};
|
|
14
120
|
/** @import { TiLocalizationLanguage } from "@ti-engine/core/localization" */
|
|
15
121
|
/**
|
|
16
122
|
* @callback TiSessionCallback
|
|
@@ -29,3 +135,63 @@ export type TiSession = {
|
|
|
29
135
|
* @property {(callback: TiSessionCallback) => TiSession} destroy
|
|
30
136
|
* @property {(callback?: TiSessionCallback) => TiSession} save
|
|
31
137
|
*/
|
|
138
|
+
/**
|
|
139
|
+
* One label/value pair inside a {@link TiInfoSection}. Both strings are display-ready — already localized and
|
|
140
|
+
* already formatted by the server, since that is where the session language and the label catalogue live. The
|
|
141
|
+
* three flags are purely presentational; an empty `value` renders the screen's placeholder.
|
|
142
|
+
*
|
|
143
|
+
* @typedef {Object} TiInfoItem
|
|
144
|
+
* @property {string} label
|
|
145
|
+
* @property {string} [value]
|
|
146
|
+
* @property {string} [href] Renders the value as a link to this target. Only `http:`, `https:` and `mailto:` are
|
|
147
|
+
* honoured — any other scheme is dropped client-side and the item degrades to plain text.
|
|
148
|
+
* @property {boolean} [wide] Span the full width of the section grid instead of one column.
|
|
149
|
+
* @property {boolean} [mono] Render the value in the monospaced face (IDs, versions, hashes).
|
|
150
|
+
* @property {boolean} [muted] Render the value as a dimmed hint rather than primary text.
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* A titled group of label/value pairs. The framework's Profile and About screens render an array of these
|
|
154
|
+
* generically, so an application contributes content without contributing layout.
|
|
155
|
+
*
|
|
156
|
+
* @typedef {Object} TiInfoSection
|
|
157
|
+
* @property {string} title
|
|
158
|
+
* @property {string} [description] Optional intro line under the section title.
|
|
159
|
+
* @property {string} [icon] Optional `ti-icon` variant name for the section head.
|
|
160
|
+
* @property {boolean} [wide] Claim the full row of the two-up section grid instead of one column.
|
|
161
|
+
* @property {TiInfoItem[]} items A section with no items is dropped rather than rendered empty.
|
|
162
|
+
*/
|
|
163
|
+
/**
|
|
164
|
+
* The identity header of the Profile screen — the avatar/name block and the pills beside it.
|
|
165
|
+
*
|
|
166
|
+
* @typedef {Object} TiProfileIdentity
|
|
167
|
+
* @property {string} name
|
|
168
|
+
* @property {string} [subtitle] Meta line under the name (e.g. `role family · specialization · unit`).
|
|
169
|
+
* @property {string} [caption] Secondary line under the subtitle (e.g. the corporate e-mail).
|
|
170
|
+
* @property {string} [avatarSeed] Stable seed for the deterministic avatar colour; defaults to the name.
|
|
171
|
+
* @property {{text: string, tone?: string}} [badge] Small qualifier rendered inside the meta line.
|
|
172
|
+
* @property {Array<{text: string, tone?: string, dot?: boolean, mono?: boolean}>} [tags] Pills beside the name.
|
|
173
|
+
*/
|
|
174
|
+
/**
|
|
175
|
+
* The descriptor backing the framework Profile screen.
|
|
176
|
+
*
|
|
177
|
+
* @typedef {Object} TiProfileInfo
|
|
178
|
+
* @property {TiProfileIdentity} identity
|
|
179
|
+
* @property {TiInfoSection[]} sections
|
|
180
|
+
*/
|
|
181
|
+
/**
|
|
182
|
+
* The descriptor backing the framework About screen. Produced by `buildApplicationInfo` and optionally extended by
|
|
183
|
+
* the application through {@link TiWebAppManager#getApplicationInfo}.
|
|
184
|
+
*
|
|
185
|
+
* @typedef {Object} TiApplicationInfo
|
|
186
|
+
* @property {string} name Display name of the application.
|
|
187
|
+
* @property {string} packageName The npm package name it was resolved from.
|
|
188
|
+
* @property {string} version
|
|
189
|
+
* @property {string} releaseDate
|
|
190
|
+
* @property {string} description
|
|
191
|
+
* @property {string} license
|
|
192
|
+
* @property {string} homepage
|
|
193
|
+
* @property {string} author
|
|
194
|
+
* @property {Array<{name: string, version: string}>} components Framework component versions.
|
|
195
|
+
* @property {Object|null} runtime Runtime facts (node/platform/instance), or `null` when withheld.
|
|
196
|
+
* @property {TiInfoSection[]} sections Application-contributed extra sections.
|
|
197
|
+
*/
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
declare const _exports: {
|
|
2
|
+
ALGORITHM: string;
|
|
3
|
+
CACHE_KEY: string;
|
|
4
|
+
HASH_DEFAULTS: Readonly<{
|
|
5
|
+
N: 16384;
|
|
6
|
+
r: 8;
|
|
7
|
+
p: 1;
|
|
8
|
+
saltBytes: 16;
|
|
9
|
+
keyBytes: 64;
|
|
10
|
+
}>;
|
|
11
|
+
hashPassword: typeof hashPassword;
|
|
12
|
+
verifyPassword: typeof verifyPassword;
|
|
13
|
+
parseRecords: typeof parseRecords;
|
|
14
|
+
reconcile: typeof reconcile;
|
|
15
|
+
findByUsername: typeof findByUsername;
|
|
16
|
+
};
|
|
17
|
+
export = _exports;
|
|
18
|
+
export type LocalUserRecord = {
|
|
19
|
+
userID: string;
|
|
20
|
+
username: string;
|
|
21
|
+
email: string;
|
|
22
|
+
name: string;
|
|
23
|
+
passwordHash: string;
|
|
24
|
+
disabled: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Hashes a password for storage in a local-users file. Synchronous because its only caller is the one-shot CLI,
|
|
28
|
+
* where blocking is free — never call it on a request path.
|
|
29
|
+
*
|
|
30
|
+
* @method
|
|
31
|
+
* @param {string} password
|
|
32
|
+
* @returns {string} The encoded hash: `scrypt$N$r$p$salt$hash`, base64 salt and key.
|
|
33
|
+
* @throws {TypeError} If `password` is empty or not a string — `verifyPassword` refuses empty passwords, so
|
|
34
|
+
* hashing one here would only mint a hash that can never be logged into.
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
declare function hashPassword(password: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Verifies a password against an encoded hash. The cost parameters come from the stored string rather than the
|
|
40
|
+
* current defaults, so raising the defaults never invalidates an existing hash.
|
|
41
|
+
*
|
|
42
|
+
* @method
|
|
43
|
+
* @param {string} password
|
|
44
|
+
* @param {string} encoded
|
|
45
|
+
* @returns {Promise<boolean>} `false` for a malformed encoding or an absent password — never a throw, because a
|
|
46
|
+
* bad stored value must read as "does not match", not as a server error on the login path.
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
declare function verifyPassword(password: string, encoded: string): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Validates raw file content into records, reporting why any entry was excluded. Never throws: a malformed row is
|
|
52
|
+
* data, not a crash, so one bad entry cannot take an instance down.
|
|
53
|
+
*
|
|
54
|
+
* @method
|
|
55
|
+
* @param {*} raw
|
|
56
|
+
* @returns {{records: LocalUserRecord[], problems: string[]}}
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
declare function parseRecords(raw: any): {
|
|
60
|
+
records: LocalUserRecord[];
|
|
61
|
+
problems: string[];
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Writes the records as the complete directory, keyed by username, and reports what changed.
|
|
65
|
+
* <br/>
|
|
66
|
+
* The whole set is written rather than patched because the file is the source of truth: a username absent from
|
|
67
|
+
* `records` must disappear, which is what makes revocation-by-file-edit work. `@ti-engine/core/cache` exposes no
|
|
68
|
+
* delete, so a whole-object write is also the only way to remove a key.
|
|
69
|
+
* <br/>
|
|
70
|
+
* Usernames are attacker-influenceable (the local sign-in handler resolves them from request input), so both the write
|
|
71
|
+
* and every read below are guarded against `Object.prototype`'s reserved names rather than trusting plain bracket
|
|
72
|
+
* access:
|
|
73
|
+
* <br/>
|
|
74
|
+
* - `incoming` is built with a null prototype (`Object.create( null )`) so it inherits nothing. On an ordinary
|
|
75
|
+
* `{}`, `incoming[ "__proto__" ] = record` would not create an own key at all — it would invoke the inherited
|
|
76
|
+
* `__proto__` setter and silently repoint the object's own prototype to `record`, so the record never shows up
|
|
77
|
+
* in `Object.keys`/`JSON.stringify` and is never persisted, without error. On a null-prototype object that
|
|
78
|
+
* setter does not exist anywhere on the (empty) prototype chain, so the assignment falls back to creating a
|
|
79
|
+
* perfectly ordinary own data property instead — confirmed empirically (see the test file) that this still
|
|
80
|
+
* `JSON.stringify`s and round-trips normally.
|
|
81
|
+
* - Every classification read below checks ownership with `Object.prototype.hasOwnProperty.call(...)` rather than
|
|
82
|
+
* relying on truthiness, because `stored` comes back from `readStored()` — ultimately a `JSON.parse` result —
|
|
83
|
+
* with the ordinary `Object.prototype` chain. An unguarded `stored[ "constructor" ]` would resolve to the
|
|
84
|
+
* inherited `Object` constructor function (always truthy) rather than "not present", misclassifying a
|
|
85
|
+
* first-time `constructor`-named user as `updated` instead of `added`, and hiding its removal from `removed`.
|
|
86
|
+
*
|
|
87
|
+
* @method
|
|
88
|
+
* @param {LocalUserRecord[]} records
|
|
89
|
+
* @returns {Promise<{added: string[], updated: string[], removed: string[]}>}
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
declare function reconcile(records: LocalUserRecord[]): Promise<{
|
|
93
|
+
added: string[];
|
|
94
|
+
updated: string[];
|
|
95
|
+
removed: string[];
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Looks a user up by exact username.
|
|
99
|
+
* <br/>
|
|
100
|
+
* `username` here is attacker-influenceable — this is the function the local sign-in handler calls with the
|
|
101
|
+
* value a client typed into the username field. Checked with `Object.prototype.hasOwnProperty.call(...)` rather than
|
|
102
|
+
* `stored[ username ] || null`, because `stored` carries the ordinary `Object.prototype` chain and an unguarded
|
|
103
|
+
* bracket read would resolve `findByUsername( "constructor" )` to the inherited `Object` constructor function
|
|
104
|
+
* instead of `null`, violating the declared return type for nearly every real query.
|
|
105
|
+
*
|
|
106
|
+
* @method
|
|
107
|
+
* @param {string} username
|
|
108
|
+
* @returns {Promise<LocalUserRecord|null>}
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
declare function findByUsername(username: string): Promise<LocalUserRecord | null>;
|
|
@@ -4,7 +4,7 @@ export = applyWebConfigEnvOverrides;
|
|
|
4
4
|
* Each override is applied ONLY when its environment variable is defined, so an absent variable leaves the
|
|
5
5
|
* configured/default value untouched (fully backward compatible). This gives ti-engine web servers 12-factor,
|
|
6
6
|
* container-friendly control over network binding, TLS, the session cookie secret, the enabled authentication
|
|
7
|
-
* methods, the admin allowlist, the trusted request origins, and the `/static` cache policy without editing config files. Note `TI_WEB_AUTH_METHODS`,
|
|
7
|
+
* methods, the admin allowlist, the local auth users file path, the trusted request origins, and the `/static` cache policy without editing config files. Note `TI_WEB_AUTH_METHODS`,
|
|
8
8
|
* `TI_WEB_AUTH_ADMINS`, `TI_WEB_TRUSTED_ORIGINS`, and `TI_WEB_STATIC_IMMUTABLE_PATHS` fully REPLACE their config arrays (`auth.enabledMethods` / `auth.admins` / `trustedOrigins` / `staticCache.immutablePaths`) rather than
|
|
9
9
|
* merging — the config-file merge is by-index and cannot cleanly override an array.
|
|
10
10
|
*
|