@dbx-tools/email 0.6.41 → 0.6.42
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/index.ts +10 -1
- package/lib/index.d.ts +10 -1
- package/lib/index.js +8 -1
- package/lib/src/auth/allowlist.d.ts +27 -0
- package/lib/src/auth/allowlist.js +82 -0
- package/lib/src/auth/gate.d.ts +83 -0
- package/lib/src/auth/gate.js +134 -0
- package/lib/src/auth/otp.d.ts +49 -0
- package/lib/src/auth/otp.js +135 -0
- package/lib/src/auth/rate-limit.d.ts +35 -0
- package/lib/src/auth/rate-limit.js +53 -0
- package/lib/src/config.d.ts +40 -0
- package/lib/src/config.js +54 -2
- package/lib/src/plugin.d.ts +23 -0
- package/lib/src/plugin.js +134 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/auth/allowlist.ts +88 -0
- package/src/auth/gate.ts +163 -0
- package/src/auth/otp.ts +151 -0
- package/src/auth/rate-limit.ts +59 -0
- package/src/config.ts +90 -1
- package/src/plugin.ts +153 -1
package/lib/src/plugin.js
CHANGED
|
@@ -26,7 +26,9 @@ import { AuthenticationError, ConfigurationError, ConnectionError, ExecutionErro
|
|
|
26
26
|
import { defineTool, executeFromRegistry, toolsFromRegistry, } from "@databricks/appkit/beta";
|
|
27
27
|
import { log } from "@dbx-tools/shared-core";
|
|
28
28
|
import { email as emailWire, } from "@dbx-tools/shared-email";
|
|
29
|
-
import {
|
|
29
|
+
import { authRequestSchema, authVerifySchema } from "@dbx-tools/shared-email";
|
|
30
|
+
import { EMAIL_CONFIG_SCHEMA, resolveAuthConfig } from "./config.js";
|
|
31
|
+
import { AuthGate, SESSION_COOKIE } from "./auth/gate.js";
|
|
30
32
|
import { EMAIL_SENDERS_SETTINGS, EMAIL_VERIFY_SETTINGS } from "./defaults.js";
|
|
31
33
|
import { isSenderAllowed, listSenderOptions, resolveSenderAddress } from "./sender.js";
|
|
32
34
|
import { SEND_EMAIL_DESCRIPTION } from "./tool.js";
|
|
@@ -57,6 +59,8 @@ const logger = log.logger("email");
|
|
|
57
59
|
* ```
|
|
58
60
|
*/
|
|
59
61
|
export class EmailPlugin extends Plugin {
|
|
62
|
+
/** The email-OTP access gate, constructed in {@link setup} when `auth.enabled`. */
|
|
63
|
+
authGate;
|
|
60
64
|
static manifest = {
|
|
61
65
|
name: "email",
|
|
62
66
|
displayName: "Email",
|
|
@@ -103,6 +107,7 @@ export class EmailPlugin extends Plugin {
|
|
|
103
107
|
async setup() {
|
|
104
108
|
const { transporter, config } = getEmailRuntime(this.config);
|
|
105
109
|
setEmailExecutor((fn, settings) => this.execute(fn, settings));
|
|
110
|
+
this.setupAuthGate();
|
|
106
111
|
const policy = {
|
|
107
112
|
mode: config.mode,
|
|
108
113
|
senderPolicy: config.senderPolicy,
|
|
@@ -169,6 +174,75 @@ export class EmailPlugin extends Plugin {
|
|
|
169
174
|
res.json(result.data);
|
|
170
175
|
},
|
|
171
176
|
});
|
|
177
|
+
this.injectAuthRoutes(router);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Mount the email-OTP login flow under `/api/email/auth/*` when the gate is
|
|
181
|
+
* enabled. These routes are the ONLY ones the gate middleware leaves open (see
|
|
182
|
+
* {@link AuthGate.middleware}); everything else requires the session cookie
|
|
183
|
+
* they establish. No-op when auth is off.
|
|
184
|
+
*/
|
|
185
|
+
injectAuthRoutes(router) {
|
|
186
|
+
const gate = this.authGate;
|
|
187
|
+
if (!gate)
|
|
188
|
+
return;
|
|
189
|
+
// `request` and `verify` take a raw email/code, not an OBO user, so they are
|
|
190
|
+
// NOT wrapped in `asUser` - the caller is anonymous until verified.
|
|
191
|
+
this.route(router, {
|
|
192
|
+
name: "authRequest",
|
|
193
|
+
method: "post",
|
|
194
|
+
path: "/auth/request",
|
|
195
|
+
handler: async (req, res) => {
|
|
196
|
+
const parsed = authRequestSchema.safeParse(req.body);
|
|
197
|
+
if (!parsed.success) {
|
|
198
|
+
// Even a malformed body reports success (anti-enumeration).
|
|
199
|
+
res.json({ ok: true });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const result = await gate.handleRequest(parsed.data.email, this.clientIp(req));
|
|
203
|
+
res.json(result);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
this.route(router, {
|
|
207
|
+
name: "authVerify",
|
|
208
|
+
method: "post",
|
|
209
|
+
path: "/auth/verify",
|
|
210
|
+
handler: async (req, res) => {
|
|
211
|
+
const parsed = authVerifySchema.safeParse(req.body);
|
|
212
|
+
if (!parsed.success) {
|
|
213
|
+
res.json({ ok: false });
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const result = await gate.handleVerify(parsed.data.email, parsed.data.code, this.clientIp(req), req.secure);
|
|
217
|
+
if (result.ok && result.token && result.cookieOptions) {
|
|
218
|
+
res.cookie(SESSION_COOKIE, result.token, result.cookieOptions);
|
|
219
|
+
}
|
|
220
|
+
res.json({ ok: result.ok, ...(result.retryAfter ? { retryAfter: result.retryAfter } : {}) });
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
this.route(router, {
|
|
224
|
+
name: "authLogout",
|
|
225
|
+
method: "post",
|
|
226
|
+
path: "/auth/logout",
|
|
227
|
+
handler: async (_req, res) => {
|
|
228
|
+
res.clearCookie(SESSION_COOKIE, { path: "/" });
|
|
229
|
+
res.json({ ok: true });
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
this.route(router, {
|
|
233
|
+
name: "authStatus",
|
|
234
|
+
method: "get",
|
|
235
|
+
path: "/auth/status",
|
|
236
|
+
handler: async (req, res) => {
|
|
237
|
+
res.json(await gate.status(req));
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/** Client IP for rate-limiting, honoring the Apps ingress `x-forwarded-for`. */
|
|
242
|
+
clientIp(req) {
|
|
243
|
+
const fwd = req.headers["x-forwarded-for"];
|
|
244
|
+
const first = Array.isArray(fwd) ? fwd[0] : fwd?.split(",")[0];
|
|
245
|
+
return (first ?? req.ip ?? "unknown").trim();
|
|
172
246
|
}
|
|
173
247
|
exports() {
|
|
174
248
|
return {
|
|
@@ -206,6 +280,64 @@ export class EmailPlugin extends Plugin {
|
|
|
206
280
|
async send(message, from, signal) {
|
|
207
281
|
return sendEmail(message, from ?? this.resolveSender(), signal);
|
|
208
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Build the email-OTP access gate when `auth.enabled`, wiring its code
|
|
285
|
+
* delivery through this plugin's own transport. Called from {@link setup}; a
|
|
286
|
+
* no-op (leaves {@link authGate} undefined) when the gate is off.
|
|
287
|
+
*/
|
|
288
|
+
setupAuthGate() {
|
|
289
|
+
const auth = resolveAuthConfig(this.config.auth);
|
|
290
|
+
if (!auth)
|
|
291
|
+
return;
|
|
292
|
+
const gate = new AuthGate({
|
|
293
|
+
allow: auth.allow,
|
|
294
|
+
sessionTtlSeconds: auth.sessionTtlSeconds,
|
|
295
|
+
codeTtlSeconds: auth.codeTtlSeconds,
|
|
296
|
+
maxAttempts: auth.maxAttempts,
|
|
297
|
+
secureCookies: process.env.NODE_ENV === "production",
|
|
298
|
+
sendCode: (email, code) => this.sendOtpEmail(email, code),
|
|
299
|
+
});
|
|
300
|
+
this.authGate = gate;
|
|
301
|
+
// The gate must protect the WHOLE app, but a plugin's own `injectRoutes`
|
|
302
|
+
// router only covers `/api/email/*`. AppKit's `server` plugin exposes
|
|
303
|
+
// `addExtension(fn)` for exactly this: an app-level middleware registered
|
|
304
|
+
// before the server listens. Look it up by its registered name (no
|
|
305
|
+
// dependency on a specific server-plugin package) and mount the gate there.
|
|
306
|
+
// The login routes themselves are exempted by the middleware's `emailBase`
|
|
307
|
+
// open-prefix (`/api/email/auth`).
|
|
308
|
+
const server = this.context?.getPlugins().get("server");
|
|
309
|
+
if (server?.addExtension) {
|
|
310
|
+
server.addExtension((app) => app.use(gate.middleware(`/api/${EmailPlugin.manifest.name}`)));
|
|
311
|
+
logger.info("auth:enabled", {
|
|
312
|
+
patterns: auth.allow.length,
|
|
313
|
+
sessionTtlSeconds: auth.sessionTtlSeconds,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// No server plugin to gate through - the login routes still work, but the
|
|
318
|
+
// rest of the app would be ungated, so refuse to half-enable silently.
|
|
319
|
+
this.authGate = undefined;
|
|
320
|
+
logger.warn("auth:disabled - the `server` plugin is required to gate the app; add server() to your plugins");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Deliver a one-time code to `email` through this plugin's transport. Throws
|
|
325
|
+
* on failure (the gate swallows it, so a delivery error never leaks whether
|
|
326
|
+
* the address was allow-listed).
|
|
327
|
+
*/
|
|
328
|
+
async sendOtpEmail(email, code) {
|
|
329
|
+
await this.send({
|
|
330
|
+
to: [email],
|
|
331
|
+
subject: `Your sign-in code: ${code}`,
|
|
332
|
+
body: [
|
|
333
|
+
`Your one-time sign-in code is:`,
|
|
334
|
+
``,
|
|
335
|
+
`## ${code}`,
|
|
336
|
+
``,
|
|
337
|
+
`It expires shortly. If you didn't request this, you can ignore this email.`,
|
|
338
|
+
].join("\n"),
|
|
339
|
+
}, undefined);
|
|
340
|
+
}
|
|
209
341
|
/** Run the sender-options lookup through the plugin's interceptor chain. */
|
|
210
342
|
async executeListSenders() {
|
|
211
343
|
return this.execute(async () => this.listSenders(), EMAIL_SENDERS_SETTINGS);
|
|
@@ -291,4 +423,4 @@ function unwrap(result) {
|
|
|
291
423
|
* ```
|
|
292
424
|
*/
|
|
293
425
|
export const email = toPlugin(EmailPlugin);
|
|
294
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFFSCxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsY0FBYyxFQUNkLG1CQUFtQixFQUNuQixNQUFNLEVBQ04sUUFBUSxFQUNSLGVBQWUsR0FLaEIsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsVUFBVSxFQUNWLG1CQUFtQixFQUNuQixpQkFBaUIsR0FJbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0MsT0FBTyxFQUNMLEtBQUssSUFBSSxTQUFTLEdBSW5CLE1BQU0seUJBQXlCLENBQUM7QUFDakMsT0FBTyxFQUFFLG1CQUFtQixFQUEwQixNQUFNLGFBQWEsQ0FBQztBQUMxRSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDOUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN2RixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDbkQsT0FBTyxFQUNMLGVBQWUsRUFDZixpQkFBaUIsRUFDakIsU0FBUyxFQUNULGdCQUFnQixFQUNoQixvQkFBb0IsR0FDckIsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QiwrRUFBK0U7QUFDL0UsTUFBTSxhQUFhLEdBQUcsVUFBVSxDQUFDO0FBRWpDLDRFQUE0RTtBQUM1RSxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUM7QUFFekIsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUVuQzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztB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QUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzVELElBQUksTUFBTSxLQUFLLEdBQUc7UUFBRSxPQUFPLElBQUksZUFBZSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3hELE9BQU8sSUFBSSxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7QUFDckMsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQVMsTUFBTSxDQUFJLE1BQTBCO0lBQzNDLElBQUksTUFBTSxDQUFDLEVBQUU7UUFBRSxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUM7SUFDbEMsTUFBTSxhQUFhLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7QUFDckQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQXBwS2l0IHBsdWdpbiAocmVnaXN0ZXJlZCBuYW1lOiBgZW1haWxgKSB0aGF0IG93bnMgdGhlIFNNVFAgcnVudGltZVxuICogZm9yIG91dGJvdW5kIG1haWwuIFJlZ2lzdGVyaW5nIGl0IHZhbGlkYXRlcyB0aGUgU01UUCBjb25maWd1cmF0aW9uXG4gKiBhbmQgdmVyaWZpZXMgY29ubmVjdGl2aXR5IGF0IHN0YXJ0dXAsIHNvIGEgYmFkIGhvc3QgLyBjcmVkZW50aWFsXG4gKiBzdXJmYWNlcyBpbiB0aGUgYm9vdCBsb2dzIGluc3RlYWQgb2Ygb24gdGhlIGZpcnN0IGFwcHJvdmVkIHNlbmQsIGFuZFxuICogaW5zdGFsbHMgdGhpcyBwbHVnaW4ncyBgZXhlY3V0ZSgpYCBhcyB0aGUgcnVudGltZSdzIGV4ZWN1dG9yIHNvIGV2ZXJ5XG4gKiBzZW5kIHBpY2tzIHVwIEFwcEtpdCdzIHJldHJ5IC8gdGltZW91dCAvIHRlbGVtZXRyeSBjaGFpbi5cbiAqXG4gKiBUaGUgcGx1Z2luIGlzIGFsc28gYSBgVG9vbFByb3ZpZGVyYCwgc28gYW4gQXBwS2l0IGFnZW50IGNhbiByZWFjaCBhblxuICogYGVtYWlsLnNlbmRgIHRvb2wgZGlyZWN0bHk7IHRoZSB7QGxpbmsgZW1haWxUb29sfSBleHBvcnQgaXMgdGhlIHNhbWVcbiAqIGNhcGFiaWxpdHkgZm9yIGEgTWFzdHJhIGFnZW50LiBCb3RoIHNoYXJlIHRoZSB0cmFuc3BvcnQgcHJpbWVkIGhlcmUsXG4gKiBhbmQge0BsaW5rIHNlbmRFbWFpbH0gaXMgYXZhaWxhYmxlIHRvIG5vbi1hZ2VudCBjYWxsZXJzLlxuICpcbiAqIENvbmZpZ3VyYXRpb24gaXMgdGhlIG1hbmlmZXN0LXB1Ymxpc2hlZCB7QGxpbmsgRW1haWxQbHVnaW5Db25maWd9XG4gKiAoU01UUCBob3N0L3BvcnQvY3JlZGVudGlhbHMsIHNlbmRlciBkb21haW4gb3IgZXhwbGljaXQgYGZyb21gLCB0aGVcbiAqIHNlbmRlciBwb2xpY3ksIGFuZCBhbiBvcHRpb25hbCBgYWxsb3dlZFNlbmRlcnNgIHJlc3RyaWN0aW9uKSwgd2l0aFxuICogdW5wcmVmaXhlZCBgU01UUF8qYCAvIGBFTUFJTF8qYCBlbnZpcm9ubWVudCBmYWxsYmFja3MuXG4gKlxuICogVGhlIHBsdWdpbiBtb3VudHMgb25lIHJvdXRlIHVuZGVyIGl0cyBiYXNlIHBhdGggKGAvYXBpL2VtYWlsYCk6XG4gKiBgR0VUIC9zZW5kZXJzYCByZXR1cm5zIHRoZSBwZXJtaXR0ZWQgYEZyb21gIG9wdGlvbnMgZm9yIHRoZSBjYWxsaW5nXG4gKiB1c2VyLCBzbyBhIGNvbXBvc2UgVUkgY2FuIG9mZmVyIHRoZW0gaW4gYSBkcm9wZG93bi5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHtcbiAgQXV0aGVudGljYXRpb25FcnJvcixcbiAgQ29uZmlndXJhdGlvbkVycm9yLFxuICBDb25uZWN0aW9uRXJyb3IsXG4gIEV4ZWN1dGlvbkVycm9yLFxuICBnZXRFeGVjdXRpb25Db250ZXh0LFxuICBQbHVnaW4sXG4gIHRvUGx1Z2luLFxuICBWYWxpZGF0aW9uRXJyb3IsXG4gIHR5cGUgQXBwS2l0RXJyb3IsXG4gIHR5cGUgRXhlY3V0aW9uUmVzdWx0LFxuICB0eXBlIElBcHBSb3V0ZXIsXG4gIHR5cGUgUGx1Z2luTWFuaWZlc3QsXG59IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7XG4gIGRlZmluZVRvb2wsXG4gIGV4ZWN1dGVGcm9tUmVnaXN0cnksXG4gIHRvb2xzRnJvbVJlZ2lzdHJ5LFxuICB0eXBlIEFnZW50VG9vbERlZmluaXRpb24sXG4gIHR5cGUgVG9vbFByb3ZpZGVyLFxuICB0eXBlIFRvb2xSZWdpc3RyeSxcbn0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdC9iZXRhXCI7XG5pbXBvcnQgeyBsb2cgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHtcbiAgZW1haWwgYXMgZW1haWxXaXJlLFxuICB0eXBlIEVtYWlsTWVzc2FnZSxcbiAgdHlwZSBFbWFpbFJlc3VsdCxcbiAgdHlwZSBFbWFpbFNlbmRlcnMsXG59IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1lbWFpbFwiO1xuaW1wb3J0IHsgRU1BSUxfQ09ORklHX1NDSEVNQSwgdHlwZSBFbWFpbFBsdWdpbkNvbmZpZyB9IGZyb20gXCIuL2NvbmZpZy50c1wiO1xuaW1wb3J0IHsgRU1BSUxfU0VOREVSU19TRVRUSU5HUywgRU1BSUxfVkVSSUZZX1NFVFRJTkdTIH0gZnJvbSBcIi4vZGVmYXVsdHMudHNcIjtcbmltcG9ydCB7IGlzU2VuZGVyQWxsb3dlZCwgbGlzdFNlbmRlck9wdGlvbnMsIHJlc29sdmVTZW5kZXJBZGRyZXNzIH0gZnJvbSBcIi4vc2VuZGVyLnRzXCI7XG5pbXBvcnQgeyBTRU5EX0VNQUlMX0RFU0NSSVBUSU9OIH0gZnJvbSBcIi4vdG9vbC50c1wiO1xuaW1wb3J0IHtcbiAgZ2V0RW1haWxSdW50aW1lLFxuICByZXNldEVtYWlsUnVudGltZSxcbiAgc2VuZEVtYWlsLFxuICBzZXRFbWFpbEV4ZWN1dG9yLFxuICB2ZXJpZnlFbWFpbFRyYW5zcG9ydCxcbn0gZnJvbSBcIi4vdHJhbnNwb3J0LnRzXCI7XG5cbi8qKiBNb3VudC1yZWxhdGl2ZSByb3V0ZSAodW5kZXIgYC9hcGkvZW1haWxgKSBmb3IgdGhlIHNlbmRlci1vcHRpb25zIGxvb2t1cC4gKi9cbmNvbnN0IFNFTkRFUlNfUk9VVEUgPSBcIi9zZW5kZXJzXCI7XG5cbi8qKiBSZWdpc3RyeSBrZXkgb2YgdGhlIGFnZW50IHRvb2wsIHdoaWNoIGFnZW50cyBhZGRyZXNzIGFzIGBlbWFpbC5zZW5kYC4gKi9cbmNvbnN0IFNFTkRfVE9PTCA9IFwic2VuZFwiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwiZW1haWxcIik7XG5cbi8qKlxuICogQXBwS2l0IHBsdWdpbiB0aGF0IGNvbmZpZ3VyZXMgYW5kIHZlcmlmaWVzIHRoZSBTTVRQIHRyYW5zcG9ydCB1c2VkIGJ5XG4gKiB0aGUgYHNlbmRfZW1haWxgIHRvb2wsIGFuZCBleHBvc2VzIHNlbmRpbmcgYXMgYW4gQXBwS2l0IGFnZW50IHRvb2wuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBpbXBvcnQgeyBjcmVhdGVBcHAsIHNlcnZlciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IHBsdWdpbiBhcyBlbWFpbFBsdWdpbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2VtYWlsXCI7XG4gKlxuICogYXdhaXQgY3JlYXRlQXBwKHtcbiAqICAgcGx1Z2luczogW1xuICogICAgIHNlcnZlcigpLFxuICogICAgIGVtYWlsUGx1Z2luLmVtYWlsKHtcbiAqICAgICAgIHNtdHA6IHsgaG9zdDogXCJzbXRwLmV4YW1wbGUuY29tXCIsIHVzZXI6IFwiYXBpa2V5XCIsIHBhc3N3b3JkOiBwcm9jZXNzLmVudi5TTVRQX0tFWSB9LFxuICogICAgICAgZG9tYWluOiBcIm1haWwuZXhhbXBsZS5jb21cIixcbiAqICAgICB9KSxcbiAqICAgXSxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjbGFzcyBFbWFpbFBsdWdpbiBleHRlbmRzIFBsdWdpbjxFbWFpbFBsdWdpbkNvbmZpZz4gaW1wbGVtZW50cyBUb29sUHJvdmlkZXIge1xuICBzdGF0aWMgbWFuaWZlc3QgPSB7XG4gICAgbmFtZTogXCJlbWFpbFwiLFxuICAgIGRpc3BsYXlOYW1lOiBcIkVtYWlsXCIsXG4gICAgZGVzY3JpcHRpb246XG4gICAgICBcIlNlbmRzIGFwcHJvdmFsLWdhdGVkIGVtYWlsIG92ZXIgU01UUCwgd2l0aCB0aGUgc2VuZGVyIGRlcml2ZWQgZnJvbSBcIiArXG4gICAgICBcInRoZSBvbi1iZWhhbGYtb2YgdXNlcidzIGFkZHJlc3Mgb24gYSBjb25maWd1cmVkIGRvbWFpbi5cIixcbiAgICBzdGFiaWxpdHk6IFwiYmV0YVwiLFxuICAgIHJlc291cmNlczoge1xuICAgICAgcmVxdWlyZWQ6IFtdLFxuICAgICAgb3B0aW9uYWw6IFtdLFxuICAgIH0sXG4gICAgY29uZmlnOiB7IHNjaGVtYTogRU1BSUxfQ09ORklHX1NDSEVNQSB9LFxuICB9IHNhdGlzZmllcyBQbHVnaW5NYW5pZmVzdDxcImVtYWlsXCI+O1xuXG4gIC8qKlxuICAgKiBUaGUgdG9vbCB0aGlzIHBsdWdpbiBvZmZlcnMgdG8gYW4gQXBwS2l0IGFnZW50LlxuICAgKlxuICAgKiBOb3QgYGF1dG9Jbmhlcml0YWJsZWA6IGEgc2VuZCBpcyBpcnJldmVyc2libGUgYW5kIGxlYXZlcyB0aGUgd29ya3NwYWNlLFxuICAgKiBzbyBpdCBtdXN0IG9ubHkgYXBwZWFyIGluIGFuIGFnZW50IHRoYXQgYXNrZWQgZm9yIGl0IGFuZCBhY2NlcHRlZCB0aGVcbiAgICogc2VuZGVyIHBvbGljeSB0aGF0IGNvbWVzIHdpdGggaXQuXG4gICAqXG4gICAqIGBleGVjdXRlYCByZS1wYXJzZXMgaXRzIGFyZ3VtZW50cyB3aXRoIHRoZSBsb2NhbCBzY2hlbWE6IEFwcEtpdCB2YWxpZGF0ZXNcbiAgICogYWdhaW5zdCB0aGUgc2FtZSBzY2hlbWEgZmlyc3QsIGJ1dCByZS1wYXJzaW5nIGlzIHdoYXQgZ2l2ZXMgdGhlIGJvZHkgdHlwZWRcbiAgICogYXJndW1lbnRzIGluc3RlYWQgb2YgYHVua25vd25gLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSB0b29sczogVG9vbFJlZ2lzdHJ5ID0ge1xuICAgIFtTRU5EX1RPT0xdOiBkZWZpbmVUb29sKHtcbiAgICAgIGRlc2NyaXB0aW9uOiBTRU5EX0VNQUlMX0RFU0NSSVBUSU9OLFxuICAgICAgc2NoZW1hOiBlbWFpbFdpcmUuZW1haWxNZXNzYWdlU2NoZW1hLFxuICAgICAgYW5ub3RhdGlvbnM6IHsgZWZmZWN0OiBcIndyaXRlXCIsIHJlcXVpcmVzVXNlckNvbnRleHQ6IHRydWUgfSxcbiAgICAgIGF1dG9Jbmhlcml0YWJsZTogZmFsc2UsXG4gICAgICBleGVjdXRlOiBhc3luYyAoYXJncywgc2lnbmFsKSA9PlxuICAgICAgICB0aGlzLnNlbmQoZW1haWxXaXJlLmVtYWlsTWVzc2FnZVNjaGVtYS5wYXJzZShhcmdzKSwgdW5kZWZpbmVkLCBzaWduYWwpLFxuICAgIH0pLFxuICB9O1xuXG4gIC8qKlxuICAgKiBQcmltZSB0aGUgc2hhcmVkIHJ1bnRpbWUgZnJvbSB0aGlzIHBsdWdpbidzIGNvbmZpZyAob3ZlciBlbnYpLCByb3V0ZSB0aGVcbiAgICogdG9vbHMnIHNlbmRzIHRocm91Z2ggdGhpcyBwbHVnaW4ncyBpbnRlcmNlcHRvciBjaGFpbiwgYW5kIGxvZyB0aGVcbiAgICogZWZmZWN0aXZlIHNlbmRlciBwb2xpY3kgc28gYW4gYWN0aXZlIHJlc3RyaWN0aW9uIGlzIG9idmlvdXMgYXQgYm9vdC4gSW5cbiAgICogU01UUCBtb2RlLCBmYWlsIHNldHVwIHdoZW4gdGhlIHRyYW5zcG9ydCBjYW5ub3QgYmUgdmVyaWZpZWQ6IGEgYmFkIGhvc3RcbiAgICogb3IgY3JlZGVudGlhbCBpcyBhIGRlcGxveS10aW1lIG1pc3Rha2UgYW5kIHNob3VsZCBzdG9wIHRoZSBhcHAgcmF0aGVyXG4gICAqIHRoYW4gd2FpdCBmb3IgYSB1c2VyIHRvIGFwcHJvdmUgYSBzZW5kIHRoYXQgY2Fubm90IHdvcmsuIFdpdGggbm8gU01UUFxuICAgKiBjcmVkZW50aWFscyB0aGUgcnVudGltZSBpcyBpbiBmaWxlL291dGJveCBtb2RlIChvbmx5IHdoZW5cbiAgICogYEVNQUlMX09VVEJPWF9NT0RFYCBpcyBzZXQpLCBsb2dnZWQgbG91ZGx5IGhlcmUgc28gaXQgaXMgb2J2aW91cyBtYWlsXG4gICAqIGlzIGJlaW5nIHdyaXR0ZW4gdG8gZGlzayByYXRoZXIgdGhhbiBzZW50LlxuICAgKi9cbiAgb3ZlcnJpZGUgYXN5bmMgc2V0dXAoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyB0cmFuc3BvcnRlciwgY29uZmlnIH0gPSBnZXRFbWFpbFJ1bnRpbWUodGhpcy5jb25maWcpO1xuICAgIHNldEVtYWlsRXhlY3V0b3IoKGZuLCBzZXR0aW5ncykgPT4gdGhpcy5leGVjdXRlKGZuLCBzZXR0aW5ncykpO1xuICAgIGNvbnN0IHBvbGljeSA9IHtcbiAgICAgIG1vZGU6IGNvbmZpZy5tb2RlLFxuICAgICAgc2VuZGVyUG9saWN5OiBjb25maWcuc2VuZGVyUG9saWN5LFxuICAgICAgcmVzdHJpY3RlZDogY29uZmlnLmFsbG93ZWRTZW5kZXJzLmxlbmd0aCA+IDAsXG4gICAgICAuLi4oY29uZmlnLmFsbG93ZWRTZW5kZXJzLmxlbmd0aCA+IDAgPyB7IGFsbG93ZWRTZW5kZXJzOiBjb25maWcuYWxsb3dlZFNlbmRlcnMgfSA6IHt9KSxcbiAgICB9O1xuICAgIGlmIChjb25maWcubW9kZSA9PT0gXCJmaWxlXCIpIHtcbiAgICAgIGxvZ2dlci53YXJuKFwib3V0Ym94OmVuYWJsZWRcIiwge1xuICAgICAgICBkaXI6IGNvbmZpZy5vdXREaXIsXG4gICAgICAgIHJlYXNvbjogXCJubyBTTVRQIGNyZWRlbnRpYWxzIGNvbmZpZ3VyZWQ7IGVtYWlscyBhcmUgd3JpdHRlbiB0byBkaXNrIGluc3RlYWQgb2Ygc2VudFwiLFxuICAgICAgfSk7XG4gICAgICBsb2dnZXIuaW5mbyhcInJlYWR5XCIsIHBvbGljeSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHZlcmlmaWVkID0gYXdhaXQgdGhpcy5leGVjdXRlKFxuICAgICAgYXN5bmMgKHNpZ25hbCkgPT4gdmVyaWZ5RW1haWxUcmFuc3BvcnQodHJhbnNwb3J0ZXIsIHNpZ25hbCksXG4gICAgICBFTUFJTF9WRVJJRllfU0VUVElOR1MsXG4gICAgKTtcbiAgICBpZiAoIXZlcmlmaWVkLm9rKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoXCJzbXRwOnVudmVyaWZpZWRcIiwge1xuICAgICAgICBob3N0OiBjb25maWcuaG9zdCxcbiAgICAgICAgcG9ydDogY29uZmlnLnBvcnQsXG4gICAgICAgIHN0YXR1czogdmVyaWZpZWQuc3RhdHVzLFxuICAgICAgICBlcnJvcjogdmVyaWZpZWQubWVzc2FnZSxcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgQ29uZmlndXJhdGlvbkVycm9yLmludmFsaWRDb25uZWN0aW9uKFxuICAgICAgICBcIlNNVFBcIixcbiAgICAgICAgYENvdWxkIG5vdCB2ZXJpZnkgJHtjb25maWcuaG9zdH06JHtjb25maWcucG9ydH0uIENoZWNrIFNNVFBfSE9TVCwgU01UUF9QT1JULCBTTVRQX1NFQ1VSRSwgYW5kIHRoZSBjcmVkZW50aWFscywgb3Igc2V0IEVNQUlMX09VVEJPWF9NT0RFPTEgZm9yIGxvY2FsIG91dGJveCB0ZXN0aW5nLmAsXG4gICAgICApO1xuICAgIH1cbiAgICBsb2dnZXIuaW5mbyhcInJlYWR5XCIsIHtcbiAgICAgIC4uLnBvbGljeSxcbiAgICAgIGhvc3Q6IGNvbmZpZy5ob3N0LFxuICAgICAgcG9ydDogY29uZmlnLnBvcnQsXG4gICAgICBzZWN1cmU6IGNvbmZpZy5zZWN1cmUsXG4gICAgfSk7XG4gIH1cblxuICAvKiogQ2xvc2UgdGhlIFNNVFAgY29ubmVjdGlvbiBwb29sLiBJZGVtcG90ZW50LiAqL1xuICBhc3luYyBzaHV0ZG93bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXNldEVtYWlsUnVudGltZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFib3J0IGluLWZsaWdodCB3b3JrLiBBcHBLaXQncyBncmFjZWZ1bCBzaHV0ZG93biBvbmx5IGludm9rZXMgdGhpcyBob29rIC1cbiAgICogaXQgbmV2ZXIgY2FsbHMge0BsaW5rIHNodXRkb3dufSAtIHNvIHRoZSBTTVRQIHBvb2wgaXMgY2xvc2VkIGZyb20gaGVyZSBvclxuICAgKiBpdCBsZWFrcyBhdCBTSUdURVJNLiBUaGUgdGVhcmRvd24gaXMgc3luY2hyb25vdXMgYW5kIGlkZW1wb3RlbnQsIHNvIHRoZVxuICAgKiB1bi1hd2FpdGVkIGNhbGwgY29zdHMgbm90aGluZy5cbiAgICovXG4gIG92ZXJyaWRlIGFib3J0QWN0aXZlT3BlcmF0aW9ucygpOiB2b2lkIHtcbiAgICBzdXBlci5hYm9ydEFjdGl2ZU9wZXJhdGlvbnMoKTtcbiAgICB2b2lkIHRoaXMuc2h1dGRvd24oKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeHBvc2UgdGhlIHNlbmRlci1vcHRpb25zIGxvb2t1cCBzbyBVSSBjb21wb3NlIHZpZXdzIGNhbiBwb3B1bGF0ZSBhXG4gICAqIGBGcm9tYCBkcm9wZG93biBmcm9tIHRoZSBjb25maWd1cmVkIGFsbG93LWxpc3QuIE1vdW50ZWQgdW5kZXIgdGhlXG4gICAqIHBsdWdpbiBiYXNlIHBhdGgsIGkuZS4gYEdFVCAvYXBpL2VtYWlsL3NlbmRlcnNgLiBSdW5zIGluIHRoZSBPQk9cbiAgICogdXNlciBzY29wZSBzbyBkb21haW4gd2lsZGNhcmRzIHJlc29sdmUgYWdhaW5zdCB0aGUgY2FsbGVyJ3Mgb3duXG4gICAqIGxvY2FsIHBhcnQuXG4gICAqL1xuICBvdmVycmlkZSBpbmplY3RSb3V0ZXMocm91dGVyOiBJQXBwUm91dGVyKTogdm9pZCB7XG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwibGlzdFNlbmRlcnNcIixcbiAgICAgIG1ldGhvZDogXCJnZXRcIixcbiAgICAgIHBhdGg6IFNFTkRFUlNfUk9VVEUsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5hc1VzZXIocmVxKS5leGVjdXRlTGlzdFNlbmRlcnMoKTtcbiAgICAgICAgaWYgKCFyZXN1bHQub2spIHtcbiAgICAgICAgICByZXMuc3RhdHVzKHJlc3VsdC5zdGF0dXMpLmpzb24oeyBlcnJvcjogcmVzdWx0Lm1lc3NhZ2UgfSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHJlcy5qc29uKHJlc3VsdC5kYXRhKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICBvdmVycmlkZSBleHBvcnRzKCkge1xuICAgIHJldHVybiB7XG4gICAgICAvKipcbiAgICAgICAqIFNlbmQgYSBtZXNzYWdlIGltbWVkaWF0ZWx5IGZyb20gYGZyb21gIHRocm91Z2ggdGhlIHNoYXJlZFxuICAgICAgICogdHJhbnNwb3J0LCBieXBhc3NpbmcgdGhlIGFwcHJvdmFsIGZsb3cuIEZvciBhZ2VudC1kcml2ZW4gc2VuZHNcbiAgICAgICAqIHVzZSB7QGxpbmsgZW1haWxUb29sfSBpbnN0ZWFkLlxuICAgICAgICovXG4gICAgICBzZW5kRW1haWw6IChcbiAgICAgICAgbWVzc2FnZTogRW1haWxNZXNzYWdlLFxuICAgICAgICBmcm9tOiBzdHJpbmcsXG4gICAgICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICAgICAgKTogUHJvbWlzZTxFbWFpbFJlc3VsdD4gPT4gdGhpcy5zZW5kKG1lc3NhZ2UsIGZyb20sIHNpZ25hbCksXG4gICAgICAvKipcbiAgICAgICAqIFNlbmRlciBvcHRpb25zIGZvciB0aGUgY3VycmVudCB1c2VyICh0aGUgYEdFVCAvc2VuZGVyc2AgcGF5bG9hZCkuXG4gICAgICAgKiBBcHBLaXQgd3JhcHMgdGhpcyB3aXRoIGBhc1VzZXIocmVxKWAgZm9yIE9CTyBzY29waW5nLlxuICAgICAgICovXG4gICAgICBsaXN0U2VuZGVyczogYXN5bmMgKCk6IFByb21pc2U8RW1haWxTZW5kZXJzPiA9PiB1bndyYXAoYXdhaXQgdGhpcy5leGVjdXRlTGlzdFNlbmRlcnMoKSksXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBBcHBLaXQgYFRvb2xQcm92aWRlcmA6IHRoZSB0b29sIGRlZmluaXRpb25zIG9mZmVyZWQgdG8gYW4gYWdlbnQuICovXG4gIGdldEFnZW50VG9vbHMoKTogQWdlbnRUb29sRGVmaW5pdGlvbltdIHtcbiAgICByZXR1cm4gdG9vbHNGcm9tUmVnaXN0cnkodGhpcy50b29scyk7XG4gIH1cblxuICAvKipcbiAgICogQXBwS2l0IGBUb29sUHJvdmlkZXJgOiBydW4gb25lIHRvb2wgY2FsbC4gQXJndW1lbnRzIGFyZSB2YWxpZGF0ZWQgYWdhaW5zdFxuICAgKiB0aGUgdG9vbCdzIHNjaGVtYSBmaXJzdCwgYW5kIGEgdmFsaWRhdGlvbiBmYWlsdXJlIGNvbWVzIGJhY2sgYXMgYW5cbiAgICogTExNLWZyaWVuZGx5IHN0cmluZyBzbyB0aGUgbW9kZWwgY2FuIGNvcnJlY3QgaXRzZWxmIG9uIHRoZSBuZXh0IHR1cm4uXG4gICAqL1xuICBhc3luYyBleGVjdXRlQWdlbnRUb29sKG5hbWU6IHN0cmluZywgYXJnczogdW5rbm93biwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHVua25vd24+IHtcbiAgICByZXR1cm4gZXhlY3V0ZUZyb21SZWdpc3RyeSh0aGlzLnRvb2xzLCBuYW1lLCBhcmdzLCBzaWduYWwpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbmQgb25lIG1lc3NhZ2UsIHJlc29sdmluZyB0aGUgc2VuZGVyIGZvciB0aGUgY2FsbGVyIGluIHNjb3BlIHdoZW5cbiAgICogYGZyb21gIGlzIG5vdCBwaW5uZWQuIFRoZSBpbnRlcmNlcHRvciBjaGFpbiBpcyBhcHBsaWVkIGluc2lkZVxuICAgKiB7QGxpbmsgc2VuZEVtYWlsfSB0aHJvdWdoIHRoZSBleGVjdXRvciBpbnN0YWxsZWQgYXQgc2V0dXAsIHNvIHRoaXMgbXVzdFxuICAgKiBub3Qgd3JhcCBpdCBhZ2Fpbi5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2VuZChcbiAgICBtZXNzYWdlOiBFbWFpbE1lc3NhZ2UsXG4gICAgZnJvbTogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPEVtYWlsUmVzdWx0PiB7XG4gICAgcmV0dXJuIHNlbmRFbWFpbChtZXNzYWdlLCBmcm9tID8/IHRoaXMucmVzb2x2ZVNlbmRlcigpLCBzaWduYWwpO1xuICB9XG5cbiAgLyoqIFJ1biB0aGUgc2VuZGVyLW9wdGlvbnMgbG9va3VwIHRocm91Z2ggdGhlIHBsdWdpbidzIGludGVyY2VwdG9yIGNoYWluLiAqL1xuICBwcml2YXRlIGFzeW5jIGV4ZWN1dGVMaXN0U2VuZGVycygpOiBQcm9taXNlPEV4ZWN1dGlvblJlc3VsdDxFbWFpbFNlbmRlcnM+PiB7XG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0ZShhc3luYyAoKSA9PiB0aGlzLmxpc3RTZW5kZXJzKCksIEVNQUlMX1NFTkRFUlNfU0VUVElOR1MpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGUgdGhlIGBGcm9tYCBvcHRpb25zIG9mZmVyZWQgdG8gdGhlIGN1cnJlbnQgdXNlcjogdGhlIGNvbmNyZXRlXG4gICAqIGFkZHJlc3NlcyB0aGUgZWZmZWN0aXZlIGFsbG93LWxpc3QgcGVybWl0cyAoZG9tYWluIHdpbGRjYXJkcyBleHBhbmRlZFxuICAgKiBhZ2FpbnN0IHRoZSBPQk8gdXNlcidzIGxvY2FsIHBhcnQpLCB0aGUgZGVmYXVsdCBhbW9uZyB0aGVtLCBhbmRcbiAgICogd2hldGhlciB0aGUgbGlzdCBpcyBhbiBlbmZvcmNlZCByZXN0cmljdGlvbi4gU2VlXG4gICAqIHtAbGluayBsaXN0U2VuZGVyT3B0aW9uc30uXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxpc3RTZW5kZXJzKCk6IFByb21pc2U8RW1haWxTZW5kZXJzPiB7XG4gICAgY29uc3QgeyBjb25maWcgfSA9IGdldEVtYWlsUnVudGltZSgpO1xuICAgIGNvbnN0IHNlbmRlcnMgPSBsaXN0U2VuZGVyT3B0aW9ucyhjb25maWcsIGN1cnJlbnRVc2VyRW1haWwoKSk7XG4gICAgLy8gUHJlZmVyIHRoZSBhZGRyZXNzIGEgc2VuZCB3b3VsZCBhY3R1YWxseSBkZWZhdWx0IHRvOyBmYWxsIGJhY2sgdG9cbiAgICAvLyB0aGUgZmlyc3Qgb2ZmZXJlZCBvcHRpb24gd2hlbiB0aGF0IGNhbid0IGJlIHJlc29sdmVkIC8gcGVybWl0dGVkLlxuICAgIGxldCBkZWZhdWx0U2VuZGVyID0gc2VuZGVyc1swXTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzb2x2ZWQgPSB0aGlzLnJlc29sdmVTZW5kZXIoKS50b0xvd2VyQ2FzZSgpO1xuICAgICAgaWYgKGlzU2VuZGVyQWxsb3dlZChyZXNvbHZlZCwgY29uZmlnLmFsbG93ZWRTZW5kZXJzKSkgZGVmYXVsdFNlbmRlciA9IHJlc29sdmVkO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gS2VlcCB0aGUgZmlyc3Qgb2ZmZXJlZCBvcHRpb24gKG9yIG5vbmUpIGFzIHRoZSBkZWZhdWx0LlxuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgc2VuZGVycyxcbiAgICAgIC4uLihkZWZhdWx0U2VuZGVyID8geyBkZWZhdWx0U2VuZGVyIH0gOiB7fSksXG4gICAgICByZXN0cmljdGVkOiBjb25maWcuYWxsb3dlZFNlbmRlcnMubGVuZ3RoID4gMCxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFRoZSBgRnJvbWAgYSBzZW5kIGRlZmF1bHRzIHRvIGZvciB0aGUgY2FsbGVyIGluIHNjb3BlLiAqL1xuICBwcml2YXRlIHJlc29sdmVTZW5kZXIoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcmVzb2x2ZVNlbmRlckFkZHJlc3MoZ2V0RW1haWxSdW50aW1lKCkuY29uZmlnLCBjdXJyZW50VXNlckVtYWlsKCkpO1xuICB9XG59XG5cbi8qKiBUaGUgT0JPIHVzZXIncyBhZGRyZXNzLCBvciB1bmRlZmluZWQgb3V0c2lkZSBhIHVzZXIgY29udGV4dC4gKi9cbmZ1bmN0aW9uIGN1cnJlbnRVc2VyRW1haWwoKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgY3R4ID0gZ2V0RXhlY3V0aW9uQ29udGV4dCgpO1xuICByZXR1cm4gXCJpc1VzZXJDb250ZXh0XCIgaW4gY3R4ID8gY3R4LnVzZXJFbWFpbCA6IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBSZS1yYWlzZSBhIGZhaWxlZCBleGVjdXRpb24gYXMgdGhlIEFwcEtpdCBlcnJvciBjbGFzcyB0aGF0IGFscmVhZHkgY2Fycmllc1xuICogdGhlIHN0YXR1cyBBcHBLaXQgcmVzb2x2ZWQsIHNvIGEgcHJvZ3JhbW1hdGljIGNhbGxlciBzZWVzIHRoZSBzYW1lIDQwMCAvXG4gKiA0MDEgLyA1MDMgYW4gSFRUUCBjYWxsZXIgd291bGQuXG4gKi9cbmZ1bmN0aW9uIHRvQXBwS2l0RXJyb3Ioc3RhdHVzOiBudW1iZXIsIG1lc3NhZ2U6IHN0cmluZyk6IEFwcEtpdEVycm9yIHtcbiAgaWYgKHN0YXR1cyA9PT0gNDAwKSByZXR1cm4gbmV3IFZhbGlkYXRpb25FcnJvcihtZXNzYWdlKTtcbiAgaWYgKHN0YXR1cyA9PT0gNDAxKSByZXR1cm4gbmV3IEF1dGhlbnRpY2F0aW9uRXJyb3IobWVzc2FnZSk7XG4gIGlmIChzdGF0dXMgPT09IDUwMykgcmV0dXJuIG5ldyBDb25uZWN0aW9uRXJyb3IobWVzc2FnZSk7XG4gIHJldHVybiBuZXcgRXhlY3V0aW9uRXJyb3IobWVzc2FnZSk7XG59XG5cbi8qKlxuICogU3VyZmFjZSBhIGZhaWxlZCB7QGxpbmsgRXhlY3V0aW9uUmVzdWx0fSB0byBhIHByb2dyYW1tYXRpYyBjYWxsZXIgYXMgYVxuICogdGhyb3cuIEhUVFAgaGFuZGxlcnMgbWFwIGBzdGF0dXNgIG9udG8gdGhlIHJlc3BvbnNlIGluc3RlYWQuXG4gKi9cbmZ1bmN0aW9uIHVud3JhcDxUPihyZXN1bHQ6IEV4ZWN1dGlvblJlc3VsdDxUPik6IFQge1xuICBpZiAocmVzdWx0Lm9rKSByZXR1cm4gcmVzdWx0LmRhdGE7XG4gIHRocm93IHRvQXBwS2l0RXJyb3IocmVzdWx0LnN0YXR1cywgcmVzdWx0Lm1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIFJlZ2lzdGVyIHRoZSBlbWFpbCBwbHVnaW4uXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBpbXBvcnQgeyBjcmVhdGVBcHAsIHNlcnZlciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IGJyYW5kLCBwbHVnaW4gYXMgZW1haWxQbHVnaW4gfSBmcm9tIFwiQGRieC10b29scy9lbWFpbFwiO1xuICpcbiAqIGF3YWl0IGNyZWF0ZUFwcCh7XG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICBzZXJ2ZXIoKSxcbiAqICAgICBlbWFpbFBsdWdpbi5lbWFpbCh7XG4gKiAgICAgICBkb21haW46IFwibWFpbC5leGFtcGxlLmNvbVwiLFxuICogICAgICAgYWxsb3dlZFNlbmRlcnM6IFtcIipAbWFpbC5leGFtcGxlLmNvbVwiXSxcbiAqICAgICAgIGJyYW5kOiBicmFuZC5kZWZhdWx0RW1haWxCcmFuZCxcbiAqICAgICB9KSxcbiAqICAgXSxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjb25zdCBlbWFpbCA9IHRvUGx1Z2luKEVtYWlsUGx1Z2luKTtcbiJdfQ==
|
|
426
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFFSCxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsY0FBYyxFQUNkLG1CQUFtQixFQUNuQixNQUFNLEVBQ04sUUFBUSxFQUNSLGVBQWUsR0FLaEIsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsVUFBVSxFQUNWLG1CQUFtQixFQUNuQixpQkFBaUIsR0FJbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0MsT0FBTyxFQUNMLEtBQUssSUFBSSxTQUFTLEdBSW5CLE1BQU0seUJBQXlCLENBQUM7QUFFakMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLGdCQUFnQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGlCQUFpQixFQUEwQixNQUFNLGFBQWEsQ0FBQztBQUM3RixPQUFPLEVBQUUsUUFBUSxFQUFFLGNBQWMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzFELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM5RSxPQUFPLEVBQUUsZUFBZSxFQUFFLGlCQUFpQixFQUFFLG9CQUFvQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3ZGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNuRCxPQUFPLEVBQ0wsZUFBZSxFQUNmLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsZ0JBQWdCLEVBQ2hCLG9CQUFvQixHQUNyQixNQUFNLGdCQUFnQixDQUFDO0FBRXhCLCtFQUErRTtBQUMvRSxNQUFNLGFBQWEsR0FBRyxVQUFVLENBQUM7QUFFakMsNEVB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Ii8qKlxuICogQXBwS2l0IHBsdWdpbiAocmVnaXN0ZXJlZCBuYW1lOiBgZW1haWxgKSB0aGF0IG93bnMgdGhlIFNNVFAgcnVudGltZVxuICogZm9yIG91dGJvdW5kIG1haWwuIFJlZ2lzdGVyaW5nIGl0IHZhbGlkYXRlcyB0aGUgU01UUCBjb25maWd1cmF0aW9uXG4gKiBhbmQgdmVyaWZpZXMgY29ubmVjdGl2aXR5IGF0IHN0YXJ0dXAsIHNvIGEgYmFkIGhvc3QgLyBjcmVkZW50aWFsXG4gKiBzdXJmYWNlcyBpbiB0aGUgYm9vdCBsb2dzIGluc3RlYWQgb2Ygb24gdGhlIGZpcnN0IGFwcHJvdmVkIHNlbmQsIGFuZFxuICogaW5zdGFsbHMgdGhpcyBwbHVnaW4ncyBgZXhlY3V0ZSgpYCBhcyB0aGUgcnVudGltZSdzIGV4ZWN1dG9yIHNvIGV2ZXJ5XG4gKiBzZW5kIHBpY2tzIHVwIEFwcEtpdCdzIHJldHJ5IC8gdGltZW91dCAvIHRlbGVtZXRyeSBjaGFpbi5cbiAqXG4gKiBUaGUgcGx1Z2luIGlzIGFsc28gYSBgVG9vbFByb3ZpZGVyYCwgc28gYW4gQXBwS2l0IGFnZW50IGNhbiByZWFjaCBhblxuICogYGVtYWlsLnNlbmRgIHRvb2wgZGlyZWN0bHk7IHRoZSB7QGxpbmsgZW1haWxUb29sfSBleHBvcnQgaXMgdGhlIHNhbWVcbiAqIGNhcGFiaWxpdHkgZm9yIGEgTWFzdHJhIGFnZW50LiBCb3RoIHNoYXJlIHRoZSB0cmFuc3BvcnQgcHJpbWVkIGhlcmUsXG4gKiBhbmQge0BsaW5rIHNlbmRFbWFpbH0gaXMgYXZhaWxhYmxlIHRvIG5vbi1hZ2VudCBjYWxsZXJzLlxuICpcbiAqIENvbmZpZ3VyYXRpb24gaXMgdGhlIG1hbmlmZXN0LXB1Ymxpc2hlZCB7QGxpbmsgRW1haWxQbHVnaW5Db25maWd9XG4gKiAoU01UUCBob3N0L3BvcnQvY3JlZGVudGlhbHMsIHNlbmRlciBkb21haW4gb3IgZXhwbGljaXQgYGZyb21gLCB0aGVcbiAqIHNlbmRlciBwb2xpY3ksIGFuZCBhbiBvcHRpb25hbCBgYWxsb3dlZFNlbmRlcnNgIHJlc3RyaWN0aW9uKSwgd2l0aFxuICogdW5wcmVmaXhlZCBgU01UUF8qYCAvIGBFTUFJTF8qYCBlbnZpcm9ubWVudCBmYWxsYmFja3MuXG4gKlxuICogVGhlIHBsdWdpbiBtb3VudHMgb25lIHJvdXRlIHVuZGVyIGl0cyBiYXNlIHBhdGggKGAvYXBpL2VtYWlsYCk6XG4gKiBgR0VUIC9zZW5kZXJzYCByZXR1cm5zIHRoZSBwZXJtaXR0ZWQgYEZyb21gIG9wdGlvbnMgZm9yIHRoZSBjYWxsaW5nXG4gKiB1c2VyLCBzbyBhIGNvbXBvc2UgVUkgY2FuIG9mZmVyIHRoZW0gaW4gYSBkcm9wZG93bi5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHtcbiAgQXV0aGVudGljYXRpb25FcnJvcixcbiAgQ29uZmlndXJhdGlvbkVycm9yLFxuICBDb25uZWN0aW9uRXJyb3IsXG4gIEV4ZWN1dGlvbkVycm9yLFxuICBnZXRFeGVjdXRpb25Db250ZXh0LFxuICBQbHVnaW4sXG4gIHRvUGx1Z2luLFxuICBWYWxpZGF0aW9uRXJyb3IsXG4gIHR5cGUgQXBwS2l0RXJyb3IsXG4gIHR5cGUgRXhlY3V0aW9uUmVzdWx0LFxuICB0eXBlIElBcHBSb3V0ZXIsXG4gIHR5cGUgUGx1Z2luTWFuaWZlc3QsXG59IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7XG4gIGRlZmluZVRvb2wsXG4gIGV4ZWN1dGVGcm9tUmVnaXN0cnksXG4gIHRvb2xzRnJvbVJlZ2lzdHJ5LFxuICB0eXBlIEFnZW50VG9vbERlZmluaXRpb24sXG4gIHR5cGUgVG9vbFByb3ZpZGVyLFxuICB0eXBlIFRvb2xSZWdpc3RyeSxcbn0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdC9iZXRhXCI7XG5pbXBvcnQgeyBsb2cgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHtcbiAgZW1haWwgYXMgZW1haWxXaXJlLFxuICB0eXBlIEVtYWlsTWVzc2FnZSxcbiAgdHlwZSBFbWFpbFJlc3VsdCxcbiAgdHlwZSBFbWFpbFNlbmRlcnMsXG59IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1lbWFpbFwiO1xuaW1wb3J0IHR5cGUgZXhwcmVzcyBmcm9tIFwiZXhwcmVzc1wiO1xuaW1wb3J0IHsgYXV0aFJlcXVlc3RTY2hlbWEsIGF1dGhWZXJpZnlTY2hlbWEgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtZW1haWxcIjtcbmltcG9ydCB7IEVNQUlMX0NPTkZJR19TQ0hFTUEsIHJlc29sdmVBdXRoQ29uZmlnLCB0eXBlIEVtYWlsUGx1Z2luQ29uZmlnIH0gZnJvbSBcIi4vY29uZmlnLnRzXCI7XG5pbXBvcnQgeyBBdXRoR2F0ZSwgU0VTU0lPTl9DT09LSUUgfSBmcm9tIFwiLi9hdXRoL2dhdGUudHNcIjtcbmltcG9ydCB7IEVNQUlMX1NFTkRFUlNfU0VUVElOR1MsIEVNQUlMX1ZFUklGWV9TRVRUSU5HUyB9IGZyb20gXCIuL2RlZmF1bHRzLnRzXCI7XG5pbXBvcnQgeyBpc1NlbmRlckFsbG93ZWQsIGxpc3RTZW5kZXJPcHRpb25zLCByZXNvbHZlU2VuZGVyQWRkcmVzcyB9IGZyb20gXCIuL3NlbmRlci50c1wiO1xuaW1wb3J0IHsgU0VORF9FTUFJTF9ERVNDUklQVElPTiB9IGZyb20gXCIuL3Rvb2wudHNcIjtcbmltcG9ydCB7XG4gIGdldEVtYWlsUnVudGltZSxcbiAgcmVzZXRFbWFpbFJ1bnRpbWUsXG4gIHNlbmRFbWFpbCxcbiAgc2V0RW1haWxFeGVjdXRvcixcbiAgdmVyaWZ5RW1haWxUcmFuc3BvcnQsXG59IGZyb20gXCIuL3RyYW5zcG9ydC50c1wiO1xuXG4vKiogTW91bnQtcmVsYXRpdmUgcm91dGUgKHVuZGVyIGAvYXBpL2VtYWlsYCkgZm9yIHRoZSBzZW5kZXItb3B0aW9ucyBsb29rdXAuICovXG5jb25zdCBTRU5ERVJTX1JPVVRFID0gXCIvc2VuZGVyc1wiO1xuXG4vKiogUmVnaXN0cnkga2V5IG9mIHRoZSBhZ2VudCB0b29sLCB3aGljaCBhZ2VudHMgYWRkcmVzcyBhcyBgZW1haWwuc2VuZGAuICovXG5jb25zdCBTRU5EX1RPT0wgPSBcInNlbmRcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImVtYWlsXCIpO1xuXG4vKipcbiAqIEFwcEtpdCBwbHVnaW4gdGhhdCBjb25maWd1cmVzIGFuZCB2ZXJpZmllcyB0aGUgU01UUCB0cmFuc3BvcnQgdXNlZCBieVxuICogdGhlIGBzZW5kX2VtYWlsYCB0b29sLCBhbmQgZXhwb3NlcyBzZW5kaW5nIGFzIGFuIEFwcEtpdCBhZ2VudCB0b29sLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c1xuICogaW1wb3J0IHsgY3JlYXRlQXBwLCBzZXJ2ZXIgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG4gKiBpbXBvcnQgeyBwbHVnaW4gYXMgZW1haWxQbHVnaW4gfSBmcm9tIFwiQGRieC10b29scy9lbWFpbFwiO1xuICpcbiAqIGF3YWl0IGNyZWF0ZUFwcCh7XG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICBzZXJ2ZXIoKSxcbiAqICAgICBlbWFpbFBsdWdpbi5lbWFpbCh7XG4gKiAgICAgICBzbXRwOiB7IGhvc3Q6IFwic210cC5leGFtcGxlLmNvbVwiLCB1c2VyOiBcImFwaWtleVwiLCBwYXNzd29yZDogcHJvY2Vzcy5lbnYuU01UUF9LRVkgfSxcbiAqICAgICAgIGRvbWFpbjogXCJtYWlsLmV4YW1wbGUuY29tXCIsXG4gKiAgICAgfSksXG4gKiAgIF0sXG4gKiB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgRW1haWxQbHVnaW4gZXh0ZW5kcyBQbHVnaW48RW1haWxQbHVnaW5Db25maWc+IGltcGxlbWVudHMgVG9vbFByb3ZpZGVyIHtcbiAgLyoqIFRoZSBlbWFpbC1PVFAgYWNjZXNzIGdhdGUsIGNvbnN0cnVjdGVkIGluIHtAbGluayBzZXR1cH0gd2hlbiBgYXV0aC5lbmFibGVkYC4gKi9cbiAgcHJpdmF0ZSBhdXRoR2F0ZT86IEF1dGhHYXRlO1xuXG4gIHN0YXRpYyBtYW5pZmVzdCA9IHtcbiAgICBuYW1lOiBcImVtYWlsXCIsXG4gICAgZGlzcGxheU5hbWU6IFwiRW1haWxcIixcbiAgICBkZXNjcmlwdGlvbjpcbiAgICAgIFwiU2VuZHMgYXBwcm92YWwtZ2F0ZWQgZW1haWwgb3ZlciBTTVRQLCB3aXRoIHRoZSBzZW5kZXIgZGVyaXZlZCBmcm9tIFwiICtcbiAgICAgIFwidGhlIG9uLWJlaGFsZi1vZiB1c2VyJ3MgYWRkcmVzcyBvbiBhIGNvbmZpZ3VyZWQgZG9tYWluLlwiLFxuICAgIHN0YWJpbGl0eTogXCJiZXRhXCIsXG4gICAgcmVzb3VyY2VzOiB7XG4gICAgICByZXF1aXJlZDogW10sXG4gICAgICBvcHRpb25hbDogW10sXG4gICAgfSxcbiAgICBjb25maWc6IHsgc2NoZW1hOiBFTUFJTF9DT05GSUdfU0NIRU1BIH0sXG4gIH0gc2F0aXNmaWVzIFBsdWdpbk1hbmlmZXN0PFwiZW1haWxcIj47XG5cbiAgLyoqXG4gICAqIFRoZSB0b29sIHRoaXMgcGx1Z2luIG9mZmVycyB0byBhbiBBcHBLaXQgYWdlbnQuXG4gICAqXG4gICAqIE5vdCBgYXV0b0luaGVyaXRhYmxlYDogYSBzZW5kIGlzIGlycmV2ZXJzaWJsZSBhbmQgbGVhdmVzIHRoZSB3b3Jrc3BhY2UsXG4gICAqIHNvIGl0IG11c3Qgb25seSBhcHBlYXIgaW4gYW4gYWdlbnQgdGhhdCBhc2tlZCBmb3IgaXQgYW5kIGFjY2VwdGVkIHRoZVxuICAgKiBzZW5kZXIgcG9saWN5IHRoYXQgY29tZXMgd2l0aCBpdC5cbiAgICpcbiAgICogYGV4ZWN1dGVgIHJlLXBhcnNlcyBpdHMgYXJndW1lbnRzIHdpdGggdGhlIGxvY2FsIHNjaGVtYTogQXBwS2l0IHZhbGlkYXRlc1xuICAgKiBhZ2FpbnN0IHRoZSBzYW1lIHNjaGVtYSBmaXJzdCwgYnV0IHJlLXBhcnNpbmcgaXMgd2hhdCBnaXZlcyB0aGUgYm9keSB0eXBlZFxuICAgKiBhcmd1bWVudHMgaW5zdGVhZCBvZiBgdW5rbm93bmAuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHRvb2xzOiBUb29sUmVnaXN0cnkgPSB7XG4gICAgW1NFTkRfVE9PTF06IGRlZmluZVRvb2woe1xuICAgICAgZGVzY3JpcHRpb246IFNFTkRfRU1BSUxfREVTQ1JJUFRJT04sXG4gICAgICBzY2hlbWE6IGVtYWlsV2lyZS5lbWFpbE1lc3NhZ2VTY2hlbWEsXG4gICAgICBhbm5vdGF0aW9uczogeyBlZmZlY3Q6IFwid3JpdGVcIiwgcmVxdWlyZXNVc2VyQ29udGV4dDogdHJ1ZSB9LFxuICAgICAgYXV0b0luaGVyaXRhYmxlOiBmYWxzZSxcbiAgICAgIGV4ZWN1dGU6IGFzeW5jIChhcmdzLCBzaWduYWwpID0+XG4gICAgICAgIHRoaXMuc2VuZChlbWFpbFdpcmUuZW1haWxNZXNzYWdlU2NoZW1hLnBhcnNlKGFyZ3MpLCB1bmRlZmluZWQsIHNpZ25hbCksXG4gICAgfSksXG4gIH07XG5cbiAgLyoqXG4gICAqIFByaW1lIHRoZSBzaGFyZWQgcnVudGltZSBmcm9tIHRoaXMgcGx1Z2luJ3MgY29uZmlnIChvdmVyIGVudiksIHJvdXRlIHRoZVxuICAgKiB0b29scycgc2VuZHMgdGhyb3VnaCB0aGlzIHBsdWdpbidzIGludGVyY2VwdG9yIGNoYWluLCBhbmQgbG9nIHRoZVxuICAgKiBlZmZlY3RpdmUgc2VuZGVyIHBvbGljeSBzbyBhbiBhY3RpdmUgcmVzdHJpY3Rpb24gaXMgb2J2aW91cyBhdCBib290LiBJblxuICAgKiBTTVRQIG1vZGUsIGZhaWwgc2V0dXAgd2hlbiB0aGUgdHJhbnNwb3J0IGNhbm5vdCBiZSB2ZXJpZmllZDogYSBiYWQgaG9zdFxuICAgKiBvciBjcmVkZW50aWFsIGlzIGEgZGVwbG95LXRpbWUgbWlzdGFrZSBhbmQgc2hvdWxkIHN0b3AgdGhlIGFwcCByYXRoZXJcbiAgICogdGhhbiB3YWl0IGZvciBhIHVzZXIgdG8gYXBwcm92ZSBhIHNlbmQgdGhhdCBjYW5ub3Qgd29yay4gV2l0aCBubyBTTVRQXG4gICAqIGNyZWRlbnRpYWxzIHRoZSBydW50aW1lIGlzIGluIGZpbGUvb3V0Ym94IG1vZGUgKG9ubHkgd2hlblxuICAgKiBgRU1BSUxfT1VUQk9YX01PREVgIGlzIHNldCksIGxvZ2dlZCBsb3VkbHkgaGVyZSBzbyBpdCBpcyBvYnZpb3VzIG1haWxcbiAgICogaXMgYmVpbmcgd3JpdHRlbiB0byBkaXNrIHJhdGhlciB0aGFuIHNlbnQuXG4gICAqL1xuICBvdmVycmlkZSBhc3luYyBzZXR1cCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IHRyYW5zcG9ydGVyLCBjb25maWcgfSA9IGdldEVtYWlsUnVudGltZSh0aGlzLmNvbmZpZyk7XG4gICAgc2V0RW1haWxFeGVjdXRvcigoZm4sIHNldHRpbmdzKSA9PiB0aGlzLmV4ZWN1dGUoZm4sIHNldHRpbmdzKSk7XG4gICAgdGhpcy5zZXR1cEF1dGhHYXRlKCk7XG4gICAgY29uc3QgcG9saWN5ID0ge1xuICAgICAgbW9kZTogY29uZmlnLm1vZGUsXG4gICAgICBzZW5kZXJQb2xpY3k6IGNvbmZpZy5zZW5kZXJQb2xpY3ksXG4gICAgICByZXN0cmljdGVkOiBjb25maWcuYWxsb3dlZFNlbmRlcnMubGVuZ3RoID4gMCxcbiAgICAgIC4uLihjb25maWcuYWxsb3dlZFNlbmRlcnMubGVuZ3RoID4gMCA/IHsgYWxsb3dlZFNlbmRlcnM6IGNvbmZpZy5hbGxvd2VkU2VuZGVycyB9IDoge30pLFxuICAgIH07XG4gICAgaWYgKGNvbmZpZy5tb2RlID09PSBcImZpbGVcIikge1xuICAgICAgbG9nZ2VyLndhcm4oXCJvdXRib3g6ZW5hYmxlZFwiLCB7XG4gICAgICAgIGRpcjogY29uZmlnLm91dERpcixcbiAgICAgICAgcmVhc29uOiBcIm5vIFNNVFAgY3JlZGVudGlhbHMgY29uZmlndXJlZDsgZW1haWxzIGFyZSB3cml0dGVuIHRvIGRpc2sgaW5zdGVhZCBvZiBzZW50XCIsXG4gICAgICB9KTtcbiAgICAgIGxvZ2dlci5pbmZvKFwicmVhZHlcIiwgcG9saWN5KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdmVyaWZpZWQgPSBhd2FpdCB0aGlzLmV4ZWN1dGUoXG4gICAgICBhc3luYyAoc2lnbmFsKSA9PiB2ZXJpZnlFbWFpbFRyYW5zcG9ydCh0cmFuc3BvcnRlciwgc2lnbmFsKSxcbiAgICAgIEVNQUlMX1ZFUklGWV9TRVRUSU5HUyxcbiAgICApO1xuICAgIGlmICghdmVyaWZpZWQub2spIHtcbiAgICAgIGxvZ2dlci5lcnJvcihcInNtdHA6dW52ZXJpZmllZFwiLCB7XG4gICAgICAgIGhvc3Q6IGNvbmZpZy5ob3N0LFxuICAgICAgICBwb3J0OiBjb25maWcucG9ydCxcbiAgICAgICAgc3RhdHVzOiB2ZXJpZmllZC5zdGF0dXMsXG4gICAgICAgIGVycm9yOiB2ZXJpZmllZC5tZXNzYWdlLFxuICAgICAgfSk7XG4gICAgICB0aHJvdyBDb25maWd1cmF0aW9uRXJyb3IuaW52YWxpZENvbm5lY3Rpb24oXG4gICAgICAgIFwiU01UUFwiLFxuICAgICAgICBgQ291bGQgbm90IHZlcmlmeSAke2NvbmZpZy5ob3N0fToke2NvbmZpZy5wb3J0fS4gQ2hlY2sgU01UUF9IT1NULCBTTVRQX1BPUlQsIFNNVFBfU0VDVVJFLCBhbmQgdGhlIGNyZWRlbnRpYWxzLCBvciBzZXQgRU1BSUxfT1VUQk9YX01PREU9MSBmb3IgbG9jYWwgb3V0Ym94IHRlc3RpbmcuYCxcbiAgICAgICk7XG4gICAgfVxuICAgIGxvZ2dlci5pbmZvKFwicmVhZHlcIiwge1xuICAgICAgLi4ucG9saWN5LFxuICAgICAgaG9zdDogY29uZmlnLmhvc3QsXG4gICAgICBwb3J0OiBjb25maWcucG9ydCxcbiAgICAgIHNlY3VyZTogY29uZmlnLnNlY3VyZSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBDbG9zZSB0aGUgU01UUCBjb25uZWN0aW9uIHBvb2wuIElkZW1wb3RlbnQuICovXG4gIGFzeW5jIHNodXRkb3duKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJlc2V0RW1haWxSdW50aW1lKCk7XG4gIH1cblxuICAvKipcbiAgICogQWJvcnQgaW4tZmxpZ2h0IHdvcmsuIEFwcEtpdCdzIGdyYWNlZnVsIHNodXRkb3duIG9ubHkgaW52b2tlcyB0aGlzIGhvb2sgLVxuICAgKiBpdCBuZXZlciBjYWxscyB7QGxpbmsgc2h1dGRvd259IC0gc28gdGhlIFNNVFAgcG9vbCBpcyBjbG9zZWQgZnJvbSBoZXJlIG9yXG4gICAqIGl0IGxlYWtzIGF0IFNJR1RFUk0uIFRoZSB0ZWFyZG93biBpcyBzeW5jaHJvbm91cyBhbmQgaWRlbXBvdGVudCwgc28gdGhlXG4gICAqIHVuLWF3YWl0ZWQgY2FsbCBjb3N0cyBub3RoaW5nLlxuICAgKi9cbiAgb3ZlcnJpZGUgYWJvcnRBY3RpdmVPcGVyYXRpb25zKCk6IHZvaWQge1xuICAgIHN1cGVyLmFib3J0QWN0aXZlT3BlcmF0aW9ucygpO1xuICAgIHZvaWQgdGhpcy5zaHV0ZG93bigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cG9zZSB0aGUgc2VuZGVyLW9wdGlvbnMgbG9va3VwIHNvIFVJIGNvbXBvc2Ugdmlld3MgY2FuIHBvcHVsYXRlIGFcbiAgICogYEZyb21gIGRyb3Bkb3duIGZyb20gdGhlIGNvbmZpZ3VyZWQgYWxsb3ctbGlzdC4gTW91bnRlZCB1bmRlciB0aGVcbiAgICogcGx1Z2luIGJhc2UgcGF0aCwgaS5lLiBgR0VUIC9hcGkvZW1haWwvc2VuZGVyc2AuIFJ1bnMgaW4gdGhlIE9CT1xuICAgKiB1c2VyIHNjb3BlIHNvIGRvbWFpbiB3aWxkY2FyZHMgcmVzb2x2ZSBhZ2FpbnN0IHRoZSBjYWxsZXIncyBvd25cbiAgICogbG9jYWwgcGFydC5cbiAgICovXG4gIG92ZXJyaWRlIGluamVjdFJvdXRlcyhyb3V0ZXI6IElBcHBSb3V0ZXIpOiB2b2lkIHtcbiAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgbmFtZTogXCJsaXN0U2VuZGVyc1wiLFxuICAgICAgbWV0aG9kOiBcImdldFwiLFxuICAgICAgcGF0aDogU0VOREVSU19ST1VURSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXEsIHJlcykgPT4ge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmFzVXNlcihyZXEpLmV4ZWN1dGVMaXN0U2VuZGVycygpO1xuICAgICAgICBpZiAoIXJlc3VsdC5vaykge1xuICAgICAgICAgIHJlcy5zdGF0dXMocmVzdWx0LnN0YXR1cykuanNvbih7IGVycm9yOiByZXN1bHQubWVzc2FnZSB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgcmVzLmpzb24ocmVzdWx0LmRhdGEpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgICB0aGlzLmluamVjdEF1dGhSb3V0ZXMocm91dGVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb3VudCB0aGUgZW1haWwtT1RQIGxvZ2luIGZsb3cgdW5kZXIgYC9hcGkvZW1haWwvYXV0aC8qYCB3aGVuIHRoZSBnYXRlIGlzXG4gICAqIGVuYWJsZWQuIFRoZXNlIHJvdXRlcyBhcmUgdGhlIE9OTFkgb25lcyB0aGUgZ2F0ZSBtaWRkbGV3YXJlIGxlYXZlcyBvcGVuIChzZWVcbiAgICoge0BsaW5rIEF1dGhHYXRlLm1pZGRsZXdhcmV9KTsgZXZlcnl0aGluZyBlbHNlIHJlcXVpcmVzIHRoZSBzZXNzaW9uIGNvb2tpZVxuICAgKiB0aGV5IGVzdGFibGlzaC4gTm8tb3Agd2hlbiBhdXRoIGlzIG9mZi5cbiAgICovXG4gIHByaXZhdGUgaW5qZWN0QXV0aFJvdXRlcyhyb3V0ZXI6IElBcHBSb3V0ZXIpOiB2b2lkIHtcbiAgICBjb25zdCBnYXRlID0gdGhpcy5hdXRoR2F0ZTtcbiAgICBpZiAoIWdhdGUpIHJldHVybjtcblxuICAgIC8vIGByZXF1ZXN0YCBhbmQgYHZlcmlmeWAgdGFrZSBhIHJhdyBlbWFpbC9jb2RlLCBub3QgYW4gT0JPIHVzZXIsIHNvIHRoZXkgYXJlXG4gICAgLy8gTk9UIHdyYXBwZWQgaW4gYGFzVXNlcmAgLSB0aGUgY2FsbGVyIGlzIGFub255bW91cyB1bnRpbCB2ZXJpZmllZC5cbiAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgbmFtZTogXCJhdXRoUmVxdWVzdFwiLFxuICAgICAgbWV0aG9kOiBcInBvc3RcIixcbiAgICAgIHBhdGg6IFwiL2F1dGgvcmVxdWVzdFwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgIGNvbnN0IHBhcnNlZCA9IGF1dGhSZXF1ZXN0U2NoZW1hLnNhZmVQYXJzZShyZXEuYm9keSk7XG4gICAgICAgIGlmICghcGFyc2VkLnN1Y2Nlc3MpIHtcbiAgICAgICAgICAvLyBFdmVuIGEgbWFsZm9ybWVkIGJvZHkgcmVwb3J0cyBzdWNjZXNzIChhbnRpLWVudW1lcmF0aW9uKS5cbiAgICAgICAgICByZXMuanNvbih7IG9rOiB0cnVlIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBnYXRlLmhhbmRsZVJlcXVlc3QocGFyc2VkLmRhdGEuZW1haWwsIHRoaXMuY2xpZW50SXAocmVxKSk7XG4gICAgICAgIHJlcy5qc29uKHJlc3VsdCk7XG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwiYXV0aFZlcmlmeVwiLFxuICAgICAgbWV0aG9kOiBcInBvc3RcIixcbiAgICAgIHBhdGg6IFwiL2F1dGgvdmVyaWZ5XCIsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgICAgY29uc3QgcGFyc2VkID0gYXV0aFZlcmlmeVNjaGVtYS5zYWZlUGFyc2UocmVxLmJvZHkpO1xuICAgICAgICBpZiAoIXBhcnNlZC5zdWNjZXNzKSB7XG4gICAgICAgICAgcmVzLmpzb24oeyBvazogZmFsc2UgfSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGdhdGUuaGFuZGxlVmVyaWZ5KFxuICAgICAgICAgIHBhcnNlZC5kYXRhLmVtYWlsLFxuICAgICAgICAgIHBhcnNlZC5kYXRhLmNvZGUsXG4gICAgICAgICAgdGhpcy5jbGllbnRJcChyZXEpLFxuICAgICAgICAgIHJlcS5zZWN1cmUsXG4gICAgICAgICk7XG4gICAgICAgIGlmIChyZXN1bHQub2sgJiYgcmVzdWx0LnRva2VuICYmIHJlc3VsdC5jb29raWVPcHRpb25zKSB7XG4gICAgICAgICAgcmVzLmNvb2tpZShTRVNTSU9OX0NPT0tJRSwgcmVzdWx0LnRva2VuLCByZXN1bHQuY29va2llT3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICAgICAgcmVzLmpzb24oeyBvazogcmVzdWx0Lm9rLCAuLi4ocmVzdWx0LnJldHJ5QWZ0ZXIgPyB7IHJldHJ5QWZ0ZXI6IHJlc3VsdC5yZXRyeUFmdGVyIH0gOiB7fSkgfSk7XG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwiYXV0aExvZ291dFwiLFxuICAgICAgbWV0aG9kOiBcInBvc3RcIixcbiAgICAgIHBhdGg6IFwiL2F1dGgvbG9nb3V0XCIsXG4gICAgICBoYW5kbGVyOiBhc3luYyAoX3JlcSwgcmVzKSA9PiB7XG4gICAgICAgIHJlcy5jbGVhckNvb2tpZShTRVNTSU9OX0NPT0tJRSwgeyBwYXRoOiBcIi9cIiB9KTtcbiAgICAgICAgcmVzLmpzb24oeyBvazogdHJ1ZSB9KTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgbmFtZTogXCJhdXRoU3RhdHVzXCIsXG4gICAgICBtZXRob2Q6IFwiZ2V0XCIsXG4gICAgICBwYXRoOiBcIi9hdXRoL3N0YXR1c1wiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgIHJlcy5qc29uKGF3YWl0IGdhdGUuc3RhdHVzKHJlcSkpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBDbGllbnQgSVAgZm9yIHJhdGUtbGltaXRpbmcsIGhvbm9yaW5nIHRoZSBBcHBzIGluZ3Jlc3MgYHgtZm9yd2FyZGVkLWZvcmAuICovXG4gIHByaXZhdGUgY2xpZW50SXAocmVxOiBleHByZXNzLlJlcXVlc3QpOiBzdHJpbmcge1xuICAgIGNvbnN0IGZ3ZCA9IHJlcS5oZWFkZXJzW1wieC1mb3J3YXJkZWQtZm9yXCJdO1xuICAgIGNvbnN0IGZpcnN0ID0gQXJyYXkuaXNBcnJheShmd2QpID8gZndkWzBdIDogZndkPy5zcGxpdChcIixcIilbMF07XG4gICAgcmV0dXJuIChmaXJzdCA/PyByZXEuaXAgPz8gXCJ1bmtub3duXCIpLnRyaW0oKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGV4cG9ydHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIC8qKlxuICAgICAgICogU2VuZCBhIG1lc3NhZ2UgaW1tZWRpYXRlbHkgZnJvbSBgZnJvbWAgdGhyb3VnaCB0aGUgc2hhcmVkXG4gICAgICAgKiB0cmFuc3BvcnQsIGJ5cGFzc2luZyB0aGUgYXBwcm92YWwgZmxvdy4gRm9yIGFnZW50LWRyaXZlbiBzZW5kc1xuICAgICAgICogdXNlIHtAbGluayBlbWFpbFRvb2x9IGluc3RlYWQuXG4gICAgICAgKi9cbiAgICAgIHNlbmRFbWFpbDogKFxuICAgICAgICBtZXNzYWdlOiBFbWFpbE1lc3NhZ2UsXG4gICAgICAgIGZyb206IHN0cmluZyxcbiAgICAgICAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4gICAgICApOiBQcm9taXNlPEVtYWlsUmVzdWx0PiA9PiB0aGlzLnNlbmQobWVzc2FnZSwgZnJvbSwgc2lnbmFsKSxcbiAgICAgIC8qKlxuICAgICAgICogU2VuZGVyIG9wdGlvbnMgZm9yIHRoZSBjdXJyZW50IHVzZXIgKHRoZSBgR0VUIC9zZW5kZXJzYCBwYXlsb2FkKS5cbiAgICAgICAqIEFwcEtpdCB3cmFwcyB0aGlzIHdpdGggYGFzVXNlcihyZXEpYCBmb3IgT0JPIHNjb3BpbmcuXG4gICAgICAgKi9cbiAgICAgIGxpc3RTZW5kZXJzOiBhc3luYyAoKTogUHJvbWlzZTxFbWFpbFNlbmRlcnM+ID0+IHVud3JhcChhd2FpdCB0aGlzLmV4ZWN1dGVMaXN0U2VuZGVycygpKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIEFwcEtpdCBgVG9vbFByb3ZpZGVyYDogdGhlIHRvb2wgZGVmaW5pdGlvbnMgb2ZmZXJlZCB0byBhbiBhZ2VudC4gKi9cbiAgZ2V0QWdlbnRUb29scygpOiBBZ2VudFRvb2xEZWZpbml0aW9uW10ge1xuICAgIHJldHVybiB0b29sc0Zyb21SZWdpc3RyeSh0aGlzLnRvb2xzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBLaXQgYFRvb2xQcm92aWRlcmA6IHJ1biBvbmUgdG9vbCBjYWxsLiBBcmd1bWVudHMgYXJlIHZhbGlkYXRlZCBhZ2FpbnN0XG4gICAqIHRoZSB0b29sJ3Mgc2NoZW1hIGZpcnN0LCBhbmQgYSB2YWxpZGF0aW9uIGZhaWx1cmUgY29tZXMgYmFjayBhcyBhblxuICAgKiBMTE0tZnJpZW5kbHkgc3RyaW5nIHNvIHRoZSBtb2RlbCBjYW4gY29ycmVjdCBpdHNlbGYgb24gdGhlIG5leHQgdHVybi5cbiAgICovXG4gIGFzeW5jIGV4ZWN1dGVBZ2VudFRvb2wobmFtZTogc3RyaW5nLCBhcmdzOiB1bmtub3duLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIHJldHVybiBleGVjdXRlRnJvbVJlZ2lzdHJ5KHRoaXMudG9vbHMsIG5hbWUsIGFyZ3MsIHNpZ25hbCk7XG4gIH1cblxuICAvKipcbiAgICogU2VuZCBvbmUgbWVzc2FnZSwgcmVzb2x2aW5nIHRoZSBzZW5kZXIgZm9yIHRoZSBjYWxsZXIgaW4gc2NvcGUgd2hlblxuICAgKiBgZnJvbWAgaXMgbm90IHBpbm5lZC4gVGhlIGludGVyY2VwdG9yIGNoYWluIGlzIGFwcGxpZWQgaW5zaWRlXG4gICAqIHtAbGluayBzZW5kRW1haWx9IHRocm91Z2ggdGhlIGV4ZWN1dG9yIGluc3RhbGxlZCBhdCBzZXR1cCwgc28gdGhpcyBtdXN0XG4gICAqIG5vdCB3cmFwIGl0IGFnYWluLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZW5kKFxuICAgIG1lc3NhZ2U6IEVtYWlsTWVzc2FnZSxcbiAgICBmcm9tOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gICAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4gICk6IFByb21pc2U8RW1haWxSZXN1bHQ+IHtcbiAgICByZXR1cm4gc2VuZEVtYWlsKG1lc3NhZ2UsIGZyb20gPz8gdGhpcy5yZXNvbHZlU2VuZGVyKCksIHNpZ25hbCk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGQgdGhlIGVtYWlsLU9UUCBhY2Nlc3MgZ2F0ZSB3aGVuIGBhdXRoLmVuYWJsZWRgLCB3aXJpbmcgaXRzIGNvZGVcbiAgICogZGVsaXZlcnkgdGhyb3VnaCB0aGlzIHBsdWdpbidzIG93biB0cmFuc3BvcnQuIENhbGxlZCBmcm9tIHtAbGluayBzZXR1cH07IGFcbiAgICogbm8tb3AgKGxlYXZlcyB7QGxpbmsgYXV0aEdhdGV9IHVuZGVmaW5lZCkgd2hlbiB0aGUgZ2F0ZSBpcyBvZmYuXG4gICAqL1xuICBwcml2YXRlIHNldHVwQXV0aEdhdGUoKTogdm9pZCB7XG4gICAgY29uc3QgYXV0aCA9IHJlc29sdmVBdXRoQ29uZmlnKHRoaXMuY29uZmlnLmF1dGgpO1xuICAgIGlmICghYXV0aCkgcmV0dXJuO1xuICAgIGNvbnN0IGdhdGUgPSBuZXcgQXV0aEdhdGUoe1xuICAgICAgYWxsb3c6IGF1dGguYWxsb3csXG4gICAgICBzZXNzaW9uVHRsU2Vjb25kczogYXV0aC5zZXNzaW9uVHRsU2Vjb25kcyxcbiAgICAgIGNvZGVUdGxTZWNvbmRzOiBhdXRoLmNvZGVUdGxTZWNvbmRzLFxuICAgICAgbWF4QXR0ZW1wdHM6IGF1dGgubWF4QXR0ZW1wdHMsXG4gICAgICBzZWN1cmVDb29raWVzOiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gXCJwcm9kdWN0aW9uXCIsXG4gICAgICBzZW5kQ29kZTogKGVtYWlsLCBjb2RlKSA9PiB0aGlzLnNlbmRPdHBFbWFpbChlbWFpbCwgY29kZSksXG4gICAgfSk7XG4gICAgdGhpcy5hdXRoR2F0ZSA9IGdhdGU7XG5cbiAgICAvLyBUaGUgZ2F0ZSBtdXN0IHByb3RlY3QgdGhlIFdIT0xFIGFwcCwgYnV0IGEgcGx1Z2luJ3Mgb3duIGBpbmplY3RSb3V0ZXNgXG4gICAgLy8gcm91dGVyIG9ubHkgY292ZXJzIGAvYXBpL2VtYWlsLypgLiBBcHBLaXQncyBgc2VydmVyYCBwbHVnaW4gZXhwb3Nlc1xuICAgIC8vIGBhZGRFeHRlbnNpb24oZm4pYCBmb3IgZXhhY3RseSB0aGlzOiBhbiBhcHAtbGV2ZWwgbWlkZGxld2FyZSByZWdpc3RlcmVkXG4gICAgLy8gYmVmb3JlIHRoZSBzZXJ2ZXIgbGlzdGVucy4gTG9vayBpdCB1cCBieSBpdHMgcmVnaXN0ZXJlZCBuYW1lIChub1xuICAgIC8vIGRlcGVuZGVuY3kgb24gYSBzcGVjaWZpYyBzZXJ2ZXItcGx1Z2luIHBhY2thZ2UpIGFuZCBtb3VudCB0aGUgZ2F0ZSB0aGVyZS5cbiAgICAvLyBUaGUgbG9naW4gcm91dGVzIHRoZW1zZWx2ZXMgYXJlIGV4ZW1wdGVkIGJ5IHRoZSBtaWRkbGV3YXJlJ3MgYGVtYWlsQmFzZWBcbiAgICAvLyBvcGVuLXByZWZpeCAoYC9hcGkvZW1haWwvYXV0aGApLlxuICAgIGNvbnN0IHNlcnZlciA9IHRoaXMuY29udGV4dD8uZ2V0UGx1Z2lucygpLmdldChcInNlcnZlclwiKSBhc1xuICAgICAgfCB7IGFkZEV4dGVuc2lvbj86IChmbjogKGFwcDogZXhwcmVzcy5BcHBsaWNhdGlvbikgPT4gdm9pZCkgPT4gdm9pZCB9XG4gICAgICB8IHVuZGVmaW5lZDtcbiAgICBpZiAoc2VydmVyPy5hZGRFeHRlbnNpb24pIHtcbiAgICAgIHNlcnZlci5hZGRFeHRlbnNpb24oKGFwcCkgPT4gYXBwLnVzZShnYXRlLm1pZGRsZXdhcmUoYC9hcGkvJHtFbWFpbFBsdWdpbi5tYW5pZmVzdC5uYW1lfWApKSk7XG4gICAgICBsb2dnZXIuaW5mbyhcImF1dGg6ZW5hYmxlZFwiLCB7XG4gICAgICAgIHBhdHRlcm5zOiBhdXRoLmFsbG93Lmxlbmd0aCxcbiAgICAgICAgc2Vzc2lvblR0bFNlY29uZHM6IGF1dGguc2Vzc2lvblR0bFNlY29uZHMsXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTm8gc2VydmVyIHBsdWdpbiB0byBnYXRlIHRocm91Z2ggLSB0aGUgbG9naW4gcm91dGVzIHN0aWxsIHdvcmssIGJ1dCB0aGVcbiAgICAgIC8vIHJlc3Qgb2YgdGhlIGFwcCB3b3VsZCBiZSB1bmdhdGVkLCBzbyByZWZ1c2UgdG8gaGFsZi1lbmFibGUgc2lsZW50bHkuXG4gICAgICB0aGlzLmF1dGhHYXRlID0gdW5kZWZpbmVkO1xuICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgIFwiYXV0aDpkaXNhYmxlZCAtIHRoZSBgc2VydmVyYCBwbHVnaW4gaXMgcmVxdWlyZWQgdG8gZ2F0ZSB0aGUgYXBwOyBhZGQgc2VydmVyKCkgdG8geW91ciBwbHVnaW5zXCIsXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWxpdmVyIGEgb25lLXRpbWUgY29kZSB0byBgZW1haWxgIHRocm91Z2ggdGhpcyBwbHVnaW4ncyB0cmFuc3BvcnQuIFRocm93c1xuICAgKiBvbiBmYWlsdXJlICh0aGUgZ2F0ZSBzd2FsbG93cyBpdCwgc28gYSBkZWxpdmVyeSBlcnJvciBuZXZlciBsZWFrcyB3aGV0aGVyXG4gICAqIHRoZSBhZGRyZXNzIHdhcyBhbGxvdy1saXN0ZWQpLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZW5kT3RwRW1haWwoZW1haWw6IHN0cmluZywgY29kZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5zZW5kKFxuICAgICAge1xuICAgICAgICB0bzogW2VtYWlsXSxcbiAgICAgICAgc3ViamVjdDogYFlvdXIgc2lnbi1pbiBjb2RlOiAke2NvZGV9YCxcbiAgICAgICAgYm9keTogW1xuICAgICAgICAgIGBZb3VyIG9uZS10aW1lIHNpZ24taW4gY29kZSBpczpgLFxuICAgICAgICAgIGBgLFxuICAgICAgICAgIGAjIyAke2NvZGV9YCxcbiAgICAgICAgICBgYCxcbiAgICAgICAgICBgSXQgZXhwaXJlcyBzaG9ydGx5LiBJZiB5b3UgZGlkbid0IHJlcXVlc3QgdGhpcywgeW91IGNhbiBpZ25vcmUgdGhpcyBlbWFpbC5gLFxuICAgICAgICBdLmpvaW4oXCJcXG5cIiksXG4gICAgICB9LFxuICAgICAgdW5kZWZpbmVkLFxuICAgICk7XG4gIH1cblxuICAvKiogUnVuIHRoZSBzZW5kZXItb3B0aW9ucyBsb29rdXAgdGhyb3VnaCB0aGUgcGx1Z2luJ3MgaW50ZXJjZXB0b3IgY2hhaW4uICovXG4gIHByaXZhdGUgYXN5bmMgZXhlY3V0ZUxpc3RTZW5kZXJzKCk6IFByb21pc2U8RXhlY3V0aW9uUmVzdWx0PEVtYWlsU2VuZGVycz4+IHtcbiAgICByZXR1cm4gdGhpcy5leGVjdXRlKGFzeW5jICgpID0+IHRoaXMubGlzdFNlbmRlcnMoKSwgRU1BSUxfU0VOREVSU19TRVRUSU5HUyk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZSB0aGUgYEZyb21gIG9wdGlvbnMgb2ZmZXJlZCB0byB0aGUgY3VycmVudCB1c2VyOiB0aGUgY29uY3JldGVcbiAgICogYWRkcmVzc2VzIHRoZSBlZmZlY3RpdmUgYWxsb3ctbGlzdCBwZXJtaXRzIChkb21haW4gd2lsZGNhcmRzIGV4cGFuZGVkXG4gICAqIGFnYWluc3QgdGhlIE9CTyB1c2VyJ3MgbG9jYWwgcGFydCksIHRoZSBkZWZhdWx0IGFtb25nIHRoZW0sIGFuZFxuICAgKiB3aGV0aGVyIHRoZSBsaXN0IGlzIGFuIGVuZm9yY2VkIHJlc3RyaWN0aW9uLiBTZWVcbiAgICoge0BsaW5rIGxpc3RTZW5kZXJPcHRpb25zfS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgbGlzdFNlbmRlcnMoKTogUHJvbWlzZTxFbWFpbFNlbmRlcnM+IHtcbiAgICBjb25zdCB7IGNvbmZpZyB9ID0gZ2V0RW1haWxSdW50aW1lKCk7XG4gICAgY29uc3Qgc2VuZGVycyA9IGxpc3RTZW5kZXJPcHRpb25zKGNvbmZpZywgY3VycmVudFVzZXJFbWFpbCgpKTtcbiAgICAvLyBQcmVmZXIgdGhlIGFkZHJlc3MgYSBzZW5kIHdvdWxkIGFjdHVhbGx5IGRlZmF1bHQgdG87IGZhbGwgYmFjayB0b1xuICAgIC8vIHRoZSBmaXJzdCBvZmZlcmVkIG9wdGlvbiB3aGVuIHRoYXQgY2FuJ3QgYmUgcmVzb2x2ZWQgLyBwZXJtaXR0ZWQuXG4gICAgbGV0IGRlZmF1bHRTZW5kZXIgPSBzZW5kZXJzWzBdO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNvbHZlZCA9IHRoaXMucmVzb2x2ZVNlbmRlcigpLnRvTG93ZXJDYXNlKCk7XG4gICAgICBpZiAoaXNTZW5kZXJBbGxvd2VkKHJlc29sdmVkLCBjb25maWcuYWxsb3dlZFNlbmRlcnMpKSBkZWZhdWx0U2VuZGVyID0gcmVzb2x2ZWQ7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBLZWVwIHRoZSBmaXJzdCBvZmZlcmVkIG9wdGlvbiAob3Igbm9uZSkgYXMgdGhlIGRlZmF1bHQuXG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBzZW5kZXJzLFxuICAgICAgLi4uKGRlZmF1bHRTZW5kZXIgPyB7IGRlZmF1bHRTZW5kZXIgfSA6IHt9KSxcbiAgICAgIHJlc3RyaWN0ZWQ6IGNvbmZpZy5hbGxvd2VkU2VuZGVycy5sZW5ndGggPiAwLFxuICAgIH07XG4gIH1cblxuICAvKiogVGhlIGBGcm9tYCBhIHNlbmQgZGVmYXVsdHMgdG8gZm9yIHRoZSBjYWxsZXIgaW4gc2NvcGUuICovXG4gIHByaXZhdGUgcmVzb2x2ZVNlbmRlcigpOiBzdHJpbmcge1xuICAgIHJldHVybiByZXNvbHZlU2VuZGVyQWRkcmVzcyhnZXRFbWFpbFJ1bnRpbWUoKS5jb25maWcsIGN1cnJlbnRVc2VyRW1haWwoKSk7XG4gIH1cbn1cblxuLyoqIFRoZSBPQk8gdXNlcidzIGFkZHJlc3MsIG9yIHVuZGVmaW5lZCBvdXRzaWRlIGEgdXNlciBjb250ZXh0LiAqL1xuZnVuY3Rpb24gY3VycmVudFVzZXJFbWFpbCgpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCBjdHggPSBnZXRFeGVjdXRpb25Db250ZXh0KCk7XG4gIHJldHVybiBcImlzVXNlckNvbnRleHRcIiBpbiBjdHggPyBjdHgudXNlckVtYWlsIDogdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFJlLXJhaXNlIGEgZmFpbGVkIGV4ZWN1dGlvbiBhcyB0aGUgQXBwS2l0IGVycm9yIGNsYXNzIHRoYXQgYWxyZWFkeSBjYXJyaWVzXG4gKiB0aGUgc3RhdHVzIEFwcEtpdCByZXNvbHZlZCwgc28gYSBwcm9ncmFtbWF0aWMgY2FsbGVyIHNlZXMgdGhlIHNhbWUgNDAwIC9cbiAqIDQwMSAvIDUwMyBhbiBIVFRQIGNhbGxlciB3b3VsZC5cbiAqL1xuZnVuY3Rpb24gdG9BcHBLaXRFcnJvcihzdGF0dXM6IG51bWJlciwgbWVzc2FnZTogc3RyaW5nKTogQXBwS2l0RXJyb3Ige1xuICBpZiAoc3RhdHVzID09PSA0MDApIHJldHVybiBuZXcgVmFsaWRhdGlvbkVycm9yKG1lc3NhZ2UpO1xuICBpZiAoc3RhdHVzID09PSA0MDEpIHJldHVybiBuZXcgQXV0aGVudGljYXRpb25FcnJvcihtZXNzYWdlKTtcbiAgaWYgKHN0YXR1cyA9PT0gNTAzKSByZXR1cm4gbmV3IENvbm5lY3Rpb25FcnJvcihtZXNzYWdlKTtcbiAgcmV0dXJuIG5ldyBFeGVjdXRpb25FcnJvcihtZXNzYWdlKTtcbn1cblxuLyoqXG4gKiBTdXJmYWNlIGEgZmFpbGVkIHtAbGluayBFeGVjdXRpb25SZXN1bHR9IHRvIGEgcHJvZ3JhbW1hdGljIGNhbGxlciBhcyBhXG4gKiB0aHJvdy4gSFRUUCBoYW5kbGVycyBtYXAgYHN0YXR1c2Agb250byB0aGUgcmVzcG9uc2UgaW5zdGVhZC5cbiAqL1xuZnVuY3Rpb24gdW53cmFwPFQ+KHJlc3VsdDogRXhlY3V0aW9uUmVzdWx0PFQ+KTogVCB7XG4gIGlmIChyZXN1bHQub2spIHJldHVybiByZXN1bHQuZGF0YTtcbiAgdGhyb3cgdG9BcHBLaXRFcnJvcihyZXN1bHQuc3RhdHVzLCByZXN1bHQubWVzc2FnZSk7XG59XG5cbi8qKlxuICogUmVnaXN0ZXIgdGhlIGVtYWlsIHBsdWdpbi5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGltcG9ydCB7IGNyZWF0ZUFwcCwgc2VydmVyIH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuICogaW1wb3J0IHsgYnJhbmQsIHBsdWdpbiBhcyBlbWFpbFBsdWdpbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2VtYWlsXCI7XG4gKlxuICogYXdhaXQgY3JlYXRlQXBwKHtcbiAqICAgcGx1Z2luczogW1xuICogICAgIHNlcnZlcigpLFxuICogICAgIGVtYWlsUGx1Z2luLmVtYWlsKHtcbiAqICAgICAgIGRvbWFpbjogXCJtYWlsLmV4YW1wbGUuY29tXCIsXG4gKiAgICAgICBhbGxvd2VkU2VuZGVyczogW1wiKkBtYWlsLmV4YW1wbGUuY29tXCJdLFxuICogICAgICAgYnJhbmQ6IGJyYW5kLmRlZmF1bHRFbWFpbEJyYW5kLFxuICogICAgIH0pLFxuICogICBdLFxuICogfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNvbnN0IGVtYWlsID0gdG9QbHVnaW4oRW1haWxQbHVnaW4pO1xuIl19
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/brand.ts","../src/config.ts","../src/defaults.ts","../src/email-html.ts","../src/juice.d.ts","../src/markdown.ts","../src/outbox.ts","../src/plugin.ts","../src/sender.ts","../src/tool.ts","../src/transport.ts","../index.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/brand.ts","../src/config.ts","../src/defaults.ts","../src/email-html.ts","../src/juice.d.ts","../src/markdown.ts","../src/outbox.ts","../src/plugin.ts","../src/sender.ts","../src/tool.ts","../src/transport.ts","../src/auth/allowlist.ts","../src/auth/gate.ts","../src/auth/otp.ts","../src/auth/rate-limit.ts","../index.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -27,9 +27,10 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@databricks/appkit": "^0.43.0",
|
|
30
|
-
"@dbx-tools/shared-core": "0.6.
|
|
31
|
-
"@dbx-tools/shared-email": "0.6.
|
|
30
|
+
"@dbx-tools/shared-core": "0.6.42",
|
|
31
|
+
"@dbx-tools/shared-email": "0.6.42",
|
|
32
32
|
"@mastra/core": "^1.47.0",
|
|
33
|
+
"jose": "^6.2.3",
|
|
33
34
|
"juice": "^12.1.1",
|
|
34
35
|
"marked": "^18.0.5",
|
|
35
36
|
"nodemailer": "^7.0.13"
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"./package.json": "./package.json"
|
|
49
50
|
}
|
|
50
51
|
},
|
|
51
|
-
"version": "0.6.
|
|
52
|
+
"version": "0.6.42",
|
|
52
53
|
"types": "index.ts",
|
|
53
54
|
"type": "module",
|
|
54
55
|
"exports": {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified access allow-list matching for the email-OTP gate.
|
|
3
|
+
*
|
|
4
|
+
* Each pattern in the configured list is one of three shapes, tried in order:
|
|
5
|
+
*
|
|
6
|
+
* - **domain shortcut** - `databricks.com` or `@databricks.com`: matches any
|
|
7
|
+
* address whose domain equals it (case-insensitive). The leading `@` is
|
|
8
|
+
* optional and stripped.
|
|
9
|
+
* - **glob** - contains `*` or `?`, e.g. `*.databricks.com` or
|
|
10
|
+
* `*@databricks.com`: matched against the WHOLE address with shell-style
|
|
11
|
+
* wildcards (`*` = any run, `?` = one char).
|
|
12
|
+
* - **regex** - wrapped in slashes, `/.../ [flags]`: compiled and tested
|
|
13
|
+
* against the whole address. An invalid regex never matches (it is skipped
|
|
14
|
+
* with a warning rather than throwing).
|
|
15
|
+
*
|
|
16
|
+
* An EMPTY list matches nobody (fail closed): an app that enables the gate but
|
|
17
|
+
* configures no patterns lets no one in, which is the safe default.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { log } from "@dbx-tools/shared-core";
|
|
23
|
+
|
|
24
|
+
const logger = log.logger("email:auth:allowlist");
|
|
25
|
+
|
|
26
|
+
/** Escape a string for literal use inside a `RegExp`. */
|
|
27
|
+
function escapeRegExp(value: string): string {
|
|
28
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Compile a shell-style glob (`*`, `?`) into an anchored, case-insensitive RegExp. */
|
|
32
|
+
function globToRegExp(glob: string): RegExp {
|
|
33
|
+
const body = glob
|
|
34
|
+
.split(/([*?])/)
|
|
35
|
+
.map((part) => (part === "*" ? ".*" : part === "?" ? "." : escapeRegExp(part)))
|
|
36
|
+
.join("");
|
|
37
|
+
return new RegExp(`^${body}$`, "i");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Parse a `/pattern/flags` string into a RegExp, or `undefined` if malformed. */
|
|
41
|
+
function parseRegexLiteral(pattern: string): RegExp | undefined {
|
|
42
|
+
const match = /^\/(.+)\/([a-z]*)$/is.exec(pattern);
|
|
43
|
+
if (!match) return undefined;
|
|
44
|
+
try {
|
|
45
|
+
const flags = match[2]!.includes("i") ? match[2]! : `${match[2]!}i`;
|
|
46
|
+
return new RegExp(match[1]!, flags);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.warn("ignoring invalid regex allow-list pattern", { pattern, error });
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** True when `email` matches a single allow-list `pattern`. */
|
|
54
|
+
function matchesPattern(email: string, pattern: string): boolean {
|
|
55
|
+
const trimmed = pattern.trim();
|
|
56
|
+
if (!trimmed) return false;
|
|
57
|
+
const address = email.trim().toLowerCase();
|
|
58
|
+
|
|
59
|
+
// Regex literal: /.../
|
|
60
|
+
if (trimmed.startsWith("/")) {
|
|
61
|
+
const re = parseRegexLiteral(trimmed);
|
|
62
|
+
return re ? re.test(address) : false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Glob: contains a wildcard.
|
|
66
|
+
if (trimmed.includes("*") || trimmed.includes("?")) {
|
|
67
|
+
return globToRegExp(trimmed).test(address);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Domain shortcut: `@d.com` or `d.com` -> match the address's domain.
|
|
71
|
+
const domain = trimmed.replace(/^@/, "").toLowerCase();
|
|
72
|
+
const at = address.lastIndexOf("@");
|
|
73
|
+
return at >= 0 && address.slice(at + 1) === domain;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* True when `email` is allowed by ANY pattern in `patterns`. An empty (or
|
|
78
|
+
* missing) list allows nobody - the gate fails closed.
|
|
79
|
+
*/
|
|
80
|
+
export function matchesAllowlist(email: string, patterns: readonly string[] | undefined): boolean {
|
|
81
|
+
if (!email || !patterns || patterns.length === 0) return false;
|
|
82
|
+
return patterns.some((pattern) => matchesPattern(email, pattern));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Rough shape check so a clearly-invalid address is rejected before any work. */
|
|
86
|
+
export function looksLikeEmail(value: string): boolean {
|
|
87
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
|
|
88
|
+
}
|
package/src/auth/gate.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The email-OTP access gate: runtime + Express middleware + request handlers.
|
|
3
|
+
*
|
|
4
|
+
* {@link AuthGate} owns the allow-list, rate limiters, code store, and session
|
|
5
|
+
* signing. The plugin wires four routes to it (`request`/`verify`/`logout`/
|
|
6
|
+
* `status`) and mounts {@link AuthGate.middleware} so every OTHER request needs a
|
|
7
|
+
* valid session cookie.
|
|
8
|
+
*
|
|
9
|
+
* Design points:
|
|
10
|
+
* - `request` ALWAYS resolves `{ ok: true }` (anti-enumeration): a code is only
|
|
11
|
+
* generated + emailed when the address is allow-listed AND under the rate
|
|
12
|
+
* limit, but the caller can't tell the difference.
|
|
13
|
+
* - the session lives in an HttpOnly + SameSite=Lax cookie (Secure in prod), so
|
|
14
|
+
* it survives reloads and is not readable by page scripts.
|
|
15
|
+
* - fail-open on a missing signing secret (see `otp.ts`): a Databricks App is
|
|
16
|
+
* already access-limited; an unset `AUTH_JWT_SECRET` degrades to
|
|
17
|
+
* non-durable sessions, not a lockout.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { CookieOptions, NextFunction, Request, Response } from "express";
|
|
23
|
+
import { http, log } from "@dbx-tools/shared-core";
|
|
24
|
+
import type { AuthStatus } from "@dbx-tools/shared-email";
|
|
25
|
+
import { looksLikeEmail, matchesAllowlist } from "./allowlist.ts";
|
|
26
|
+
import { CodeStore, signSession, verifySession } from "./otp.ts";
|
|
27
|
+
import { RateLimiter } from "./rate-limit.ts";
|
|
28
|
+
|
|
29
|
+
const logger = log.logger("email:auth");
|
|
30
|
+
|
|
31
|
+
/** Cookie the session JWT rides in. */
|
|
32
|
+
export const SESSION_COOKIE = "dbx_auth";
|
|
33
|
+
|
|
34
|
+
/** Route prefix (under the plugin base `/api/email`) the gate leaves open. */
|
|
35
|
+
const AUTH_PATH_PREFIX = "/auth";
|
|
36
|
+
|
|
37
|
+
/** Resolved gate configuration. */
|
|
38
|
+
export interface AuthGateOptions {
|
|
39
|
+
/** Allow-list patterns (domain / glob / `/regex/`). Empty = allow nobody. */
|
|
40
|
+
readonly allow: readonly string[];
|
|
41
|
+
/** Session lifetime in seconds. */
|
|
42
|
+
readonly sessionTtlSeconds: number;
|
|
43
|
+
/** One-time code lifetime in seconds. */
|
|
44
|
+
readonly codeTtlSeconds: number;
|
|
45
|
+
/** Max verify attempts per issued code. */
|
|
46
|
+
readonly maxAttempts: number;
|
|
47
|
+
/** Send the code email. Returns nothing; failures are logged, not surfaced. */
|
|
48
|
+
readonly sendCode: (email: string, code: string) => Promise<void>;
|
|
49
|
+
/** True in production (sets the `Secure` cookie flag). */
|
|
50
|
+
readonly secureCookies: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The email-OTP gate for one app. */
|
|
54
|
+
export class AuthGate {
|
|
55
|
+
private readonly codes: CodeStore;
|
|
56
|
+
// Separate limiters: requesting a code is cheap-to-abuse (email spam), verifying
|
|
57
|
+
// is a brute-force surface. Per-email AND per-IP so neither axis alone is a bypass.
|
|
58
|
+
private readonly requestLimiter = new RateLimiter(5, 15 * 60 * 1000);
|
|
59
|
+
private readonly verifyLimiter = new RateLimiter(10, 15 * 60 * 1000);
|
|
60
|
+
|
|
61
|
+
constructor(private readonly options: AuthGateOptions) {
|
|
62
|
+
this.codes = new CodeStore(options.codeTtlSeconds * 1000, options.maxAttempts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handle `POST /auth/request`. Always resolves `{ ok: true }` (plus a
|
|
67
|
+
* `retryAfter` when rate-limited); a code is issued + emailed only for an
|
|
68
|
+
* allow-listed address under the limit. Errors in sending are swallowed so the
|
|
69
|
+
* response never reveals whether an address exists / is allowed.
|
|
70
|
+
*/
|
|
71
|
+
async handleRequest(email: string, ip: string): Promise<{ ok: true; retryAfter?: number }> {
|
|
72
|
+
const address = email.trim().toLowerCase();
|
|
73
|
+
const byIp = this.requestLimiter.hit(`ip:${ip}`);
|
|
74
|
+
const byEmail = this.requestLimiter.hit(`email:${address}`);
|
|
75
|
+
if (!byIp.allowed || !byEmail.allowed) {
|
|
76
|
+
return { ok: true, retryAfter: byIp.retryAfter ?? byEmail.retryAfter };
|
|
77
|
+
}
|
|
78
|
+
if (looksLikeEmail(address) && matchesAllowlist(address, this.options.allow)) {
|
|
79
|
+
const code = this.codes.issue(address);
|
|
80
|
+
try {
|
|
81
|
+
await this.options.sendCode(address, code);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logger.warn("failed to send OTP email", { error });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { ok: true };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Handle `POST /auth/verify`. On a correct code, returns the session `token`
|
|
91
|
+
* plus the `cookieOptions` for it so the route sets it with Express's own
|
|
92
|
+
* `res.cookie(SESSION_COOKIE, token, options)` (no hand-rolled Set-Cookie).
|
|
93
|
+
* Failures return `{ ok: false }` with a generic reason.
|
|
94
|
+
*/
|
|
95
|
+
async handleVerify(
|
|
96
|
+
email: string,
|
|
97
|
+
code: string,
|
|
98
|
+
ip: string,
|
|
99
|
+
secure: boolean,
|
|
100
|
+
): Promise<{ ok: boolean; token?: string; cookieOptions?: CookieOptions; retryAfter?: number }> {
|
|
101
|
+
const address = email.trim().toLowerCase();
|
|
102
|
+
const byIp = this.verifyLimiter.hit(`ip:${ip}`);
|
|
103
|
+
const byEmail = this.verifyLimiter.hit(`email:${address}`);
|
|
104
|
+
if (!byIp.allowed || !byEmail.allowed) {
|
|
105
|
+
return { ok: false, retryAfter: byIp.retryAfter ?? byEmail.retryAfter };
|
|
106
|
+
}
|
|
107
|
+
if (this.codes.verify(address, code.trim()) !== "ok") return { ok: false };
|
|
108
|
+
// Correct code: clear the caller's request/verify budget and mint a session.
|
|
109
|
+
this.requestLimiter.reset(`email:${address}`);
|
|
110
|
+
this.verifyLimiter.reset(`email:${address}`);
|
|
111
|
+
const token = await signSession(address, this.options.sessionTtlSeconds);
|
|
112
|
+
return { ok: true, token, cookieOptions: this.cookieOptions(secure) };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Express cookie options for the session (used with `res.cookie`/`res.clearCookie`). */
|
|
116
|
+
cookieOptions(secure: boolean): CookieOptions {
|
|
117
|
+
return {
|
|
118
|
+
httpOnly: true,
|
|
119
|
+
sameSite: "lax",
|
|
120
|
+
secure: secure || this.options.secureCookies,
|
|
121
|
+
path: "/",
|
|
122
|
+
maxAge: this.options.sessionTtlSeconds * 1000,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Resolve the authenticated email for a request, or `undefined`. */
|
|
127
|
+
async authenticate(req: Request): Promise<string | undefined> {
|
|
128
|
+
// Reuse shared-core's cookie parser (accepts an Express req directly) rather
|
|
129
|
+
// than re-implementing header splitting here.
|
|
130
|
+
const token = http.parseCookies(req)[SESSION_COOKIE];
|
|
131
|
+
return verifySession(token);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** The `GET /auth/status` payload for a request. */
|
|
135
|
+
async status(req: Request): Promise<AuthStatus> {
|
|
136
|
+
const email = await this.authenticate(req);
|
|
137
|
+
return { authenticated: Boolean(email), email, enabled: true };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Express middleware gating requests on the APP router (mounted at the app
|
|
142
|
+
* root). Requests to the login flow (`<emailBase>/auth/*`) pass through so a
|
|
143
|
+
* caller can authenticate; the app's own API/asset requests need a valid
|
|
144
|
+
* session or get a 401. `emailBase` is the plugin's mount path (e.g.
|
|
145
|
+
* `/api/email`) so the open prefix is matched against the full request path.
|
|
146
|
+
*/
|
|
147
|
+
middleware(emailBase: string): (req: Request, res: Response, next: NextFunction) => void {
|
|
148
|
+
const openPrefix = `${emailBase}${AUTH_PATH_PREFIX}`;
|
|
149
|
+
return (req, res, next) => {
|
|
150
|
+
if (req.path.startsWith(openPrefix)) {
|
|
151
|
+
next();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
void this.authenticate(req).then((email) => {
|
|
155
|
+
if (email) {
|
|
156
|
+
next();
|
|
157
|
+
} else {
|
|
158
|
+
res.status(401).json({ error: "authentication required", loginPath: openPrefix });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|