@dbx-tools/cli-tunnel 0.6.49 → 0.6.51
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/README.md +70 -19
- package/index.ts +4 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +4 -2
- package/lib/src/cli.js +3 -1
- package/lib/src/env.d.ts +5 -0
- package/lib/src/env.js +6 -1
- package/lib/src/otp.d.ts +7 -11
- package/lib/src/otp.js +24 -38
- package/lib/src/plugin.d.ts +17 -1
- package/lib/src/plugin.js +10 -2
- package/lib/src/signing-key.d.ts +83 -0
- package/lib/src/signing-key.js +181 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -6
- package/src/cli.ts +6 -0
- package/src/env.ts +6 -0
- package/src/otp.ts +23 -40
- package/src/plugin.ts +26 -2
- package/src/signing-key.ts +212 -0
package/lib/src/plugin.js
CHANGED
|
@@ -21,6 +21,7 @@ import { looksLikeEmail, matchesAllowlist } from "./allowlist.js";
|
|
|
21
21
|
import { ALLOW_ENV, BRAND_NAME_ENV, CODE_TTL_ENV, MESSAGE_ENV, SESSION_TTL_ENV, SUBJECT_ENV, } from "./env.js";
|
|
22
22
|
import { CodeStore, signSession, verifySession } from "./otp.js";
|
|
23
23
|
import { RateLimiter } from "./rate-limit.js";
|
|
24
|
+
import { KEY_TTL_SECONDS, resolveSessionEpoch, signingKey } from "./signing-key.js";
|
|
24
25
|
const logger = log.logger("tunnel:auth");
|
|
25
26
|
const DEFAULTS = {
|
|
26
27
|
subject: "Your verification code",
|
|
@@ -32,7 +33,8 @@ const DEFAULTS = {
|
|
|
32
33
|
// is the fallback when nothing is configured.
|
|
33
34
|
brandName: brand.defaultBrandContext.name,
|
|
34
35
|
message: "Your verification code is:",
|
|
35
|
-
|
|
36
|
+
// 30 days, the same window the cache-backed signing key is stored for.
|
|
37
|
+
sessionTtlSeconds: KEY_TTL_SECONDS,
|
|
36
38
|
codeTtlSeconds: 600,
|
|
37
39
|
maxAttempts: 5,
|
|
38
40
|
};
|
|
@@ -50,6 +52,7 @@ export function resolveAuthGateConfig(config) {
|
|
|
50
52
|
sessionTtlSeconds: env.positiveInt(config.sessionTtlSeconds, SESSION_TTL_ENV, DEFAULTS.sessionTtlSeconds),
|
|
51
53
|
codeTtlSeconds: env.positiveInt(config.codeTtlSeconds, CODE_TTL_ENV, DEFAULTS.codeTtlSeconds),
|
|
52
54
|
maxAttempts: config.maxAttempts ?? DEFAULTS.maxAttempts,
|
|
55
|
+
sessionEpochMs: resolveSessionEpoch(config.sessionEpoch),
|
|
53
56
|
};
|
|
54
57
|
}
|
|
55
58
|
/** AppKit plugin owning the email-OTP gate's logic (no HTTP routes; proxy-driven). */
|
|
@@ -70,9 +73,14 @@ export class AuthGatePlugin extends Plugin {
|
|
|
70
73
|
async setup() {
|
|
71
74
|
this.resolved = resolveAuthGateConfig(this.config);
|
|
72
75
|
this.codes = new CodeStore(this.resolved.codeTtlSeconds, this.resolved.maxAttempts);
|
|
76
|
+
// Resolve the signing key HERE rather than lazily on the first sign-in, so a
|
|
77
|
+
// cache that cannot hold it (and the resulting "sessions won't survive a
|
|
78
|
+
// restart" warning) shows up in the startup log, not hours later.
|
|
79
|
+
const { epochMs } = await signingKey(this.resolved.sessionEpochMs);
|
|
73
80
|
logger.info("ready", {
|
|
74
81
|
patterns: this.resolved.allow.length,
|
|
75
82
|
sessionTtlSeconds: this.resolved.sessionTtlSeconds,
|
|
83
|
+
...(epochMs > 0 ? { sessionEpoch: new Date(epochMs).toISOString() } : {}),
|
|
76
84
|
});
|
|
77
85
|
}
|
|
78
86
|
exports() {
|
|
@@ -127,4 +135,4 @@ export class AuthGatePlugin extends Plugin {
|
|
|
127
135
|
}
|
|
128
136
|
/** Factory: `authGate({ allow, subject, ... })` for an AppKit `plugins` array. */
|
|
129
137
|
export const authGate = toPlugin(AuthGatePlugin);
|
|
130
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUE4QyxNQUFNLG9CQUFvQixDQUFDO0FBQ2xHLE9BQU8sRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVqRSxPQUFPLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEUsT0FBTyxFQUNMLFNBQVMsRUFDVCxjQUFjLEVBQ2QsWUFBWSxFQUNaLFdBQVcsRUFDWCxlQUFlLEVBQ2YsV0FBVyxHQUNaLE1BQU0sVUFBVSxDQUFDO0FBQ2xCLE9BQU8sRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNqRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFOUMsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztBQWtFekMsTUFBTSxRQUFRLEdBQUc7SUFDZixPQUFPLEVBQUUsd0JBQXdCO0lBQ2pDLDhFQUE4RTtJQUM5RSwrRUFBK0U7SUFDL0Usa0VBQWtFO0lBQ2xFLGlFQUFpRTtJQUNqRSxnRkFBZ0Y7SUFDaEYsOENBQThDO0lBQzlDLFNBQVMsRUFBRSxLQUFLLENBQUMsbUJBQW1CLENBQUMsSUFBSTtJQUN6QyxPQUFPLEVBQUUsNEJBQTRCO0lBQ3JDLGlCQUFpQixFQUFFLEtBQUs7SUFDeEIsY0FBYyxFQUFFLEdBQUc7SUFDbkIsV0FBVyxFQUFFLENBQUM7Q0FDZixDQUFDO0FBRUYsa0ZBQWtGO0FBQ2xGLE1BQU0sVUFBVSxxQkFBcUIsQ0FBQyxNQUFzQjtJQUMxRCxPQUFPO1FBQ0wseUVBQXlFO1FBQ3pF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QUssRUFBRSxDQUFDO1FBQ25GLElBQUksQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLFNBQVMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxTQUFTLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDN0MsT0FBTyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLE1BQU0sV0FBVyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztJQUMxRixDQUFDOztBQUdILGtGQUFrRjtBQUNsRixNQUFNLENBQUMsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBgYXV0aEdhdGUoKWAgLSB0aGUgQXBwS2l0IHBsdWdpbiBiZWhpbmQgdGhlIHR1bm5lbCdzIGVtYWlsLU9UUCBnYXRlLlxuICpcbiAqIEl0IGhhcyBOTyByb3V0ZXMgb2YgaXRzIG93bjogdGhlIHR1bm5lbCBQUk9YWSAobm90IGFuIEhUVFAgc2VydmVyKSBjYWxscyB0aGVcbiAqIGhhbmRsZXJzIHRoaXMgcGx1Z2luIGV4cG9zZXMgdmlhIHtAbGluayBBdXRoR2F0ZVBsdWdpbi5leHBvcnRzfS4gVGhlIHBsdWdpblxuICogb3ducyB0aGUgYWxsb3ctbGlzdCwgdGhlIHBlci1lbWFpbC9wZXItSVAgcmF0ZSBsaW1pdGVycywgdGhlIENhY2hlTWFuYWdlci1iYWNrZWRcbiAqIG9uZS10aW1lLWNvZGUgc3RvcmUsIGFuZCB0aGUgc2Vzc2lvbiBKV1QuIGBjcmVhdGVBcHBgICh3aXRoIG5vIGBzZXJ2ZXIoKWApIGlzXG4gKiB1c2VkIG9ubHkgdG8gYXV0by1pbml0IGBDYWNoZU1hbmFnZXJgICsgcHJpbWUgdGhlIHNpYmxpbmcgYGVtYWlsYCB0cmFuc3BvcnQ7XG4gKiB0aGlzIHBsdWdpbiBpcyB3aGVyZSB0aGUgZ2F0ZSBsb2dpYyBsaXZlcy5cbiAqXG4gKiBPcHRpb25zIGNvbWUgZnJvbSBDTEkgZmxhZ3MgT1IgZW52LCB3aXRoIHNlbnNpYmxlIGRlZmF1bHRzIC0gc2VlXG4gKiB7QGxpbmsgcmVzb2x2ZUF1dGhHYXRlQ29uZmlnfS4gVGhlIG9uZSBydW50aW1lIGRlcGVuZGVuY3kgdGhlIHBsdWdpbiBjYW4ndFxuICogcmVzb2x2ZSBpdHNlbGYgaXMgSE9XIHRvIGVtYWlsIHRoZSBjb2RlLCBzbyBpdCB0YWtlcyBhIGBzZW5kQ29kZWAgY2FsbGJhY2sgdGhlXG4gKiBhcHAgd2lyZXMgdG8gdGhlIGVtYWlsIHBsdWdpbi5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgUGx1Z2luLCB0b1BsdWdpbiwgdHlwZSBCYXNlUGx1Z2luQ29uZmlnLCB0eXBlIFBsdWdpbk1hbmlmZXN0IH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHsgYnJhbmQsIGVudiwgbG9nLCBzdHJpbmcgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHR5cGUgeyBBdXRoU3RhdHVzIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWVtYWlsXCI7XG5pbXBvcnQgeyBsb29rc0xpa2VFbWFpbCwgbWF0Y2hlc0FsbG93bGlzdCB9IGZyb20gXCIuL2FsbG93bGlzdC50c1wiO1xuaW1wb3J0IHtcbiAgQUxMT1dfRU5WLFxuICBCUkFORF9OQU1FX0VOVixcbiAgQ09ERV9UVExfRU5WLFxuICBNRVNTQUdFX0VOVixcbiAgU0VTU0lPTl9UVExfRU5WLFxuICBTVUJKRUNUX0VOVixcbn0gZnJvbSBcIi4vZW52LnRzXCI7XG5pbXBvcnQgeyBDb2RlU3RvcmUsIHNpZ25TZXNzaW9uLCB2ZXJpZnlTZXNzaW9uIH0gZnJvbSBcIi4vb3RwLnRzXCI7XG5pbXBvcnQgeyBSYXRlTGltaXRlciB9IGZyb20gXCIuL3JhdGUtbGltaXQudHNcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcInR1bm5lbDphdXRoXCIpO1xuXG4vKiogT3B0aW9ucyBmb3IgdGhlIHtAbGluayBhdXRoR2F0ZX0gcGx1Z2luIChhbGwgcmVzb2x2YWJsZSBmcm9tIGVudiAtIHNlZSBiZWxvdykuICovXG5leHBvcnQgaW50ZXJmYWNlIEF1dGhHYXRlQ29uZmlnIGV4dGVuZHMgQmFzZVBsdWdpbkNvbmZpZyB7XG4gIC8qKiBBbGxvdy1saXN0IHBhdHRlcm5zIChkb21haW4gLyBnbG9iIC8gYC9yZWdleC9gKS4gRW1wdHkgPSBhbGxvdyBub2JvZHkuIEVudiBUVU5ORUxfQVVUSF9BTExPVy4gKi9cbiAgYWxsb3c/OiBzdHJpbmcgfCBzdHJpbmdbXTtcbiAgLyoqXG4gICAqIFN1YmplY3QgbGluZSBmb3IgdGhlIGNvZGUgZW1haWwuIEVudiBUVU5ORUxfQVVUSF9TVUJKRUNULlxuICAgKlxuICAgKiBEZWZhdWx0cyB0byBcIllvdXIgdmVyaWZpY2F0aW9uIGNvZGVcIi4gVGhlIHdvcmRpbmcgb2YgdGhlIHN1YmplY3QgYW5kXG4gICAqIHtAbGluayBtZXNzYWdlfSBpcyBkZWxpYmVyYXRlbHkgdGhlIGNvbnZlbnRpb25hbCBwaHJhc2luZyByYXRoZXIgdGhhblxuICAgKiBhbnl0aGluZyBicmFuZGVkOiBpT1MsIEdtYWlsLCBPdXRsb29rLCBhbmQgQW5kcm9pZCBhbGwgZGV0ZWN0IGEgb25lLXRpbWVcbiAgICogY29kZSBmcm9tIHRoaXMgc2hhcGUgYW5kIG9mZmVyIHRvIGF1dG9maWxsIGl0LCBhbmQgYSBub3ZlbCBwaHJhc2luZyBpcyB3aGF0XG4gICAqIGJyZWFrcyB0aGF0IGRldGVjdGlvbi5cbiAgICovXG4gIHN1YmplY3Q/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBEaXNwbGF5IG5hbWUgdXNlZCBpbiB0aGUgY29kZSBlbWFpbCBjb3B5LiBFbnYgVFVOTkVMX0FVVEhfQlJBTkRfTkFNRS5cbiAgICpcbiAgICogRGVmYXVsdHMgdG8gdGhlIGJyYW5kIGNvbnRleHQncyBgbmFtZWAgLSB0aGUgYXBwJ3Mgb3duIGBicmFuZGluZy9icmFuZC55YW1sYFxuICAgKiB3aGVuIGl0IGhhcyBvbmUsIGVsc2UgdGhlIGRieC10b29scyBkZWZhdWx0LiBTZXQgdGhpcyBvbmx5IHRvIG92ZXJyaWRlIHRoZVxuICAgKiBicmFuZCBmb3IgdGhpcyBnYXRlLlxuICAgKi9cbiAgYnJhbmROYW1lPzogc3RyaW5nO1xuICAvKipcbiAgICogTGluZSBzaG93biBpbW1lZGlhdGVseSBhYm92ZSB0aGUgY29kZSBpbiB0aGUgZW1haWwuIEVudiBUVU5ORUxfQVVUSF9NRVNTQUdFLlxuICAgKlxuICAgKiBLZWVwIHRoZSBjb2RlIG9uIGl0cyBPV04gbGluZSBkaXJlY3RseSBhZnRlciB0aGlzIHRleHQgLSB0aGF0IGFkamFjZW5jeSBpc1xuICAgKiB3aGF0IHRoZSBwbGF0Zm9ybSBjb2RlLWRldGVjdGlvbiBoZXVyaXN0aWNzIGtleSBvbi5cbiAgICovXG4gIG1lc3NhZ2U/OiBzdHJpbmc7XG4gIC8qKiBTZXNzaW9uIGxpZmV0aW1lIChzZWNvbmRzKS4gRW52IFRVTk5FTF9BVVRIX1NFU1NJT05fVFRMLiBEZWZhdWx0IDQzMjAwICgxMmgpLiAqL1xuICBzZXNzaW9uVHRsU2Vjb25kcz86IG51bWJlcjtcbiAgLyoqIE9uZS10aW1lLWNvZGUgbGlmZXRpbWUgKHNlY29uZHMpLiBFbnYgVFVOTkVMX0FVVEhfQ09ERV9UVEwuIERlZmF1bHQgNjAwICgxMG0pLiAqL1xuICBjb2RlVHRsU2Vjb25kcz86IG51bWJlcjtcbiAgLyoqIE1heCB2ZXJpZnkgYXR0ZW1wdHMgcGVyIGlzc3VlZCBjb2RlLiBEZWZhdWx0IDUuICovXG4gIG1heEF0dGVtcHRzPzogbnVtYmVyO1xuICAvKiogRGVsaXZlciBhIGNvZGUgdG8gYW4gYWRkcmVzcy4gV2lyZWQgYnkgdGhlIGFwcCB0byB0aGUgZW1haWwgcGx1Z2luLiAqL1xuICBzZW5kQ29kZT86IChlbWFpbDogc3RyaW5nLCBjb2RlOiBzdHJpbmcsIG9wdHM6IFNlbmRDb2RlT3B0aW9ucykgPT4gUHJvbWlzZTx2b2lkPjtcbn1cblxuLyoqIEJyYW5kaW5nL21lc3NhZ2luZyBwYXNzZWQgdG8ge0BsaW5rIEF1dGhHYXRlQ29uZmlnLnNlbmRDb2RlfS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VuZENvZGVPcHRpb25zIHtcbiAgc3ViamVjdDogc3RyaW5nO1xuICBicmFuZE5hbWU6IHN0cmluZztcbiAgbWVzc2FnZTogc3RyaW5nO1xuICAvKipcbiAgICogTGlmZXRpbWUgb2YgdGhlIGNvZGUgYmVpbmcgc2VudCwgaW4gc2Vjb25kcyAtIHRoZSBSRVNPTFZFRFxuICAgKiB7QGxpbmsgQXV0aEdhdGVDb25maWcuY29kZVR0bFNlY29uZHN9LCBzbyB0aGUgZW1haWwgY2FuIHN0YXRlIHRoZSByZWFsXG4gICAqIGV4cGlyeSAoXCJUaGlzIGNvZGUgZXhwaXJlcyBpbiAxMCBtaW51dGVzXCIpIGluc3RlYWQgb2YgYSB2YWd1ZSBcInNob3J0bHlcIlxuICAgKiB0aGF0IGRyaWZ0cyBmcm9tIHRoZSBjb25maWd1cmVkIFRUTC5cbiAgICovXG4gIGNvZGVUdGxTZWNvbmRzOiBudW1iZXI7XG59XG5cbi8qKiBSZXNvbHZlZCBnYXRlIGNvbmZpZyB3aXRoIGVudiBmYWxsYmFja3MgKyBkZWZhdWx0cyBhcHBsaWVkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZEF1dGhHYXRlQ29uZmlnIHtcbiAgYWxsb3c6IHN0cmluZ1tdO1xuICBzdWJqZWN0OiBzdHJpbmc7XG4gIGJyYW5kTmFtZTogc3RyaW5nO1xuICBtZXNzYWdlOiBzdHJpbmc7XG4gIHNlc3Npb25UdGxTZWNvbmRzOiBudW1iZXI7XG4gIGNvZGVUdGxTZWNvbmRzOiBudW1iZXI7XG4gIG1heEF0dGVtcHRzOiBudW1iZXI7XG59XG5cbmNvbnN0IERFRkFVTFRTID0ge1xuICBzdWJqZWN0OiBcIllvdXIgdmVyaWZpY2F0aW9uIGNvZGVcIixcbiAgLy8gVGhlIHJlcG8td2lkZSBicmFuZCBjb250ZXh0J3MgZGlzcGxheSBuYW1lLCBOT1QgYSBoYXJkY29kZWQgcHJvZHVjdCBzdHJpbmc6XG4gIC8vIHRoaXMgbmFtZSBpcyB3aGF0IHRoZSByZWNpcGllbnQgcmVhZHMgaW4gdGhlIGNvZGUgZW1haWwsIHNvIGl0IGhhcyB0byBiZSB0aGVcbiAgLy8gc2FtZSBpZGVudGl0eSB0aGUgcmVzdCBvZiB0aGUgYXBwIHByZXNlbnRzLiBBIGhvc3Qgd2l0aCBpdHMgb3duXG4gIC8vIGBicmFuZGluZy9icmFuZC55YW1sYCBvdmVycmlkZXMgaXQgYnkgcGFzc2luZyBgYnJhbmROYW1lYCAoc2VlXG4gIC8vIHtAbGluayBzdGFydEdhdGVBcHB9LCB3aGljaCByZXNvbHZlcyB0aGUgb24tZGlzayBjb250ZXh0KTsgdGhlIHNoYXJlZCBkZWZhdWx0XG4gIC8vIGlzIHRoZSBmYWxsYmFjayB3aGVuIG5vdGhpbmcgaXMgY29uZmlndXJlZC5cbiAgYnJhbmROYW1lOiBicmFuZC5kZWZhdWx0QnJhbmRDb250ZXh0Lm5hbWUsXG4gIG1lc3NhZ2U6IFwiWW91ciB2ZXJpZmljYXRpb24gY29kZSBpczpcIixcbiAgc2Vzc2lvblR0bFNlY29uZHM6IDQzMjAwLFxuICBjb2RlVHRsU2Vjb25kczogNjAwLFxuICBtYXhBdHRlbXB0czogNSxcbn07XG5cbi8qKiBNZXJnZSB7QGxpbmsgQXV0aEdhdGVDb25maWd9IG92ZXIgZW52IG92ZXIgZGVmYXVsdHMgaW50byBhIHJlc29sdmVkIGNvbmZpZy4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlQXV0aEdhdGVDb25maWcoY29uZmlnOiBBdXRoR2F0ZUNvbmZpZyk6IFJlc29sdmVkQXV0aEdhdGVDb25maWcge1xuICByZXR1cm4ge1xuICAgIC8vIEJvdGggc291cmNlcyBhcmUgdW5pb25lZCByYXRoZXIgdGhhbiBvbmUgb3ZlcnJpZGluZzogYSBkZXBsb3ltZW50LXdpZGVcbiAgICAvLyBUVU5ORUxfQVVUSF9BTExPVyBhbmQgYSBwZXItaW52b2NhdGlvbiBgLS1hbGxvd2Agc2hvdWxkIGJvdGggZ3JhbnQgYWNjZXNzLlxuICAgIC8vIEhlbmNlIGBwYXJzZUxpc3RgIG9uIGVhY2ggcmF0aGVyIHRoYW4gYGVudi5saXN0YCwgd2hpY2ggc3RvcHMgYXQgdGhlIGZpcnN0XG4gICAgLy8gc291cmNlIHRoYXQgeWllbGRzIGFueXRoaW5nLlxuICAgIGFsbG93OiBbLi4uc3RyaW5nLnBhcnNlTGlzdChjb25maWcuYWxsb3cpLCAuLi5zdHJpbmcucGFyc2VMaXN0KGVudi50ZXh0KEFMTE9XX0VOVikpXSxcbiAgICBzdWJqZWN0OiBlbnYuc3RyaW5nKGNvbmZpZy5zdWJqZWN0LCBTVUJKRUNUX0VOVikgPz8gREVGQVVMVFMuc3ViamVjdCxcbiAgICBicmFuZE5hbWU6IGVudi5zdHJpbmcoY29uZmlnLmJyYW5kTmFtZSwgQlJBTkRfTkFNRV9FTlYpID8/IERFRkFVTFRTLmJyYW5kTmFtZSxcbiAgICBtZXNzYWdlOiBlbnYuc3RyaW5nKGNvbmZpZy5tZXNzYWdlLCBNRVNTQUdFX0VOVikgPz8gREVGQVVMVFMubWVzc2FnZSxcbiAgICBzZXNzaW9uVHRsU2Vjb25kczogZW52LnBvc2l0aXZlSW50KFxuICAgICAgY29uZmlnLnNlc3Npb25UdGxTZWNvbmRzLFxuICAgICAgU0VTU0lPTl9UVExfRU5WLFxuICAgICAgREVGQVVMVFMuc2Vzc2lvblR0bFNlY29uZHMsXG4gICAgKSxcbiAgICBjb2RlVHRsU2Vjb25kczogZW52LnBvc2l0aXZlSW50KGNvbmZpZy5jb2RlVHRsU2Vjb25kcywgQ09ERV9UVExfRU5WLCBERUZBVUxUUy5jb2RlVHRsU2Vjb25kcyksXG4gICAgbWF4QXR0ZW1wdHM6IGNvbmZpZy5tYXhBdHRlbXB0cyA/PyBERUZBVUxUUy5tYXhBdHRlbXB0cyxcbiAgfTtcbn1cblxuLyoqIFRoZSBoYW5kbGVycyB0aGUgcHJveHkgY2FsbHMgaW4tcHJvY2VzcyAocmV0dXJuZWQgYnkge0BsaW5rIEF1dGhHYXRlUGx1Z2luLmV4cG9ydHN9KS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXV0aEdhdGVBcGkge1xuICAvKiogSGFuZGxlIGEgY29kZSByZXF1ZXN0LiBBbHdheXMgcmVzb2x2ZXMgYHsgb2s6IHRydWUgfWAgKGFudGktZW51bWVyYXRpb24pLiAqL1xuICByZXF1ZXN0KGVtYWlsOiBzdHJpbmcsIGlwOiBzdHJpbmcpOiBQcm9taXNlPHsgb2s6IHRydWU7IHJldHJ5QWZ0ZXI/OiBudW1iZXIgfT47XG4gIC8qKiBIYW5kbGUgYSBjb2RlIHZlcmlmaWNhdGlvbi4gT24gc3VjY2VzcyByZXR1cm5zIHRoZSBzZXNzaW9uIHRva2VuIHRvIGNvb2tpZS4gKi9cbiAgdmVyaWZ5KFxuICAgIGVtYWlsOiBzdHJpbmcsXG4gICAgY29kZTogc3RyaW5nLFxuICAgIGlwOiBzdHJpbmcsXG4gICk6IFByb21pc2U8eyBvazogYm9vbGVhbjsgdG9rZW4/OiBzdHJpbmc7IHJldHJ5QWZ0ZXI/OiBudW1iZXIgfT47XG4gIC8qKiBSZXNvbHZlIHRoZSBhdXRoZW50aWNhdGVkIGVtYWlsIGZvciBhIHNlc3Npb24gdG9rZW4sIG9yIHVuZGVmaW5lZC4gKi9cbiAgc2Vzc2lvbih0b2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+O1xuICAvKiogU2Vzc2lvbiBUVEwgaW4gc2Vjb25kcyAoZm9yIHRoZSBjb29raWUgTWF4LUFnZSkuICovXG4gIHJlYWRvbmx5IHNlc3Npb25UdGxTZWNvbmRzOiBudW1iZXI7XG4gIC8qKiBUaGUgZ2F0ZSBzdGF0dXMgcGF5bG9hZCAoYGVuYWJsZWRgIGlzIGFsd2F5cyB0cnVlIHdoZW4gdGhpcyBwbHVnaW4gcnVucykuICovXG4gIHN0YXR1cyh0b2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxBdXRoU3RhdHVzPjtcbn1cblxuLyoqIEFwcEtpdCBwbHVnaW4gb3duaW5nIHRoZSBlbWFpbC1PVFAgZ2F0ZSdzIGxvZ2ljIChubyBIVFRQIHJvdXRlczsgcHJveHktZHJpdmVuKS4gKi9cbmV4cG9ydCBjbGFzcyBBdXRoR2F0ZVBsdWdpbiBleHRlbmRzIFBsdWdpbjxBdXRoR2F0ZUNvbmZpZz4ge1xuICBzdGF0aWMgbWFuaWZlc3QgPSB7XG4gICAgbmFtZTogXCJhdXRoR2F0ZVwiLFxuICAgIGRpc3BsYXlOYW1lOiBcIkF1dGggR2F0ZVwiLFxuICAgIGRlc2NyaXB0aW9uOiBcIkVtYWlsIG9uZS10aW1lLXBhc3N3b3JkIGFjY2VzcyBnYXRlIGZvciBhIHB1YmxpYyB0dW5uZWwuXCIsXG4gICAgc3RhYmlsaXR5OiBcImJldGFcIixcbiAgICByZXNvdXJjZXM6IHsgcmVxdWlyZWQ6IFtdLCBvcHRpb25hbDogW10gfSxcbiAgfSBzYXRpc2ZpZXMgUGx1Z2luTWFuaWZlc3Q8XCJhdXRoR2F0ZVwiPjtcblxuICBwcml2YXRlIHJlc29sdmVkITogUmVzb2x2ZWRBdXRoR2F0ZUNvbmZpZztcbiAgcHJpdmF0ZSBjb2RlcyE6IENvZGVTdG9yZTtcbiAgLy8gUmVxdWVzdGluZyBhIGNvZGUgaXMgZW1haWwtc3BhbS1wcm9uZTsgdmVyaWZ5aW5nIGlzIGEgYnJ1dGUtZm9yY2Ugc3VyZmFjZS5cbiAgLy8gUGVyLWVtYWlsIEFORCBwZXItSVAgc28gbmVpdGhlciBheGlzIGFsb25lIGlzIGEgYnlwYXNzLlxuICBwcml2YXRlIHJlYWRvbmx5IHJlcXVlc3RMaW1pdGVyID0gbmV3IFJhdGVMaW1pdGVyKDUsIDE1ICogNjAgKiAxMDAwKTtcbiAgcHJpdmF0ZSByZWFkb25seSB2ZXJpZnlMaW1pdGVyID0gbmV3IFJhdGVMaW1pdGVyKDEwLCAxNSAqIDYwICogMTAwMCk7XG5cbiAgb3ZlcnJpZGUgYXN5bmMgc2V0dXAoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5yZXNvbHZlZCA9IHJlc29sdmVBdXRoR2F0ZUNvbmZpZyh0aGlzLmNvbmZpZyk7XG4gICAgdGhpcy5jb2RlcyA9IG5ldyBDb2RlU3RvcmUodGhpcy5yZXNvbHZlZC5jb2RlVHRsU2Vjb25kcywgdGhpcy5yZXNvbHZlZC5tYXhBdHRlbXB0cyk7XG4gICAgbG9nZ2VyLmluZm8oXCJyZWFkeVwiLCB7XG4gICAgICBwYXR0ZXJuczogdGhpcy5yZXNvbHZlZC5hbGxvdy5sZW5ndGgsXG4gICAgICBzZXNzaW9uVHRsU2Vjb25kczogdGhpcy5yZXNvbHZlZC5zZXNzaW9uVHRsU2Vjb25kcyxcbiAgICB9KTtcbiAgfVxuXG4gIG92ZXJyaWRlIGV4cG9ydHMoKTogQXV0aEdhdGVBcGkge1xuICAgIHJldHVybiB7XG4gICAgICBzZXNzaW9uVHRsU2Vjb25kczogdGhpcy5yZXNvbHZlZC5zZXNzaW9uVHRsU2Vjb25kcyxcbiAgICAgIHJlcXVlc3Q6IChlbWFpbCwgaXApID0+IHRoaXMuaGFuZGxlUmVxdWVzdChlbWFpbCwgaXApLFxuICAgICAgdmVyaWZ5OiAoZW1haWwsIGNvZGUsIGlwKSA9PiB0aGlzLmhhbmRsZVZlcmlmeShlbWFpbCwgY29kZSwgaXApLFxuICAgICAgc2Vzc2lvbjogKHRva2VuKSA9PiB2ZXJpZnlTZXNzaW9uKHRva2VuKSxcbiAgICAgIHN0YXR1czogYXN5bmMgKHRva2VuKSA9PiAoe1xuICAgICAgICBhdXRoZW50aWNhdGVkOiBCb29sZWFuKGF3YWl0IHZlcmlmeVNlc3Npb24odG9rZW4pKSxcbiAgICAgICAgZW1haWw6IChhd2FpdCB2ZXJpZnlTZXNzaW9uKHRva2VuKSkgPz8gdW5kZWZpbmVkLFxuICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgfSksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlUmVxdWVzdChcbiAgICBlbWFpbDogc3RyaW5nLFxuICAgIGlwOiBzdHJpbmcsXG4gICk6IFByb21pc2U8eyBvazogdHJ1ZTsgcmV0cnlBZnRlcj86IG51bWJlciB9PiB7XG4gICAgY29uc3QgYWRkcmVzcyA9IGVtYWlsLnRyaW0oKS50b0xvd2VyQ2FzZSgpO1xuICAgIGNvbnN0IGJ5SXAgPSB0aGlzLnJlcXVlc3RMaW1pdGVyLmhpdChgaXA6JHtpcH1gKTtcbiAgICBjb25zdCBieUVtYWlsID0gdGhpcy5yZXF1ZXN0TGltaXRlci5oaXQoYGVtYWlsOiR7YWRkcmVzc31gKTtcbiAgICBpZiAoIWJ5SXAuYWxsb3dlZCB8fCAhYnlFbWFpbC5hbGxvd2VkKSB7XG4gICAgICByZXR1cm4geyBvazogdHJ1ZSwgcmV0cnlBZnRlcjogYnlJcC5yZXRyeUFmdGVyID8/IGJ5RW1haWwucmV0cnlBZnRlciB9O1xuICAgIH1cbiAgICBpZiAobG9va3NMaWtlRW1haWwoYWRkcmVzcykgJiYgbWF0Y2hlc0FsbG93bGlzdChhZGRyZXNzLCB0aGlzLnJlc29sdmVkLmFsbG93KSkge1xuICAgICAgY29uc3QgY29kZSA9IGF3YWl0IHRoaXMuY29kZXMuaXNzdWUoYWRkcmVzcyk7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB0aGlzLmNvbmZpZy5zZW5kQ29kZT8uKGFkZHJlc3MsIGNvZGUsIHtcbiAgICAgICAgICBzdWJqZWN0OiB0aGlzLnJlc29sdmVkLnN1YmplY3QsXG4gICAgICAgICAgYnJhbmROYW1lOiB0aGlzLnJlc29sdmVkLmJyYW5kTmFtZSxcbiAgICAgICAgICBtZXNzYWdlOiB0aGlzLnJlc29sdmVkLm1lc3NhZ2UsXG4gICAgICAgICAgY29kZVR0bFNlY29uZHM6IHRoaXMucmVzb2x2ZWQuY29kZVR0bFNlY29uZHMsXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oXCJmYWlsZWQgdG8gc2VuZCBPVFAgZW1haWxcIiwgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHsgb2s6IHRydWUgfTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlVmVyaWZ5KFxuICAgIGVtYWlsOiBzdHJpbmcsXG4gICAgY29kZTogc3RyaW5nLFxuICAgIGlwOiBzdHJpbmcsXG4gICk6IFByb21pc2U8eyBvazogYm9vbGVhbjsgdG9rZW4/OiBzdHJpbmc7IHJldHJ5QWZ0ZXI/OiBudW1iZXIgfT4ge1xuICAgIGNvbnN0IGFkZHJlc3MgPSBlbWFpbC50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgICBjb25zdCBieUlwID0gdGhpcy52ZXJpZnlMaW1pdGVyLmhpdChgaXA6JHtpcH1gKTtcbiAgICBjb25zdCBieUVtYWlsID0gdGhpcy52ZXJpZnlMaW1pdGVyLmhpdChgZW1haWw6JHthZGRyZXNzfWApO1xuICAgIGlmICghYnlJcC5hbGxvd2VkIHx8ICFieUVtYWlsLmFsbG93ZWQpIHtcbiAgICAgIHJldHVybiB7IG9rOiBmYWxzZSwgcmV0cnlBZnRlcjogYnlJcC5yZXRyeUFmdGVyID8/IGJ5RW1haWwucmV0cnlBZnRlciB9O1xuICAgIH1cbiAgICBpZiAoKGF3YWl0IHRoaXMuY29kZXMudmVyaWZ5KGFkZHJlc3MsIGNvZGUudHJpbSgpKSkgIT09IFwib2tcIikgcmV0dXJuIHsgb2s6IGZhbHNlIH07XG4gICAgdGhpcy5yZXF1ZXN0TGltaXRlci5yZXNldChgZW1haWw6JHthZGRyZXNzfWApO1xuICAgIHRoaXMudmVyaWZ5TGltaXRlci5yZXNldChgZW1haWw6JHthZGRyZXNzfWApO1xuICAgIHJldHVybiB7IG9rOiB0cnVlLCB0b2tlbjogYXdhaXQgc2lnblNlc3Npb24oYWRkcmVzcywgdGhpcy5yZXNvbHZlZC5zZXNzaW9uVHRsU2Vjb25kcykgfTtcbiAgfVxufVxuXG4vKiogRmFjdG9yeTogYGF1dGhHYXRlKHsgYWxsb3csIHN1YmplY3QsIC4uLiB9KWAgZm9yIGFuIEFwcEtpdCBgcGx1Z2luc2AgYXJyYXkuICovXG5leHBvcnQgY29uc3QgYXV0aEdhdGUgPSB0b1BsdWdpbihBdXRoR2F0ZVBsdWdpbik7XG4iXX0=
|
|
138
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUE4QyxNQUFNLG9CQUFvQixDQUFDO0FBQ2xHLE9BQU8sRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVqRSxPQUFPLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEUsT0FBTyxFQUNMLFNBQVMsRUFDVCxjQUFjLEVBQ2QsWUFBWSxFQUNaLFdBQVcsRUFDWCxlQUFlLEVBQ2YsV0FBVyxHQUNaLE1BQU0sVUFBVSxDQUFDO0FBQ2xCLE9BQU8sRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNqRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxtQkFBbUIsRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVwRixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0FBa0Z6QyxNQUFNLFFBQVEsR0FBRztJQUNmLE9BQU8sRUFBRSx3QkFBd0I7SUFDakMsOEVBQThFO0lBQzlFLCtFQUErRTtJQUMvRSxrRUFBa0U7SUFDbEUsaUVBQWlFO0lBQ2pFLGdGQUFnRjtJQUNoRiw4Q0FBOEM7SUFDOUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJO0lBQ3pDLE9BQU8sRUFBRSw0QkFBNEI7SUFDckMsdUVBQXVFO0lBQ3ZFLGlCQUFpQixFQUFFLGVBQWU7SUFDbEMsY0FBYyxFQUFFLEdBQUc7SUFDbkIsV0FBVyxFQUFFLENBQUM7Q0FDZixD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LENBQUM7UUFDaEQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsU0FBUyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3RDLE9BQU8sRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVSxJQUFJLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUMxRSxDQUFDO1FBQ0QsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEtBQUssSUFBSTtZQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDbkYsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsU0FBUyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQzlDLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLFNBQVMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM3QyxPQUFPLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsTUFBTSxXQUFXLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDO0lBQzFGLENBQUM7O0FBR0gsa0ZBQWtGO0FBQ2xGLE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsY0FBYyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGBhdXRoR2F0ZSgpYCAtIHRoZSBBcHBLaXQgcGx1Z2luIGJlaGluZCB0aGUgdHVubmVsJ3MgZW1haWwtT1RQIGdhdGUuXG4gKlxuICogSXQgaGFzIE5PIHJvdXRlcyBvZiBpdHMgb3duOiB0aGUgdHVubmVsIFBST1hZIChub3QgYW4gSFRUUCBzZXJ2ZXIpIGNhbGxzIHRoZVxuICogaGFuZGxlcnMgdGhpcyBwbHVnaW4gZXhwb3NlcyB2aWEge0BsaW5rIEF1dGhHYXRlUGx1Z2luLmV4cG9ydHN9LiBUaGUgcGx1Z2luXG4gKiBvd25zIHRoZSBhbGxvdy1saXN0LCB0aGUgcGVyLWVtYWlsL3Blci1JUCByYXRlIGxpbWl0ZXJzLCB0aGUgQ2FjaGVNYW5hZ2VyLWJhY2tlZFxuICogb25lLXRpbWUtY29kZSBzdG9yZSwgYW5kIHRoZSBzZXNzaW9uIEpXVC4gYGNyZWF0ZUFwcGAgKHdpdGggbm8gYHNlcnZlcigpYCkgaXNcbiAqIHVzZWQgb25seSB0byBhdXRvLWluaXQgYENhY2hlTWFuYWdlcmAgKyBwcmltZSB0aGUgc2libGluZyBgZW1haWxgIHRyYW5zcG9ydDtcbiAqIHRoaXMgcGx1Z2luIGlzIHdoZXJlIHRoZSBnYXRlIGxvZ2ljIGxpdmVzLlxuICpcbiAqIE9wdGlvbnMgY29tZSBmcm9tIENMSSBmbGFncyBPUiBlbnYsIHdpdGggc2Vuc2libGUgZGVmYXVsdHMgLSBzZWVcbiAqIHtAbGluayByZXNvbHZlQXV0aEdhdGVDb25maWd9LiBUaGUgb25lIHJ1bnRpbWUgZGVwZW5kZW5jeSB0aGUgcGx1Z2luIGNhbid0XG4gKiByZXNvbHZlIGl0c2VsZiBpcyBIT1cgdG8gZW1haWwgdGhlIGNvZGUsIHNvIGl0IHRha2VzIGEgYHNlbmRDb2RlYCBjYWxsYmFjayB0aGVcbiAqIGFwcCB3aXJlcyB0byB0aGUgZW1haWwgcGx1Z2luLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBQbHVnaW4sIHRvUGx1Z2luLCB0eXBlIEJhc2VQbHVnaW5Db25maWcsIHR5cGUgUGx1Z2luTWFuaWZlc3QgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBicmFuZCwgZW52LCBsb2csIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgdHlwZSB7IEF1dGhTdGF0dXMgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtZW1haWxcIjtcbmltcG9ydCB7IGxvb2tzTGlrZUVtYWlsLCBtYXRjaGVzQWxsb3dsaXN0IH0gZnJvbSBcIi4vYWxsb3dsaXN0LnRzXCI7XG5pbXBvcnQge1xuICBBTExPV19FTlYsXG4gIEJSQU5EX05BTUVfRU5WLFxuICBDT0RFX1RUTF9FTlYsXG4gIE1FU1NBR0VfRU5WLFxuICBTRVNTSU9OX1RUTF9FTlYsXG4gIFNVQkpFQ1RfRU5WLFxufSBmcm9tIFwiLi9lbnYudHNcIjtcbmltcG9ydCB7IENvZGVTdG9yZSwgc2lnblNlc3Npb24sIHZlcmlmeVNlc3Npb24gfSBmcm9tIFwiLi9vdHAudHNcIjtcbmltcG9ydCB7IFJhdGVMaW1pdGVyIH0gZnJvbSBcIi4vcmF0ZS1saW1pdC50c1wiO1xuaW1wb3J0IHsgS0VZX1RUTF9TRUNPTkRTLCByZXNvbHZlU2Vzc2lvbkVwb2NoLCBzaWduaW5nS2V5IH0gZnJvbSBcIi4vc2lnbmluZy1rZXkudHNcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcInR1bm5lbDphdXRoXCIpO1xuXG4vKiogT3B0aW9ucyBmb3IgdGhlIHtAbGluayBhdXRoR2F0ZX0gcGx1Z2luIChhbGwgcmVzb2x2YWJsZSBmcm9tIGVudiAtIHNlZSBiZWxvdykuICovXG5leHBvcnQgaW50ZXJmYWNlIEF1dGhHYXRlQ29uZmlnIGV4dGVuZHMgQmFzZVBsdWdpbkNvbmZpZyB7XG4gIC8qKiBBbGxvdy1saXN0IHBhdHRlcm5zIChkb21haW4gLyBnbG9iIC8gYC9yZWdleC9gKS4gRW1wdHkgPSBhbGxvdyBub2JvZHkuIEVudiBUVU5ORUxfQVVUSF9BTExPVy4gKi9cbiAgYWxsb3c/OiBzdHJpbmcgfCBzdHJpbmdbXTtcbiAgLyoqXG4gICAqIFN1YmplY3QgbGluZSBmb3IgdGhlIGNvZGUgZW1haWwuIEVudiBUVU5ORUxfQVVUSF9TVUJKRUNULlxuICAgKlxuICAgKiBEZWZhdWx0cyB0byBcIllvdXIgdmVyaWZpY2F0aW9uIGNvZGVcIi4gVGhlIHdvcmRpbmcgb2YgdGhlIHN1YmplY3QgYW5kXG4gICAqIHtAbGluayBtZXNzYWdlfSBpcyBkZWxpYmVyYXRlbHkgdGhlIGNvbnZlbnRpb25hbCBwaHJhc2luZyByYXRoZXIgdGhhblxuICAgKiBhbnl0aGluZyBicmFuZGVkOiBpT1MsIEdtYWlsLCBPdXRsb29rLCBhbmQgQW5kcm9pZCBhbGwgZGV0ZWN0IGEgb25lLXRpbWVcbiAgICogY29kZSBmcm9tIHRoaXMgc2hhcGUgYW5kIG9mZmVyIHRvIGF1dG9maWxsIGl0LCBhbmQgYSBub3ZlbCBwaHJhc2luZyBpcyB3aGF0XG4gICAqIGJyZWFrcyB0aGF0IGRldGVjdGlvbi5cbiAgICovXG4gIHN1YmplY3Q/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBEaXNwbGF5IG5hbWUgdXNlZCBpbiB0aGUgY29kZSBlbWFpbCBjb3B5LiBFbnYgVFVOTkVMX0FVVEhfQlJBTkRfTkFNRS5cbiAgICpcbiAgICogRGVmYXVsdHMgdG8gdGhlIGJyYW5kIGNvbnRleHQncyBgbmFtZWAgLSB0aGUgYXBwJ3Mgb3duIGBicmFuZGluZy9icmFuZC55YW1sYFxuICAgKiB3aGVuIGl0IGhhcyBvbmUsIGVsc2UgdGhlIGRieC10b29scyBkZWZhdWx0LiBTZXQgdGhpcyBvbmx5IHRvIG92ZXJyaWRlIHRoZVxuICAgKiBicmFuZCBmb3IgdGhpcyBnYXRlLlxuICAgKi9cbiAgYnJhbmROYW1lPzogc3RyaW5nO1xuICAvKipcbiAgICogTGluZSBzaG93biBpbW1lZGlhdGVseSBhYm92ZSB0aGUgY29kZSBpbiB0aGUgZW1haWwuIEVudiBUVU5ORUxfQVVUSF9NRVNTQUdFLlxuICAgKlxuICAgKiBLZWVwIHRoZSBjb2RlIG9uIGl0cyBPV04gbGluZSBkaXJlY3RseSBhZnRlciB0aGlzIHRleHQgLSB0aGF0IGFkamFjZW5jeSBpc1xuICAgKiB3aGF0IHRoZSBwbGF0Zm9ybSBjb2RlLWRldGVjdGlvbiBoZXVyaXN0aWNzIGtleSBvbi5cbiAgICovXG4gIG1lc3NhZ2U/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBTZXNzaW9uIGxpZmV0aW1lIChzZWNvbmRzKS4gRW52IFRVTk5FTF9BVVRIX1NFU1NJT05fVFRMLiBEZWZhdWx0IDI1OTIwMDAgKDMwZCkuXG4gICAqXG4gICAqIE1hdGNoZWQgdG8gdGhlIGNhY2hlLWJhY2tlZCBzaWduaW5nIGtleSdzIG93biAzMC1kYXkgVFRMIChzZWVcbiAgICogYC4vc2lnbmluZy1rZXkudHNgKTogdGhlIGNvb2tpZSBhbmQgdGhlIGtleSB0aGF0IHZhbGlkYXRlcyBpdCBzaG91bGQgZXhwaXJlXG4gICAqIHRvZ2V0aGVyLCBvciBvbmUgc2lsZW50bHkgb3V0bGl2ZXMgdGhlIG90aGVyLlxuICAgKi9cbiAgc2Vzc2lvblR0bFNlY29uZHM/OiBudW1iZXI7XG4gIC8qKiBPbmUtdGltZS1jb2RlIGxpZmV0aW1lIChzZWNvbmRzKS4gRW52IFRVTk5FTF9BVVRIX0NPREVfVFRMLiBEZWZhdWx0IDYwMCAoMTBtKS4gKi9cbiAgY29kZVR0bFNlY29uZHM/OiBudW1iZXI7XG4gIC8qKiBNYXggdmVyaWZ5IGF0dGVtcHRzIHBlciBpc3N1ZWQgY29kZS4gRGVmYXVsdCA1LiAqL1xuICBtYXhBdHRlbXB0cz86IG51bWJlcjtcbiAgLyoqXG4gICAqIEZvcmNlLWNsZWFyIGRhdGU6IGV2ZXJ5IHNlc3Npb24gaXNzdWVkIEJFRk9SRSBpdCBzdG9wcyB2ZXJpZnlpbmcsIHNvIG1vdmluZ1xuICAgKiBpdCBmb3J3YXJkIHNpZ25zIGV2ZXJ5b25lIG91dC4gRW52IFRVTk5FTF9BVVRIX1NFU1NJT05fRVBPQ0guXG4gICAqXG4gICAqIEFueSBgRGF0ZWAtcGFyc2VhYmxlIHZhbHVlIChgMjAyNi0wOC0wMmAsIGFuIElTTyB0aW1lc3RhbXApIG9yIGJhcmUgZXBvY2hcbiAgICogc2Vjb25kcyAvIG1pbGxpcy4gVW5zZXQgbWVhbnMgbm8gY3V0b2ZmLlxuICAgKi9cbiAgc2Vzc2lvbkVwb2NoPzogc3RyaW5nIHwgbnVtYmVyIHwgRGF0ZTtcbiAgLyoqIERlbGl2ZXIgYSBjb2RlIHRvIGFuIGFkZHJlc3MuIFdpcmVkIGJ5IHRoZSBhcHAgdG8gdGhlIGVtYWlsIHBsdWdpbi4gKi9cbiAgc2VuZENvZGU/OiAoZW1haWw6IHN0cmluZywgY29kZTogc3RyaW5nLCBvcHRzOiBTZW5kQ29kZU9wdGlvbnMpID0+IFByb21pc2U8dm9pZD47XG59XG5cbi8qKiBCcmFuZGluZy9tZXNzYWdpbmcgcGFzc2VkIHRvIHtAbGluayBBdXRoR2F0ZUNvbmZpZy5zZW5kQ29kZX0uICovXG5leHBvcnQgaW50ZXJmYWNlIFNlbmRDb2RlT3B0aW9ucyB7XG4gIHN1YmplY3Q6IHN0cmluZztcbiAgYnJhbmROYW1lOiBzdHJpbmc7XG4gIG1lc3NhZ2U6IHN0cmluZztcbiAgLyoqXG4gICAqIExpZmV0aW1lIG9mIHRoZSBjb2RlIGJlaW5nIHNlbnQsIGluIHNlY29uZHMgLSB0aGUgUkVTT0xWRURcbiAgICoge0BsaW5rIEF1dGhHYXRlQ29uZmlnLmNvZGVUdGxTZWNvbmRzfSwgc28gdGhlIGVtYWlsIGNhbiBzdGF0ZSB0aGUgcmVhbFxuICAgKiBleHBpcnkgKFwiVGhpcyBjb2RlIGV4cGlyZXMgaW4gMTAgbWludXRlc1wiKSBpbnN0ZWFkIG9mIGEgdmFndWUgXCJzaG9ydGx5XCJcbiAgICogdGhhdCBkcmlmdHMgZnJvbSB0aGUgY29uZmlndXJlZCBUVEwuXG4gICAqL1xuICBjb2RlVHRsU2Vjb25kczogbnVtYmVyO1xufVxuXG4vKiogUmVzb2x2ZWQgZ2F0ZSBjb25maWcgd2l0aCBlbnYgZmFsbGJhY2tzICsgZGVmYXVsdHMgYXBwbGllZC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZWRBdXRoR2F0ZUNvbmZpZyB7XG4gIGFsbG93OiBzdHJpbmdbXTtcbiAgc3ViamVjdDogc3RyaW5nO1xuICBicmFuZE5hbWU6IHN0cmluZztcbiAgbWVzc2FnZTogc3RyaW5nO1xuICBzZXNzaW9uVHRsU2Vjb25kczogbnVtYmVyO1xuICBjb2RlVHRsU2Vjb25kczogbnVtYmVyO1xuICBtYXhBdHRlbXB0czogbnVtYmVyO1xuICAvKiogRm9yY2UtY2xlYXIgY3V0b2ZmIGluIGVwb2NoIG1zOyBgMGAgd2hlbiB1bnNldC4gKi9cbiAgc2Vzc2lvbkVwb2NoTXM6IG51bWJlcjtcbn1cblxuY29uc3QgREVGQVVMVFMgPSB7XG4gIHN1YmplY3Q6IFwiWW91ciB2ZXJpZmljYXRpb24gY29kZVwiLFxuICAvLyBUaGUgcmVwby13aWRlIGJyYW5kIGNvbnRleHQncyBkaXNwbGF5IG5hbWUsIE5PVCBhIGhhcmRjb2RlZCBwcm9kdWN0IHN0cmluZzpcbiAgLy8gdGhpcyBuYW1lIGlzIHdoYXQgdGhlIHJlY2lwaWVudCByZWFkcyBpbiB0aGUgY29kZSBlbWFpbCwgc28gaXQgaGFzIHRvIGJlIHRoZVxuICAvLyBzYW1lIGlkZW50aXR5IHRoZSByZXN0IG9mIHRoZSBhcHAgcHJlc2VudHMuIEEgaG9zdCB3aXRoIGl0cyBvd25cbiAgLy8gYGJyYW5kaW5nL2JyYW5kLnlhbWxgIG92ZXJyaWRlcyBpdCBieSBwYXNzaW5nIGBicmFuZE5hbWVgIChzZWVcbiAgLy8ge0BsaW5rIHN0YXJ0R2F0ZUFwcH0sIHdoaWNoIHJlc29sdmVzIHRoZSBvbi1kaXNrIGNvbnRleHQpOyB0aGUgc2hhcmVkIGRlZmF1bHRcbiAgLy8gaXMgdGhlIGZhbGxiYWNrIHdoZW4gbm90aGluZyBpcyBjb25maWd1cmVkLlxuICBicmFuZE5hbWU6IGJyYW5kLmRlZmF1bHRCcmFuZENvbnRleHQubmFtZSxcbiAgbWVzc2FnZTogXCJZb3VyIHZlcmlmaWNhdGlvbiBjb2RlIGlzOlwiLFxuICAvLyAzMCBkYXlzLCB0aGUgc2FtZSB3aW5kb3cgdGhlIGNhY2hlLWJhY2tlZCBzaWduaW5nIGtleSBpcyBzdG9yZWQgZm9yLlxuICBzZXNzaW9uVHRsU2Vjb25kczogS0VZX1RUTF9TRUNPTkRTLFxuICBjb2RlVHRsU2Vjb25kczogNjAwLFxuICBtYXhBdHRlbXB0czogNSxcbn07XG5cbi8qKiBNZXJnZSB7QGxpbmsgQXV0aEdhdGVDb25maWd9IG92ZXIgZW52IG92ZXIgZGVmYXVsdHMgaW50byBhIHJlc29sdmVkIGNvbmZpZy4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlQXV0aEdhdGVDb25maWcoY29uZmlnOiBBdXRoR2F0ZUNvbmZpZyk6IFJlc29sdmVkQXV0aEdhdGVDb25maWcge1xuICByZXR1cm4ge1xuICAgIC8vIEJvdGggc291cmNlcyBhcmUgdW5pb25lZCByYXRoZXIgdGhhbiBvbmUgb3ZlcnJpZGluZzogYSBkZXBsb3ltZW50LXdpZGVcbiAgICAvLyBUVU5ORUxfQVVUSF9BTExPVyBhbmQgYSBwZXItaW52b2NhdGlvbiBgLS1hbGxvd2Agc2hvdWxkIGJvdGggZ3JhbnQgYWNjZXNzLlxuICAgIC8vIEhlbmNlIGBwYXJzZUxpc3RgIG9uIGVhY2ggcmF0aGVyIHRoYW4gYGVudi5saXN0YCwgd2hpY2ggc3RvcHMgYXQgdGhlIGZpcnN0XG4gICAgLy8gc291cmNlIHRoYXQgeWllbGRzIGFueXRoaW5nLlxuICAgIGFsbG93OiBbLi4uc3RyaW5nLnBhcnNlTGlzdChjb25maWcuYWxsb3cpLCAuLi5zdHJpbmcucGFyc2VMaXN0KGVudi50ZXh0KEFMTE9XX0VOVikpXSxcbiAgICBzdWJqZWN0OiBlbnYuc3RyaW5nKGNvbmZpZy5zdWJqZWN0LCBTVUJKRUNUX0VOVikgPz8gREVGQVVMVFMuc3ViamVjdCxcbiAgICBicmFuZE5hbWU6IGVudi5zdHJpbmcoY29uZmlnLmJyYW5kTmFtZSwgQlJBTkRfTkFNRV9FTlYpID8/IERFRkFVTFRTLmJyYW5kTmFtZSxcbiAgICBtZXNzYWdlOiBlbnYuc3RyaW5nKGNvbmZpZy5tZXNzYWdlLCBNRVNTQUdFX0VOVikgPz8gREVGQVVMVFMubWVzc2FnZSxcbiAgICBzZXNzaW9uVHRsU2Vjb25kczogZW52LnBvc2l0aXZlSW50KFxuICAgICAgY29uZmlnLnNlc3Npb25UdGxTZWNvbmRzLFxuICAgICAgU0VTU0lPTl9UVExfRU5WLFxuICAgICAgREVGQVVMVFMuc2Vzc2lvblR0bFNlY29uZHMsXG4gICAgKSxcbiAgICBjb2RlVHRsU2Vjb25kczogZW52LnBvc2l0aXZlSW50KGNvbmZpZy5jb2RlVHRsU2Vjb25kcywgQ09ERV9UVExfRU5WLCBERUZBVUxUUy5jb2RlVHRsU2Vjb25kcyksXG4gICAgbWF4QXR0ZW1wdHM6IGNvbmZpZy5tYXhBdHRlbXB0cyA/PyBERUZBVUxUUy5tYXhBdHRlbXB0cyxcbiAgICBzZXNzaW9uRXBvY2hNczogcmVzb2x2ZVNlc3Npb25FcG9jaChjb25maWcuc2Vzc2lvbkVwb2NoKSxcbiAgfTtcbn1cblxuLyoqIFRoZSBoYW5kbGVycyB0aGUgcHJveHkgY2FsbHMgaW4tcHJvY2VzcyAocmV0dXJuZWQgYnkge0BsaW5rIEF1dGhHYXRlUGx1Z2luLmV4cG9ydHN9KS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXV0aEdhdGVBcGkge1xuICAvKiogSGFuZGxlIGEgY29kZSByZXF1ZXN0LiBBbHdheXMgcmVzb2x2ZXMgYHsgb2s6IHRydWUgfWAgKGFudGktZW51bWVyYXRpb24pLiAqL1xuICByZXF1ZXN0KGVtYWlsOiBzdHJpbmcsIGlwOiBzdHJpbmcpOiBQcm9taXNlPHsgb2s6IHRydWU7IHJldHJ5QWZ0ZXI/OiBudW1iZXIgfT47XG4gIC8qKiBIYW5kbGUgYSBjb2RlIHZlcmlmaWNhdGlvbi4gT24gc3VjY2VzcyByZXR1cm5zIHRoZSBzZXNzaW9uIHRva2VuIHRvIGNvb2tpZS4gKi9cbiAgdmVyaWZ5KFxuICAgIGVtYWlsOiBzdHJpbmcsXG4gICAgY29kZTogc3RyaW5nLFxuICAgIGlwOiBzdHJpbmcsXG4gICk6IFByb21pc2U8eyBvazogYm9vbGVhbjsgdG9rZW4/OiBzdHJpbmc7IHJldHJ5QWZ0ZXI/OiBudW1iZXIgfT47XG4gIC8qKiBSZXNvbHZlIHRoZSBhdXRoZW50aWNhdGVkIGVtYWlsIGZvciBhIHNlc3Npb24gdG9rZW4sIG9yIHVuZGVmaW5lZC4gKi9cbiAgc2Vzc2lvbih0b2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+O1xuICAvKiogU2Vzc2lvbiBUVEwgaW4gc2Vjb25kcyAoZm9yIHRoZSBjb29raWUgTWF4LUFnZSkuICovXG4gIHJlYWRvbmx5IHNlc3Npb25UdGxTZWNvbmRzOiBudW1iZXI7XG4gIC8qKiBUaGUgZ2F0ZSBzdGF0dXMgcGF5bG9hZCAoYGVuYWJsZWRgIGlzIGFsd2F5cyB0cnVlIHdoZW4gdGhpcyBwbHVnaW4gcnVucykuICovXG4gIHN0YXR1cyh0b2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxBdXRoU3RhdHVzPjtcbn1cblxuLyoqIEFwcEtpdCBwbHVnaW4gb3duaW5nIHRoZSBlbWFpbC1PVFAgZ2F0ZSdzIGxvZ2ljIChubyBIVFRQIHJvdXRlczsgcHJveHktZHJpdmVuKS4gKi9cbmV4cG9ydCBjbGFzcyBBdXRoR2F0ZVBsdWdpbiBleHRlbmRzIFBsdWdpbjxBdXRoR2F0ZUNvbmZpZz4ge1xuICBzdGF0aWMgbWFuaWZlc3QgPSB7XG4gICAgbmFtZTogXCJhdXRoR2F0ZVwiLFxuICAgIGRpc3BsYXlOYW1lOiBcIkF1dGggR2F0ZVwiLFxuICAgIGRlc2NyaXB0aW9uOiBcIkVtYWlsIG9uZS10aW1lLXBhc3N3b3JkIGFjY2VzcyBnYXRlIGZvciBhIHB1YmxpYyB0dW5uZWwuXCIsXG4gICAgc3RhYmlsaXR5OiBcImJldGFcIixcbiAgICByZXNvdXJjZXM6IHsgcmVxdWlyZWQ6IFtdLCBvcHRpb25hbDogW10gfSxcbiAgfSBzYXRpc2ZpZXMgUGx1Z2luTWFuaWZlc3Q8XCJhdXRoR2F0ZVwiPjtcblxuICBwcml2YXRlIHJlc29sdmVkITogUmVzb2x2ZWRBdXRoR2F0ZUNvbmZpZztcbiAgcHJpdmF0ZSBjb2RlcyE6IENvZGVTdG9yZTtcbiAgLy8gUmVxdWVzdGluZyBhIGNvZGUgaXMgZW1haWwtc3BhbS1wcm9uZTsgdmVyaWZ5aW5nIGlzIGEgYnJ1dGUtZm9yY2Ugc3VyZmFjZS5cbiAgLy8gUGVyLWVtYWlsIEFORCBwZXItSVAgc28gbmVpdGhlciBheGlzIGFsb25lIGlzIGEgYnlwYXNzLlxuICBwcml2YXRlIHJlYWRvbmx5IHJlcXVlc3RMaW1pdGVyID0gbmV3IFJhdGVMaW1pdGVyKDUsIDE1ICogNjAgKiAxMDAwKTtcbiAgcHJpdmF0ZSByZWFkb25seSB2ZXJpZnlMaW1pdGVyID0gbmV3IFJhdGVMaW1pdGVyKDEwLCAxNSAqIDYwICogMTAwMCk7XG5cbiAgb3ZlcnJpZGUgYXN5bmMgc2V0dXAoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5yZXNvbHZlZCA9IHJlc29sdmVBdXRoR2F0ZUNvbmZpZyh0aGlzLmNvbmZpZyk7XG4gICAgdGhpcy5jb2RlcyA9IG5ldyBDb2RlU3RvcmUodGhpcy5yZXNvbHZlZC5jb2RlVHRsU2Vjb25kcywgdGhpcy5yZXNvbHZlZC5tYXhBdHRlbXB0cyk7XG4gICAgLy8gUmVzb2x2ZSB0aGUgc2lnbmluZyBrZXkgSEVSRSByYXRoZXIgdGhhbiBsYXppbHkgb24gdGhlIGZpcnN0IHNpZ24taW4sIHNvIGFcbiAgICAvLyBjYWNoZSB0aGF0IGNhbm5vdCBob2xkIGl0IChhbmQgdGhlIHJlc3VsdGluZyBcInNlc3Npb25zIHdvbid0IHN1cnZpdmUgYVxuICAgIC8vIHJlc3RhcnRcIiB3YXJuaW5nKSBzaG93cyB1cCBpbiB0aGUgc3RhcnR1cCBsb2csIG5vdCBob3VycyBsYXRlci5cbiAgICBjb25zdCB7IGVwb2NoTXMgfSA9IGF3YWl0IHNpZ25pbmdLZXkodGhpcy5yZXNvbHZlZC5zZXNzaW9uRXBvY2hNcyk7XG4gICAgbG9nZ2VyLmluZm8oXCJyZWFkeVwiLCB7XG4gICAgICBwYXR0ZXJuczogdGhpcy5yZXNvbHZlZC5hbGxvdy5sZW5ndGgsXG4gICAgICBzZXNzaW9uVHRsU2Vjb25kczogdGhpcy5yZXNvbHZlZC5zZXNzaW9uVHRsU2Vjb25kcyxcbiAgICAgIC4uLihlcG9jaE1zID4gMCA/IHsgc2Vzc2lvbkVwb2NoOiBuZXcgRGF0ZShlcG9jaE1zKS50b0lTT1N0cmluZygpIH0gOiB7fSksXG4gICAgfSk7XG4gIH1cblxuICBvdmVycmlkZSBleHBvcnRzKCk6IEF1dGhHYXRlQXBpIHtcbiAgICByZXR1cm4ge1xuICAgICAgc2Vzc2lvblR0bFNlY29uZHM6IHRoaXMucmVzb2x2ZWQuc2Vzc2lvblR0bFNlY29uZHMsXG4gICAgICByZXF1ZXN0OiAoZW1haWwsIGlwKSA9PiB0aGlzLmhhbmRsZVJlcXVlc3QoZW1haWwsIGlwKSxcbiAgICAgIHZlcmlmeTogKGVtYWlsLCBjb2RlLCBpcCkgPT4gdGhpcy5oYW5kbGVWZXJpZnkoZW1haWwsIGNvZGUsIGlwKSxcbiAgICAgIHNlc3Npb246ICh0b2tlbikgPT4gdmVyaWZ5U2Vzc2lvbih0b2tlbiksXG4gICAgICBzdGF0dXM6IGFzeW5jICh0b2tlbikgPT4gKHtcbiAgICAgICAgYXV0aGVudGljYXRlZDogQm9vbGVhbihhd2FpdCB2ZXJpZnlTZXNzaW9uKHRva2VuKSksXG4gICAgICAgIGVtYWlsOiAoYXdhaXQgdmVyaWZ5U2Vzc2lvbih0b2tlbikpID8/IHVuZGVmaW5lZCxcbiAgICAgICAgZW5hYmxlZDogdHJ1ZSxcbiAgICAgIH0pLFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGhhbmRsZVJlcXVlc3QoXG4gICAgZW1haWw6IHN0cmluZyxcbiAgICBpcDogc3RyaW5nLFxuICApOiBQcm9taXNlPHsgb2s6IHRydWU7IHJldHJ5QWZ0ZXI/OiBudW1iZXIgfT4ge1xuICAgIGNvbnN0IGFkZHJlc3MgPSBlbWFpbC50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgICBjb25zdCBieUlwID0gdGhpcy5yZXF1ZXN0TGltaXRlci5oaXQoYGlwOiR7aXB9YCk7XG4gICAgY29uc3QgYnlFbWFpbCA9IHRoaXMucmVxdWVzdExpbWl0ZXIuaGl0KGBlbWFpbDoke2FkZHJlc3N9YCk7XG4gICAgaWYgKCFieUlwLmFsbG93ZWQgfHwgIWJ5RW1haWwuYWxsb3dlZCkge1xuICAgICAgcmV0dXJuIHsgb2s6IHRydWUsIHJldHJ5QWZ0ZXI6IGJ5SXAucmV0cnlBZnRlciA/PyBieUVtYWlsLnJldHJ5QWZ0ZXIgfTtcbiAgICB9XG4gICAgaWYgKGxvb2tzTGlrZUVtYWlsKGFkZHJlc3MpICYmIG1hdGNoZXNBbGxvd2xpc3QoYWRkcmVzcywgdGhpcy5yZXNvbHZlZC5hbGxvdykpIHtcbiAgICAgIGNvbnN0IGNvZGUgPSBhd2FpdCB0aGlzLmNvZGVzLmlzc3VlKGFkZHJlc3MpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25maWcuc2VuZENvZGU/LihhZGRyZXNzLCBjb2RlLCB7XG4gICAgICAgICAgc3ViamVjdDogdGhpcy5yZXNvbHZlZC5zdWJqZWN0LFxuICAgICAgICAgIGJyYW5kTmFtZTogdGhpcy5yZXNvbHZlZC5icmFuZE5hbWUsXG4gICAgICAgICAgbWVzc2FnZTogdGhpcy5yZXNvbHZlZC5tZXNzYWdlLFxuICAgICAgICAgIGNvZGVUdGxTZWNvbmRzOiB0aGlzLnJlc29sdmVkLmNvZGVUdGxTZWNvbmRzLFxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKFwiZmFpbGVkIHRvIHNlbmQgT1RQIGVtYWlsXCIsIHsgZXJyb3IgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7IG9rOiB0cnVlIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGhhbmRsZVZlcmlmeShcbiAgICBlbWFpbDogc3RyaW5nLFxuICAgIGNvZGU6IHN0cmluZyxcbiAgICBpcDogc3RyaW5nLFxuICApOiBQcm9taXNlPHsgb2s6IGJvb2xlYW47IHRva2VuPzogc3RyaW5nOyByZXRyeUFmdGVyPzogbnVtYmVyIH0+IHtcbiAgICBjb25zdCBhZGRyZXNzID0gZW1haWwudHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3QgYnlJcCA9IHRoaXMudmVyaWZ5TGltaXRlci5oaXQoYGlwOiR7aXB9YCk7XG4gICAgY29uc3QgYnlFbWFpbCA9IHRoaXMudmVyaWZ5TGltaXRlci5oaXQoYGVtYWlsOiR7YWRkcmVzc31gKTtcbiAgICBpZiAoIWJ5SXAuYWxsb3dlZCB8fCAhYnlFbWFpbC5hbGxvd2VkKSB7XG4gICAgICByZXR1cm4geyBvazogZmFsc2UsIHJldHJ5QWZ0ZXI6IGJ5SXAucmV0cnlBZnRlciA/PyBieUVtYWlsLnJldHJ5QWZ0ZXIgfTtcbiAgICB9XG4gICAgaWYgKChhd2FpdCB0aGlzLmNvZGVzLnZlcmlmeShhZGRyZXNzLCBjb2RlLnRyaW0oKSkpICE9PSBcIm9rXCIpIHJldHVybiB7IG9rOiBmYWxzZSB9O1xuICAgIHRoaXMucmVxdWVzdExpbWl0ZXIucmVzZXQoYGVtYWlsOiR7YWRkcmVzc31gKTtcbiAgICB0aGlzLnZlcmlmeUxpbWl0ZXIucmVzZXQoYGVtYWlsOiR7YWRkcmVzc31gKTtcbiAgICByZXR1cm4geyBvazogdHJ1ZSwgdG9rZW46IGF3YWl0IHNpZ25TZXNzaW9uKGFkZHJlc3MsIHRoaXMucmVzb2x2ZWQuc2Vzc2lvblR0bFNlY29uZHMpIH07XG4gIH1cbn1cblxuLyoqIEZhY3Rvcnk6IGBhdXRoR2F0ZSh7IGFsbG93LCBzdWJqZWN0LCAuLi4gfSlgIGZvciBhbiBBcHBLaXQgYHBsdWdpbnNgIGFycmF5LiAqL1xuZXhwb3J0IGNvbnN0IGF1dGhHYXRlID0gdG9QbHVnaW4oQXV0aEdhdGVQbHVnaW4pO1xuIl19
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gate's HS256 session-signing key, persisted in AppKit's cache.
|
|
3
|
+
*
|
|
4
|
+
* The key decides whether a session COOKIE still verifies. Before this module the
|
|
5
|
+
* fallback was `randomBytes(32)` per process, so every restart invalidated every
|
|
6
|
+
* outstanding cookie and each signed-in user had to request a new code - painful
|
|
7
|
+
* for a tunnel, which restarts whenever the app it wraps does. Now the key is
|
|
8
|
+
* stored in the cache AppKit already configured (memory, or Lakebase when the
|
|
9
|
+
* host wires a persistent `CacheStorage`), so with persistent storage a restart
|
|
10
|
+
* keeps sessions alive for {@link KEY_TTL_SECONDS}.
|
|
11
|
+
*
|
|
12
|
+
* An explicitly configured `TUNNEL_AUTH_JWT_SECRET` still wins outright. That is
|
|
13
|
+
* the right answer for a fleet: an operator-held secret needs no shared cache and
|
|
14
|
+
* no convergence, and it survives a cache flush.
|
|
15
|
+
*
|
|
16
|
+
* ## get / generate / get
|
|
17
|
+
*
|
|
18
|
+
* Two instances booting at once both miss, so both would generate - and the loser
|
|
19
|
+
* would sign cookies with a key the winner rejects. Resolution is a re-READ after
|
|
20
|
+
* the write: whatever the cache holds afterwards is the key everyone adopts, so
|
|
21
|
+
* the instances converge on ONE value instead of trusting the one they minted.
|
|
22
|
+
* (`set` is not conditional in the cache API - there is no `setnx` to lean on -
|
|
23
|
+
* and this runs once per process, so the extra round-trip is free.)
|
|
24
|
+
*
|
|
25
|
+
* A pathological interleave can still cost a key: if A writes between B's write
|
|
26
|
+
* and B's re-read, B adopts A's key while A adopts its own. The cost is bounded -
|
|
27
|
+
* a cookie minted in that window fails to verify and the holder signs in again -
|
|
28
|
+
* and it cannot produce a key one instance TRUSTS but another rejects for longer
|
|
29
|
+
* than the window itself. Set `TUNNEL_AUTH_JWT_SECRET` to remove the race
|
|
30
|
+
* entirely.
|
|
31
|
+
*
|
|
32
|
+
* ## Forcing every session to end
|
|
33
|
+
*
|
|
34
|
+
* {@link resolveSessionEpoch} reads a date from `TUNNEL_AUTH_SESSION_EPOCH` (or
|
|
35
|
+
* `--session-epoch`), and that date is part of the cache KEY. Moving it forward
|
|
36
|
+
* makes every prior key unreachable, so every cookie signed against it stops
|
|
37
|
+
* verifying - the log-everyone-out switch, without having to find and flush a
|
|
38
|
+
* cache entry. The epoch is also asserted against each token's `iat`, so a
|
|
39
|
+
* cookie that predates it is refused even if it was signed with the key that is
|
|
40
|
+
* somehow still current.
|
|
41
|
+
*
|
|
42
|
+
* @module
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* How long a cached signing key lives: 30 days.
|
|
46
|
+
*
|
|
47
|
+
* This is the ceiling on how long a session cookie can stay valid across
|
|
48
|
+
* restarts, so it is deliberately >= the default session TTL - a key that expired
|
|
49
|
+
* before the cookies it signed would log everyone out for no reason.
|
|
50
|
+
*/
|
|
51
|
+
export declare const KEY_TTL_SECONDS: number;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the force-clear epoch as epoch MILLISECONDS, or `0` when unset.
|
|
54
|
+
*
|
|
55
|
+
* Accepts anything `Date` parses (`2026-08-02`, an ISO timestamp) plus a bare
|
|
56
|
+
* epoch-seconds / epoch-millis number, so a value pasted from `date +%s` works.
|
|
57
|
+
* An UNPARSEABLE value is ignored with a warning rather than throwing: this is
|
|
58
|
+
* the switch that logs a fleet back in, and failing to boot over a typo is worse
|
|
59
|
+
* than not rotating.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveSessionEpoch(configured?: string | number | Date): number;
|
|
62
|
+
/** A resolved key plus the epoch it is scoped to. */
|
|
63
|
+
export interface SigningKey {
|
|
64
|
+
key: Uint8Array;
|
|
65
|
+
/** Force-clear epoch in ms; `0` when unset. Tokens older than this are refused. */
|
|
66
|
+
epochMs: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The signing key for this gate, resolved once per process.
|
|
70
|
+
*
|
|
71
|
+
* Resolved ONCE, so `configuredEpoch` is honoured only on the first call - the
|
|
72
|
+
* plugin's `setup()` passes its resolved value there, before any request can
|
|
73
|
+
* reach the lazy path.
|
|
74
|
+
*
|
|
75
|
+
* `TUNNEL_AUTH_JWT_SECRET` when set, else the cache-backed key. A cache that is
|
|
76
|
+
* unavailable degrades to an ephemeral per-process key (the previous behaviour)
|
|
77
|
+
* rather than refusing to sign: the key only validates an ALREADY-issued session,
|
|
78
|
+
* so losing it costs sessions, never admission - a caller still needs a code
|
|
79
|
+
* delivered to an allow-listed address.
|
|
80
|
+
*/
|
|
81
|
+
export declare function signingKey(configuredEpoch?: string | number | Date): Promise<SigningKey>;
|
|
82
|
+
/** Reset the per-process key (tests, or after changing the env in-process). */
|
|
83
|
+
export declare function resetSigningKey(): void;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gate's HS256 session-signing key, persisted in AppKit's cache.
|
|
3
|
+
*
|
|
4
|
+
* The key decides whether a session COOKIE still verifies. Before this module the
|
|
5
|
+
* fallback was `randomBytes(32)` per process, so every restart invalidated every
|
|
6
|
+
* outstanding cookie and each signed-in user had to request a new code - painful
|
|
7
|
+
* for a tunnel, which restarts whenever the app it wraps does. Now the key is
|
|
8
|
+
* stored in the cache AppKit already configured (memory, or Lakebase when the
|
|
9
|
+
* host wires a persistent `CacheStorage`), so with persistent storage a restart
|
|
10
|
+
* keeps sessions alive for {@link KEY_TTL_SECONDS}.
|
|
11
|
+
*
|
|
12
|
+
* An explicitly configured `TUNNEL_AUTH_JWT_SECRET` still wins outright. That is
|
|
13
|
+
* the right answer for a fleet: an operator-held secret needs no shared cache and
|
|
14
|
+
* no convergence, and it survives a cache flush.
|
|
15
|
+
*
|
|
16
|
+
* ## get / generate / get
|
|
17
|
+
*
|
|
18
|
+
* Two instances booting at once both miss, so both would generate - and the loser
|
|
19
|
+
* would sign cookies with a key the winner rejects. Resolution is a re-READ after
|
|
20
|
+
* the write: whatever the cache holds afterwards is the key everyone adopts, so
|
|
21
|
+
* the instances converge on ONE value instead of trusting the one they minted.
|
|
22
|
+
* (`set` is not conditional in the cache API - there is no `setnx` to lean on -
|
|
23
|
+
* and this runs once per process, so the extra round-trip is free.)
|
|
24
|
+
*
|
|
25
|
+
* A pathological interleave can still cost a key: if A writes between B's write
|
|
26
|
+
* and B's re-read, B adopts A's key while A adopts its own. The cost is bounded -
|
|
27
|
+
* a cookie minted in that window fails to verify and the holder signs in again -
|
|
28
|
+
* and it cannot produce a key one instance TRUSTS but another rejects for longer
|
|
29
|
+
* than the window itself. Set `TUNNEL_AUTH_JWT_SECRET` to remove the race
|
|
30
|
+
* entirely.
|
|
31
|
+
*
|
|
32
|
+
* ## Forcing every session to end
|
|
33
|
+
*
|
|
34
|
+
* {@link resolveSessionEpoch} reads a date from `TUNNEL_AUTH_SESSION_EPOCH` (or
|
|
35
|
+
* `--session-epoch`), and that date is part of the cache KEY. Moving it forward
|
|
36
|
+
* makes every prior key unreachable, so every cookie signed against it stops
|
|
37
|
+
* verifying - the log-everyone-out switch, without having to find and flush a
|
|
38
|
+
* cache entry. The epoch is also asserted against each token's `iat`, so a
|
|
39
|
+
* cookie that predates it is refused even if it was signed with the key that is
|
|
40
|
+
* somehow still current.
|
|
41
|
+
*
|
|
42
|
+
* @module
|
|
43
|
+
*/
|
|
44
|
+
import { randomBytes } from "node:crypto";
|
|
45
|
+
import { CacheManager } from "@databricks/appkit";
|
|
46
|
+
import { env, log } from "@dbx-tools/shared-core";
|
|
47
|
+
import { JWT_SECRET_ENV, SESSION_EPOCH_ENV } from "./env.js";
|
|
48
|
+
const logger = log.logger("tunnel:signing-key");
|
|
49
|
+
/**
|
|
50
|
+
* How long a cached signing key lives: 30 days.
|
|
51
|
+
*
|
|
52
|
+
* This is the ceiling on how long a session cookie can stay valid across
|
|
53
|
+
* restarts, so it is deliberately >= the default session TTL - a key that expired
|
|
54
|
+
* before the cookies it signed would log everyone out for no reason.
|
|
55
|
+
*/
|
|
56
|
+
export const KEY_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
57
|
+
/** Cache-key prefix for the signing key, namespaced away from other cache use. */
|
|
58
|
+
const KEY_PREFIX = "tunnel:auth:signing-key:";
|
|
59
|
+
/** Bytes of entropy in a generated key (256-bit, matching HS256's hash width). */
|
|
60
|
+
const KEY_BYTES = 32;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the force-clear epoch as epoch MILLISECONDS, or `0` when unset.
|
|
63
|
+
*
|
|
64
|
+
* Accepts anything `Date` parses (`2026-08-02`, an ISO timestamp) plus a bare
|
|
65
|
+
* epoch-seconds / epoch-millis number, so a value pasted from `date +%s` works.
|
|
66
|
+
* An UNPARSEABLE value is ignored with a warning rather than throwing: this is
|
|
67
|
+
* the switch that logs a fleet back in, and failing to boot over a typo is worse
|
|
68
|
+
* than not rotating.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveSessionEpoch(configured) {
|
|
71
|
+
const raw = configured ?? env.text(SESSION_EPOCH_ENV) ?? undefined;
|
|
72
|
+
if (raw === undefined || raw === null || raw === "")
|
|
73
|
+
return 0;
|
|
74
|
+
return clampToPast(parseEpoch(raw));
|
|
75
|
+
}
|
|
76
|
+
/** Parse the accepted epoch spellings to ms, or `0` when unusable. */
|
|
77
|
+
function parseEpoch(raw) {
|
|
78
|
+
if (raw instanceof Date)
|
|
79
|
+
return Number.isNaN(raw.getTime()) ? 0 : raw.getTime();
|
|
80
|
+
const text = String(raw).trim();
|
|
81
|
+
// A bare number is a timestamp, not a date string: `Date.parse("1785...")`
|
|
82
|
+
// would read it as a YEAR. Values below ~1e11 are seconds (any millis
|
|
83
|
+
// timestamp since 1973 is larger), which is what `date +%s` prints.
|
|
84
|
+
if (/^\d+$/.test(text)) {
|
|
85
|
+
const numeric = Number(text);
|
|
86
|
+
return numeric < 1e11 ? numeric * 1000 : numeric;
|
|
87
|
+
}
|
|
88
|
+
const parsed = Date.parse(text);
|
|
89
|
+
if (Number.isNaN(parsed)) {
|
|
90
|
+
logger.warn(`ignoring unparseable ${env.name(SESSION_EPOCH_ENV)}`, { value: text });
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Hold the cutoff at "now", because a FUTURE epoch would refuse the sessions it
|
|
97
|
+
* is about to mint as well as the old ones - an app nobody can sign in to, from a
|
|
98
|
+
* mistyped year, with the fix hidden behind understanding this flag. Clamped, a
|
|
99
|
+
* future date means what an operator setting it always meant: clear everything
|
|
100
|
+
* outstanding, then carry on.
|
|
101
|
+
*/
|
|
102
|
+
function clampToPast(epochMs) {
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
if (epochMs <= now)
|
|
105
|
+
return epochMs;
|
|
106
|
+
logger.warn(`${env.name(SESSION_EPOCH_ENV)} is in the future - clamping to now`, {
|
|
107
|
+
configured: new Date(epochMs).toISOString(),
|
|
108
|
+
});
|
|
109
|
+
return now;
|
|
110
|
+
}
|
|
111
|
+
/** The cache key for one epoch, so moving the epoch orphans every earlier key. */
|
|
112
|
+
function cacheKey(epochMs) {
|
|
113
|
+
return `${KEY_PREFIX}${epochMs}`;
|
|
114
|
+
}
|
|
115
|
+
function decode(stored) {
|
|
116
|
+
return new Uint8Array(Buffer.from(stored.secret, "base64url"));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Load the signing key for `epochMs` from the cache, minting and storing one when
|
|
120
|
+
* absent. See the module docs for why the write is followed by a re-read.
|
|
121
|
+
*/
|
|
122
|
+
async function loadFromCache(epochMs) {
|
|
123
|
+
const cache = CacheManager.getInstanceSync();
|
|
124
|
+
const key = cacheKey(epochMs);
|
|
125
|
+
const existing = await cache.get(key);
|
|
126
|
+
if (existing?.secret) {
|
|
127
|
+
logger.info("reusing cached signing key", { createdAt: existing.createdAt });
|
|
128
|
+
return decode(existing);
|
|
129
|
+
}
|
|
130
|
+
const minted = {
|
|
131
|
+
secret: Buffer.from(randomBytes(KEY_BYTES)).toString("base64url"),
|
|
132
|
+
createdAt: new Date().toISOString(),
|
|
133
|
+
};
|
|
134
|
+
await cache.set(key, minted, { ttl: KEY_TTL_SECONDS });
|
|
135
|
+
// Re-read rather than trusting `minted`: another instance that raced this boot
|
|
136
|
+
// may have written first, and adopting whatever is stored is what makes the two
|
|
137
|
+
// converge on one key.
|
|
138
|
+
const settled = (await cache.get(key)) ?? minted;
|
|
139
|
+
logger.info("stored a new signing key", {
|
|
140
|
+
createdAt: settled.createdAt,
|
|
141
|
+
ttlSeconds: KEY_TTL_SECONDS,
|
|
142
|
+
adopted: settled.createdAt === minted.createdAt ? "own" : "concurrent-instance",
|
|
143
|
+
});
|
|
144
|
+
return decode(settled);
|
|
145
|
+
}
|
|
146
|
+
let pending;
|
|
147
|
+
/**
|
|
148
|
+
* The signing key for this gate, resolved once per process.
|
|
149
|
+
*
|
|
150
|
+
* Resolved ONCE, so `configuredEpoch` is honoured only on the first call - the
|
|
151
|
+
* plugin's `setup()` passes its resolved value there, before any request can
|
|
152
|
+
* reach the lazy path.
|
|
153
|
+
*
|
|
154
|
+
* `TUNNEL_AUTH_JWT_SECRET` when set, else the cache-backed key. A cache that is
|
|
155
|
+
* unavailable degrades to an ephemeral per-process key (the previous behaviour)
|
|
156
|
+
* rather than refusing to sign: the key only validates an ALREADY-issued session,
|
|
157
|
+
* so losing it costs sessions, never admission - a caller still needs a code
|
|
158
|
+
* delivered to an allow-listed address.
|
|
159
|
+
*/
|
|
160
|
+
export function signingKey(configuredEpoch) {
|
|
161
|
+
pending ??= (async () => {
|
|
162
|
+
const epochMs = resolveSessionEpoch(configuredEpoch);
|
|
163
|
+
const configured = env.text(JWT_SECRET_ENV);
|
|
164
|
+
if (configured) {
|
|
165
|
+
return { key: new TextEncoder().encode(configured), epochMs };
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
return { key: await loadFromCache(epochMs), epochMs };
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger.warn(`no ${env.name(JWT_SECRET_ENV)} and the cache is unavailable - using an ephemeral per-process key; sessions will not survive a restart`, { error });
|
|
172
|
+
return { key: new Uint8Array(randomBytes(KEY_BYTES)), epochMs };
|
|
173
|
+
}
|
|
174
|
+
})();
|
|
175
|
+
return pending;
|
|
176
|
+
}
|
|
177
|
+
/** Reset the per-process key (tests, or after changing the env in-process). */
|
|
178
|
+
export function resetSigningKey() {
|
|
179
|
+
pending = undefined;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbmluZy1rZXkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2lnbmluZy1rZXkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBDRztBQUVILE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDMUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUU3RCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFFaEQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUVqRCxrRkFBa0Y7QUFDbEYsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUM7QUFFOUMsa0ZBQWtGO0FBQ2xGLE1BQU0sU0FBUyxHQUFHLEVBQUUsQ0FBQztBQVVyQjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxVQUFtQztJQUNyRSxNQUFNLEdBQUcsR0FBRyxVQUFVLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLFNBQVMsQ0FBQztJQUNuRSxJQUFJLEdBQUcsS0FBSyxTQUFTLElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxHQUFHLEtBQUssRUFBRTtRQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzlELE9BQU8sV0FBVyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQ3RDLENBQUM7QUFFRCxzRUFBc0U7QUFDdEUsU0FBUyxVQUFVLENBQUMsR0FBMkI7SUFDN0MsSUFBSSxHQUFHLFlBQVksSUFBSTtRQUFFLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7SUFFaEYsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ2hDLDJFQUEyRTtJQUMzRSxzRUFBc0U7SUFDdEUsb0VBQW9FO0lBQ3BFLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QixPQUFPLE9BQU8sR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztJQUNuRCxDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUN6QixNQUFNLENBQUMsSUFBSSxDQUFDLHdCQUF3QixHQUFHLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3BGLE9BQU8sQ0FBQyxDQUFDO0lBQ1gsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFTLFdBQVcsQ0FBQyxPQUFlO0lBQ2xDLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN2QixJQUFJLE9BQU8sSUFBSSxHQUFHO1FBQUUsT0FBTyxPQUFPLENBQUM7SUFDbkMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMscUNBQXFDLEVBQUU7UUFDL0UsVUFBVSxFQUFFLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFdBQVcsRUFBRTtLQUM1QyxDQUFDLENBQUM7SUFDSCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRCxrRkFBa0Y7QUFDbEYsU0FBUyxRQUFRLENBQUMsT0FBZTtJQUMvQixPQUFPLEdBQUcsVUFBVSxHQUFHLE9BQU8sRUFBRSxDQUFDO0FBQ25DLENBQUM7QUFFRCxTQUFTLE1BQU0sQ0FBQyxNQUFpQjtJQUMvQixPQUFPLElBQUksVUFBVSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBQ2pFLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxLQUFLLFVBQVUsYUFBYSxDQUFDLE9BQWU7SUFDMUMsTUFBTSxLQUFLLEdBQUcsWUFBWSxDQUFDLGVBQWUsRUFBRSxDQUFDO0lBQzdDLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUU5QixNQUFNLFFBQVEsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQVksR0FBRyxDQUFDLENBQUM7SUFDakQsSUFBSSxRQUFRLEVBQUUsTUFBTSxFQUFFLENBQUM7UUFDckIsTUFBTSxDQUFDLElBQUksQ0FBQyw0QkFBNEIsRUFBRSxFQUFFLFNBQVMsRUFBRSxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQztRQUM3RSxPQUFPLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMxQixDQUFDO0lBRUQsTUFBTSxNQUFNLEdBQWM7UUFDeEIsTUFBTSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztRQUNqRSxTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUU7S0FDcEMsQ0FBQztJQUNGLE1BQU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUM7SUFFdkQsK0VBQStFO0lBQy9FLGdGQUFnRjtJQUNoRix1QkFBdUI7SUFDdkIsTUFBTSxPQUFPLEdBQUcsQ0FBQyxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQVksR0FBRyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUM7SUFDNUQsTUFBTSxDQUFDLElBQUksQ0FBQywwQkFBMEIsRUFBRTtRQUN0QyxTQUFTLEVBQUUsT0FBTyxDQUFDLFNBQVM7UUFDNUIsVUFBVSxFQUFFLGVBQWU7UUFDM0IsT0FBTyxFQUFFLE9BQU8sQ0FBQyxTQUFTLEtBQUssTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxxQkFBcUI7S0FDaEYsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7QUFDekIsQ0FBQztBQVNELElBQUksT0FBd0MsQ0FBQztBQUU3Qzs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFNLFVBQVUsVUFBVSxDQUFDLGVBQXdDO0lBQ2pFLE9BQU8sS0FBSyxDQUFDLEtBQUssSUFBSSxFQUFFO1FBQ3RCLE1BQU0sT0FBTyxHQUFHLG1CQUFtQixDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQ3JELE1BQU0sVUFBVSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDNUMsSUFBSSxVQUFVLEVBQUUsQ0FBQztZQUNmLE9BQU8sRUFBRSxHQUFHLEVBQUUsSUFBSSxXQUFXLEVBQUUsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDaEUsQ0FBQztRQUNELElBQUksQ0FBQztZQUNILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxhQUFhLENBQUMsT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDeEQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLENBQUMsSUFBSSxDQUNULE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMseUdBQXlHLEVBQ3ZJLEVBQUUsS0FBSyxFQUFFLENBQ1YsQ0FBQztZQUNGLE9BQU8sRUFBRSxHQUFHLEVBQUUsSUFBSSxVQUFVLENBQUMsV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDbEUsQ0FBQztJQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDTCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FBRUQsK0VBQStFO0FBQy9FLE1BQU0sVUFBVSxlQUFlO0lBQzdCLE9BQU8sR0FBRyxTQUFTLENBQUM7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhlIGdhdGUncyBIUzI1NiBzZXNzaW9uLXNpZ25pbmcga2V5LCBwZXJzaXN0ZWQgaW4gQXBwS2l0J3MgY2FjaGUuXG4gKlxuICogVGhlIGtleSBkZWNpZGVzIHdoZXRoZXIgYSBzZXNzaW9uIENPT0tJRSBzdGlsbCB2ZXJpZmllcy4gQmVmb3JlIHRoaXMgbW9kdWxlIHRoZVxuICogZmFsbGJhY2sgd2FzIGByYW5kb21CeXRlcygzMilgIHBlciBwcm9jZXNzLCBzbyBldmVyeSByZXN0YXJ0IGludmFsaWRhdGVkIGV2ZXJ5XG4gKiBvdXRzdGFuZGluZyBjb29raWUgYW5kIGVhY2ggc2lnbmVkLWluIHVzZXIgaGFkIHRvIHJlcXVlc3QgYSBuZXcgY29kZSAtIHBhaW5mdWxcbiAqIGZvciBhIHR1bm5lbCwgd2hpY2ggcmVzdGFydHMgd2hlbmV2ZXIgdGhlIGFwcCBpdCB3cmFwcyBkb2VzLiBOb3cgdGhlIGtleSBpc1xuICogc3RvcmVkIGluIHRoZSBjYWNoZSBBcHBLaXQgYWxyZWFkeSBjb25maWd1cmVkIChtZW1vcnksIG9yIExha2ViYXNlIHdoZW4gdGhlXG4gKiBob3N0IHdpcmVzIGEgcGVyc2lzdGVudCBgQ2FjaGVTdG9yYWdlYCksIHNvIHdpdGggcGVyc2lzdGVudCBzdG9yYWdlIGEgcmVzdGFydFxuICoga2VlcHMgc2Vzc2lvbnMgYWxpdmUgZm9yIHtAbGluayBLRVlfVFRMX1NFQ09ORFN9LlxuICpcbiAqIEFuIGV4cGxpY2l0bHkgY29uZmlndXJlZCBgVFVOTkVMX0FVVEhfSldUX1NFQ1JFVGAgc3RpbGwgd2lucyBvdXRyaWdodC4gVGhhdCBpc1xuICogdGhlIHJpZ2h0IGFuc3dlciBmb3IgYSBmbGVldDogYW4gb3BlcmF0b3ItaGVsZCBzZWNyZXQgbmVlZHMgbm8gc2hhcmVkIGNhY2hlIGFuZFxuICogbm8gY29udmVyZ2VuY2UsIGFuZCBpdCBzdXJ2aXZlcyBhIGNhY2hlIGZsdXNoLlxuICpcbiAqICMjIGdldCAvIGdlbmVyYXRlIC8gZ2V0XG4gKlxuICogVHdvIGluc3RhbmNlcyBib290aW5nIGF0IG9uY2UgYm90aCBtaXNzLCBzbyBib3RoIHdvdWxkIGdlbmVyYXRlIC0gYW5kIHRoZSBsb3NlclxuICogd291bGQgc2lnbiBjb29raWVzIHdpdGggYSBrZXkgdGhlIHdpbm5lciByZWplY3RzLiBSZXNvbHV0aW9uIGlzIGEgcmUtUkVBRCBhZnRlclxuICogdGhlIHdyaXRlOiB3aGF0ZXZlciB0aGUgY2FjaGUgaG9sZHMgYWZ0ZXJ3YXJkcyBpcyB0aGUga2V5IGV2ZXJ5b25lIGFkb3B0cywgc29cbiAqIHRoZSBpbnN0YW5jZXMgY29udmVyZ2Ugb24gT05FIHZhbHVlIGluc3RlYWQgb2YgdHJ1c3RpbmcgdGhlIG9uZSB0aGV5IG1pbnRlZC5cbiAqIChgc2V0YCBpcyBub3QgY29uZGl0aW9uYWwgaW4gdGhlIGNhY2hlIEFQSSAtIHRoZXJlIGlzIG5vIGBzZXRueGAgdG8gbGVhbiBvbiAtXG4gKiBhbmQgdGhpcyBydW5zIG9uY2UgcGVyIHByb2Nlc3MsIHNvIHRoZSBleHRyYSByb3VuZC10cmlwIGlzIGZyZWUuKVxuICpcbiAqIEEgcGF0aG9sb2dpY2FsIGludGVybGVhdmUgY2FuIHN0aWxsIGNvc3QgYSBrZXk6IGlmIEEgd3JpdGVzIGJldHdlZW4gQidzIHdyaXRlXG4gKiBhbmQgQidzIHJlLXJlYWQsIEIgYWRvcHRzIEEncyBrZXkgd2hpbGUgQSBhZG9wdHMgaXRzIG93bi4gVGhlIGNvc3QgaXMgYm91bmRlZCAtXG4gKiBhIGNvb2tpZSBtaW50ZWQgaW4gdGhhdCB3aW5kb3cgZmFpbHMgdG8gdmVyaWZ5IGFuZCB0aGUgaG9sZGVyIHNpZ25zIGluIGFnYWluIC1cbiAqIGFuZCBpdCBjYW5ub3QgcHJvZHVjZSBhIGtleSBvbmUgaW5zdGFuY2UgVFJVU1RTIGJ1dCBhbm90aGVyIHJlamVjdHMgZm9yIGxvbmdlclxuICogdGhhbiB0aGUgd2luZG93IGl0c2VsZi4gU2V0IGBUVU5ORUxfQVVUSF9KV1RfU0VDUkVUYCB0byByZW1vdmUgdGhlIHJhY2VcbiAqIGVudGlyZWx5LlxuICpcbiAqICMjIEZvcmNpbmcgZXZlcnkgc2Vzc2lvbiB0byBlbmRcbiAqXG4gKiB7QGxpbmsgcmVzb2x2ZVNlc3Npb25FcG9jaH0gcmVhZHMgYSBkYXRlIGZyb20gYFRVTk5FTF9BVVRIX1NFU1NJT05fRVBPQ0hgIChvclxuICogYC0tc2Vzc2lvbi1lcG9jaGApLCBhbmQgdGhhdCBkYXRlIGlzIHBhcnQgb2YgdGhlIGNhY2hlIEtFWS4gTW92aW5nIGl0IGZvcndhcmRcbiAqIG1ha2VzIGV2ZXJ5IHByaW9yIGtleSB1bnJlYWNoYWJsZSwgc28gZXZlcnkgY29va2llIHNpZ25lZCBhZ2FpbnN0IGl0IHN0b3BzXG4gKiB2ZXJpZnlpbmcgLSB0aGUgbG9nLWV2ZXJ5b25lLW91dCBzd2l0Y2gsIHdpdGhvdXQgaGF2aW5nIHRvIGZpbmQgYW5kIGZsdXNoIGFcbiAqIGNhY2hlIGVudHJ5LiBUaGUgZXBvY2ggaXMgYWxzbyBhc3NlcnRlZCBhZ2FpbnN0IGVhY2ggdG9rZW4ncyBgaWF0YCwgc28gYVxuICogY29va2llIHRoYXQgcHJlZGF0ZXMgaXQgaXMgcmVmdXNlZCBldmVuIGlmIGl0IHdhcyBzaWduZWQgd2l0aCB0aGUga2V5IHRoYXQgaXNcbiAqIHNvbWVob3cgc3RpbGwgY3VycmVudC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgcmFuZG9tQnl0ZXMgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IENhY2hlTWFuYWdlciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IGVudiwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IEpXVF9TRUNSRVRfRU5WLCBTRVNTSU9OX0VQT0NIX0VOViB9IGZyb20gXCIuL2Vudi50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsOnNpZ25pbmcta2V5XCIpO1xuXG4vKipcbiAqIEhvdyBsb25nIGEgY2FjaGVkIHNpZ25pbmcga2V5IGxpdmVzOiAzMCBkYXlzLlxuICpcbiAqIFRoaXMgaXMgdGhlIGNlaWxpbmcgb24gaG93IGxvbmcgYSBzZXNzaW9uIGNvb2tpZSBjYW4gc3RheSB2YWxpZCBhY3Jvc3NcbiAqIHJlc3RhcnRzLCBzbyBpdCBpcyBkZWxpYmVyYXRlbHkgPj0gdGhlIGRlZmF1bHQgc2Vzc2lvbiBUVEwgLSBhIGtleSB0aGF0IGV4cGlyZWRcbiAqIGJlZm9yZSB0aGUgY29va2llcyBpdCBzaWduZWQgd291bGQgbG9nIGV2ZXJ5b25lIG91dCBmb3Igbm8gcmVhc29uLlxuICovXG5leHBvcnQgY29uc3QgS0VZX1RUTF9TRUNPTkRTID0gMzAgKiAyNCAqIDYwICogNjA7XG5cbi8qKiBDYWNoZS1rZXkgcHJlZml4IGZvciB0aGUgc2lnbmluZyBrZXksIG5hbWVzcGFjZWQgYXdheSBmcm9tIG90aGVyIGNhY2hlIHVzZS4gKi9cbmNvbnN0IEtFWV9QUkVGSVggPSBcInR1bm5lbDphdXRoOnNpZ25pbmcta2V5OlwiO1xuXG4vKiogQnl0ZXMgb2YgZW50cm9weSBpbiBhIGdlbmVyYXRlZCBrZXkgKDI1Ni1iaXQsIG1hdGNoaW5nIEhTMjU2J3MgaGFzaCB3aWR0aCkuICovXG5jb25zdCBLRVlfQllURVMgPSAzMjtcblxuLyoqIFdoYXQgdGhlIGNhY2hlIHN0b3JlczogdGhlIGtleSBwbHVzIHdoZW4gaXQgd2FzIG1pbnRlZCwgZm9yIG9ic2VydmFiaWxpdHkuICovXG5pbnRlcmZhY2UgU3RvcmVkS2V5IHtcbiAgLyoqIEJhc2U2NHVybCBvZiB0aGUgcmF3IGtleSBieXRlcy4gKi9cbiAgc2VjcmV0OiBzdHJpbmc7XG4gIC8qKiBXaGVuIHRoaXMga2V5IHdhcyBnZW5lcmF0ZWQsIElTTy04NjAxLiAqL1xuICBjcmVhdGVkQXQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBmb3JjZS1jbGVhciBlcG9jaCBhcyBlcG9jaCBNSUxMSVNFQ09ORFMsIG9yIGAwYCB3aGVuIHVuc2V0LlxuICpcbiAqIEFjY2VwdHMgYW55dGhpbmcgYERhdGVgIHBhcnNlcyAoYDIwMjYtMDgtMDJgLCBhbiBJU08gdGltZXN0YW1wKSBwbHVzIGEgYmFyZVxuICogZXBvY2gtc2Vjb25kcyAvIGVwb2NoLW1pbGxpcyBudW1iZXIsIHNvIGEgdmFsdWUgcGFzdGVkIGZyb20gYGRhdGUgKyVzYCB3b3Jrcy5cbiAqIEFuIFVOUEFSU0VBQkxFIHZhbHVlIGlzIGlnbm9yZWQgd2l0aCBhIHdhcm5pbmcgcmF0aGVyIHRoYW4gdGhyb3dpbmc6IHRoaXMgaXNcbiAqIHRoZSBzd2l0Y2ggdGhhdCBsb2dzIGEgZmxlZXQgYmFjayBpbiwgYW5kIGZhaWxpbmcgdG8gYm9vdCBvdmVyIGEgdHlwbyBpcyB3b3JzZVxuICogdGhhbiBub3Qgcm90YXRpbmcuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlU2Vzc2lvbkVwb2NoKGNvbmZpZ3VyZWQ/OiBzdHJpbmcgfCBudW1iZXIgfCBEYXRlKTogbnVtYmVyIHtcbiAgY29uc3QgcmF3ID0gY29uZmlndXJlZCA/PyBlbnYudGV4dChTRVNTSU9OX0VQT0NIX0VOVikgPz8gdW5kZWZpbmVkO1xuICBpZiAocmF3ID09PSB1bmRlZmluZWQgfHwgcmF3ID09PSBudWxsIHx8IHJhdyA9PT0gXCJcIikgcmV0dXJuIDA7XG4gIHJldHVybiBjbGFtcFRvUGFzdChwYXJzZUVwb2NoKHJhdykpO1xufVxuXG4vKiogUGFyc2UgdGhlIGFjY2VwdGVkIGVwb2NoIHNwZWxsaW5ncyB0byBtcywgb3IgYDBgIHdoZW4gdW51c2FibGUuICovXG5mdW5jdGlvbiBwYXJzZUVwb2NoKHJhdzogc3RyaW5nIHwgbnVtYmVyIHwgRGF0ZSk6IG51bWJlciB7XG4gIGlmIChyYXcgaW5zdGFuY2VvZiBEYXRlKSByZXR1cm4gTnVtYmVyLmlzTmFOKHJhdy5nZXRUaW1lKCkpID8gMCA6IHJhdy5nZXRUaW1lKCk7XG5cbiAgY29uc3QgdGV4dCA9IFN0cmluZyhyYXcpLnRyaW0oKTtcbiAgLy8gQSBiYXJlIG51bWJlciBpcyBhIHRpbWVzdGFtcCwgbm90IGEgZGF0ZSBzdHJpbmc6IGBEYXRlLnBhcnNlKFwiMTc4NS4uLlwiKWBcbiAgLy8gd291bGQgcmVhZCBpdCBhcyBhIFlFQVIuIFZhbHVlcyBiZWxvdyB+MWUxMSBhcmUgc2Vjb25kcyAoYW55IG1pbGxpc1xuICAvLyB0aW1lc3RhbXAgc2luY2UgMTk3MyBpcyBsYXJnZXIpLCB3aGljaCBpcyB3aGF0IGBkYXRlICslc2AgcHJpbnRzLlxuICBpZiAoL15cXGQrJC8udGVzdCh0ZXh0KSkge1xuICAgIGNvbnN0IG51bWVyaWMgPSBOdW1iZXIodGV4dCk7XG4gICAgcmV0dXJuIG51bWVyaWMgPCAxZTExID8gbnVtZXJpYyAqIDEwMDAgOiBudW1lcmljO1xuICB9XG4gIGNvbnN0IHBhcnNlZCA9IERhdGUucGFyc2UodGV4dCk7XG4gIGlmIChOdW1iZXIuaXNOYU4ocGFyc2VkKSkge1xuICAgIGxvZ2dlci53YXJuKGBpZ25vcmluZyB1bnBhcnNlYWJsZSAke2Vudi5uYW1lKFNFU1NJT05fRVBPQ0hfRU5WKX1gLCB7IHZhbHVlOiB0ZXh0IH0pO1xuICAgIHJldHVybiAwO1xuICB9XG4gIHJldHVybiBwYXJzZWQ7XG59XG5cbi8qKlxuICogSG9sZCB0aGUgY3V0b2ZmIGF0IFwibm93XCIsIGJlY2F1c2UgYSBGVVRVUkUgZXBvY2ggd291bGQgcmVmdXNlIHRoZSBzZXNzaW9ucyBpdFxuICogaXMgYWJvdXQgdG8gbWludCBhcyB3ZWxsIGFzIHRoZSBvbGQgb25lcyAtIGFuIGFwcCBub2JvZHkgY2FuIHNpZ24gaW4gdG8sIGZyb20gYVxuICogbWlzdHlwZWQgeWVhciwgd2l0aCB0aGUgZml4IGhpZGRlbiBiZWhpbmQgdW5kZXJzdGFuZGluZyB0aGlzIGZsYWcuIENsYW1wZWQsIGFcbiAqIGZ1dHVyZSBkYXRlIG1lYW5zIHdoYXQgYW4gb3BlcmF0b3Igc2V0dGluZyBpdCBhbHdheXMgbWVhbnQ6IGNsZWFyIGV2ZXJ5dGhpbmdcbiAqIG91dHN0YW5kaW5nLCB0aGVuIGNhcnJ5IG9uLlxuICovXG5mdW5jdGlvbiBjbGFtcFRvUGFzdChlcG9jaE1zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBpZiAoZXBvY2hNcyA8PSBub3cpIHJldHVybiBlcG9jaE1zO1xuICBsb2dnZXIud2FybihgJHtlbnYubmFtZShTRVNTSU9OX0VQT0NIX0VOVil9IGlzIGluIHRoZSBmdXR1cmUgLSBjbGFtcGluZyB0byBub3dgLCB7XG4gICAgY29uZmlndXJlZDogbmV3IERhdGUoZXBvY2hNcykudG9JU09TdHJpbmcoKSxcbiAgfSk7XG4gIHJldHVybiBub3c7XG59XG5cbi8qKiBUaGUgY2FjaGUga2V5IGZvciBvbmUgZXBvY2gsIHNvIG1vdmluZyB0aGUgZXBvY2ggb3JwaGFucyBldmVyeSBlYXJsaWVyIGtleS4gKi9cbmZ1bmN0aW9uIGNhY2hlS2V5KGVwb2NoTXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBgJHtLRVlfUFJFRklYfSR7ZXBvY2hNc31gO1xufVxuXG5mdW5jdGlvbiBkZWNvZGUoc3RvcmVkOiBTdG9yZWRLZXkpOiBVaW50OEFycmF5IHtcbiAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEJ1ZmZlci5mcm9tKHN0b3JlZC5zZWNyZXQsIFwiYmFzZTY0dXJsXCIpKTtcbn1cblxuLyoqXG4gKiBMb2FkIHRoZSBzaWduaW5nIGtleSBmb3IgYGVwb2NoTXNgIGZyb20gdGhlIGNhY2hlLCBtaW50aW5nIGFuZCBzdG9yaW5nIG9uZSB3aGVuXG4gKiBhYnNlbnQuIFNlZSB0aGUgbW9kdWxlIGRvY3MgZm9yIHdoeSB0aGUgd3JpdGUgaXMgZm9sbG93ZWQgYnkgYSByZS1yZWFkLlxuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkRnJvbUNhY2hlKGVwb2NoTXM6IG51bWJlcik6IFByb21pc2U8VWludDhBcnJheT4ge1xuICBjb25zdCBjYWNoZSA9IENhY2hlTWFuYWdlci5nZXRJbnN0YW5jZVN5bmMoKTtcbiAgY29uc3Qga2V5ID0gY2FjaGVLZXkoZXBvY2hNcyk7XG5cbiAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBjYWNoZS5nZXQ8U3RvcmVkS2V5PihrZXkpO1xuICBpZiAoZXhpc3Rpbmc/LnNlY3JldCkge1xuICAgIGxvZ2dlci5pbmZvKFwicmV1c2luZyBjYWNoZWQgc2lnbmluZyBrZXlcIiwgeyBjcmVhdGVkQXQ6IGV4aXN0aW5nLmNyZWF0ZWRBdCB9KTtcbiAgICByZXR1cm4gZGVjb2RlKGV4aXN0aW5nKTtcbiAgfVxuXG4gIGNvbnN0IG1pbnRlZDogU3RvcmVkS2V5ID0ge1xuICAgIHNlY3JldDogQnVmZmVyLmZyb20ocmFuZG9tQnl0ZXMoS0VZX0JZVEVTKSkudG9TdHJpbmcoXCJiYXNlNjR1cmxcIiksXG4gICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gIH07XG4gIGF3YWl0IGNhY2hlLnNldChrZXksIG1pbnRlZCwgeyB0dGw6IEtFWV9UVExfU0VDT05EUyB9KTtcblxuICAvLyBSZS1yZWFkIHJhdGhlciB0aGFuIHRydXN0aW5nIGBtaW50ZWRgOiBhbm90aGVyIGluc3RhbmNlIHRoYXQgcmFjZWQgdGhpcyBib290XG4gIC8vIG1heSBoYXZlIHdyaXR0ZW4gZmlyc3QsIGFuZCBhZG9wdGluZyB3aGF0ZXZlciBpcyBzdG9yZWQgaXMgd2hhdCBtYWtlcyB0aGUgdHdvXG4gIC8vIGNvbnZlcmdlIG9uIG9uZSBrZXkuXG4gIGNvbnN0IHNldHRsZWQgPSAoYXdhaXQgY2FjaGUuZ2V0PFN0b3JlZEtleT4oa2V5KSkgPz8gbWludGVkO1xuICBsb2dnZXIuaW5mbyhcInN0b3JlZCBhIG5ldyBzaWduaW5nIGtleVwiLCB7XG4gICAgY3JlYXRlZEF0OiBzZXR0bGVkLmNyZWF0ZWRBdCxcbiAgICB0dGxTZWNvbmRzOiBLRVlfVFRMX1NFQ09ORFMsXG4gICAgYWRvcHRlZDogc2V0dGxlZC5jcmVhdGVkQXQgPT09IG1pbnRlZC5jcmVhdGVkQXQgPyBcIm93blwiIDogXCJjb25jdXJyZW50LWluc3RhbmNlXCIsXG4gIH0pO1xuICByZXR1cm4gZGVjb2RlKHNldHRsZWQpO1xufVxuXG4vKiogQSByZXNvbHZlZCBrZXkgcGx1cyB0aGUgZXBvY2ggaXQgaXMgc2NvcGVkIHRvLiAqL1xuZXhwb3J0IGludGVyZmFjZSBTaWduaW5nS2V5IHtcbiAga2V5OiBVaW50OEFycmF5O1xuICAvKiogRm9yY2UtY2xlYXIgZXBvY2ggaW4gbXM7IGAwYCB3aGVuIHVuc2V0LiBUb2tlbnMgb2xkZXIgdGhhbiB0aGlzIGFyZSByZWZ1c2VkLiAqL1xuICBlcG9jaE1zOiBudW1iZXI7XG59XG5cbmxldCBwZW5kaW5nOiBQcm9taXNlPFNpZ25pbmdLZXk+IHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIFRoZSBzaWduaW5nIGtleSBmb3IgdGhpcyBnYXRlLCByZXNvbHZlZCBvbmNlIHBlciBwcm9jZXNzLlxuICpcbiAqIFJlc29sdmVkIE9OQ0UsIHNvIGBjb25maWd1cmVkRXBvY2hgIGlzIGhvbm91cmVkIG9ubHkgb24gdGhlIGZpcnN0IGNhbGwgLSB0aGVcbiAqIHBsdWdpbidzIGBzZXR1cCgpYCBwYXNzZXMgaXRzIHJlc29sdmVkIHZhbHVlIHRoZXJlLCBiZWZvcmUgYW55IHJlcXVlc3QgY2FuXG4gKiByZWFjaCB0aGUgbGF6eSBwYXRoLlxuICpcbiAqIGBUVU5ORUxfQVVUSF9KV1RfU0VDUkVUYCB3aGVuIHNldCwgZWxzZSB0aGUgY2FjaGUtYmFja2VkIGtleS4gQSBjYWNoZSB0aGF0IGlzXG4gKiB1bmF2YWlsYWJsZSBkZWdyYWRlcyB0byBhbiBlcGhlbWVyYWwgcGVyLXByb2Nlc3Mga2V5ICh0aGUgcHJldmlvdXMgYmVoYXZpb3VyKVxuICogcmF0aGVyIHRoYW4gcmVmdXNpbmcgdG8gc2lnbjogdGhlIGtleSBvbmx5IHZhbGlkYXRlcyBhbiBBTFJFQURZLWlzc3VlZCBzZXNzaW9uLFxuICogc28gbG9zaW5nIGl0IGNvc3RzIHNlc3Npb25zLCBuZXZlciBhZG1pc3Npb24gLSBhIGNhbGxlciBzdGlsbCBuZWVkcyBhIGNvZGVcbiAqIGRlbGl2ZXJlZCB0byBhbiBhbGxvdy1saXN0ZWQgYWRkcmVzcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNpZ25pbmdLZXkoY29uZmlndXJlZEVwb2NoPzogc3RyaW5nIHwgbnVtYmVyIHwgRGF0ZSk6IFByb21pc2U8U2lnbmluZ0tleT4ge1xuICBwZW5kaW5nID8/PSAoYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGVwb2NoTXMgPSByZXNvbHZlU2Vzc2lvbkVwb2NoKGNvbmZpZ3VyZWRFcG9jaCk7XG4gICAgY29uc3QgY29uZmlndXJlZCA9IGVudi50ZXh0KEpXVF9TRUNSRVRfRU5WKTtcbiAgICBpZiAoY29uZmlndXJlZCkge1xuICAgICAgcmV0dXJuIHsga2V5OiBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUoY29uZmlndXJlZCksIGVwb2NoTXMgfTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiB7IGtleTogYXdhaXQgbG9hZEZyb21DYWNoZShlcG9jaE1zKSwgZXBvY2hNcyB9O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIud2FybihcbiAgICAgICAgYG5vICR7ZW52Lm5hbWUoSldUX1NFQ1JFVF9FTlYpfSBhbmQgdGhlIGNhY2hlIGlzIHVuYXZhaWxhYmxlIC0gdXNpbmcgYW4gZXBoZW1lcmFsIHBlci1wcm9jZXNzIGtleTsgc2Vzc2lvbnMgd2lsbCBub3Qgc3Vydml2ZSBhIHJlc3RhcnRgLFxuICAgICAgICB7IGVycm9yIH0sXG4gICAgICApO1xuICAgICAgcmV0dXJuIHsga2V5OiBuZXcgVWludDhBcnJheShyYW5kb21CeXRlcyhLRVlfQllURVMpKSwgZXBvY2hNcyB9O1xuICAgIH1cbiAgfSkoKTtcbiAgcmV0dXJuIHBlbmRpbmc7XG59XG5cbi8qKiBSZXNldCB0aGUgcGVyLXByb2Nlc3Mga2V5ICh0ZXN0cywgb3IgYWZ0ZXIgY2hhbmdpbmcgdGhlIGVudiBpbi1wcm9jZXNzKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNldFNpZ25pbmdLZXkoKTogdm9pZCB7XG4gIHBlbmRpbmcgPSB1bmRlZmluZWQ7XG59XG4iXX0=
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/allowlist.ts","../src/app.ts","../src/cli.ts","../src/env.ts","../src/headers.ts","../src/otp.ts","../src/plugin.ts","../src/portr.ts","../src/proxy.ts","../src/rate-limit.ts","../index.ts","../bin/dbx-tools-tunnel.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/allowlist.ts","../src/app.ts","../src/cli.ts","../src/env.ts","../src/headers.ts","../src/otp.ts","../src/plugin.ts","../src/portr.ts","../src/proxy.ts","../src/rate-limit.ts","../src/signing-key.ts","../index.ts","../bin/dbx-tools-tunnel.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@clack/prompts": "^1.7.0",
|
|
31
31
|
"@databricks/appkit": "^0.43.0",
|
|
32
|
-
"@dbx-tools/appkit": "0.6.
|
|
33
|
-
"@dbx-tools/core": "0.6.
|
|
34
|
-
"@dbx-tools/email": "0.6.
|
|
35
|
-
"@dbx-tools/shared-core": "0.6.
|
|
36
|
-
"@dbx-tools/shared-email": "0.6.
|
|
32
|
+
"@dbx-tools/appkit": "0.6.51",
|
|
33
|
+
"@dbx-tools/core": "0.6.51",
|
|
34
|
+
"@dbx-tools/email": "0.6.51",
|
|
35
|
+
"@dbx-tools/shared-core": "0.6.51",
|
|
36
|
+
"@dbx-tools/shared-email": "0.6.51",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"http-proxy-3": "^1.23.3",
|
|
39
39
|
"jose": "^6.2.3"
|
|
@@ -93,10 +93,14 @@
|
|
|
93
93
|
"types": "./lib/src/rate-limit.d.ts",
|
|
94
94
|
"default": "./lib/src/rate-limit.js"
|
|
95
95
|
},
|
|
96
|
+
"./signing-key": {
|
|
97
|
+
"types": "./lib/src/signing-key.d.ts",
|
|
98
|
+
"default": "./lib/src/signing-key.js"
|
|
99
|
+
},
|
|
96
100
|
"./package.json": "./package.json"
|
|
97
101
|
}
|
|
98
102
|
},
|
|
99
|
-
"version": "0.6.
|
|
103
|
+
"version": "0.6.51",
|
|
100
104
|
"types": "./lib/index.d.ts",
|
|
101
105
|
"type": "module",
|
|
102
106
|
"exports": {
|
|
@@ -144,6 +148,10 @@
|
|
|
144
148
|
"types": "./lib/src/rate-limit.d.ts",
|
|
145
149
|
"default": "./lib/src/rate-limit.js"
|
|
146
150
|
},
|
|
151
|
+
"./signing-key": {
|
|
152
|
+
"types": "./lib/src/signing-key.d.ts",
|
|
153
|
+
"default": "./lib/src/signing-key.js"
|
|
154
|
+
},
|
|
147
155
|
"./package.json": "./package.json"
|
|
148
156
|
},
|
|
149
157
|
"files": [
|
package/src/cli.ts
CHANGED
|
@@ -52,6 +52,7 @@ interface TunnelOpts {
|
|
|
52
52
|
message?: string;
|
|
53
53
|
sessionTtl?: string;
|
|
54
54
|
codeTtl?: string;
|
|
55
|
+
sessionEpoch?: string;
|
|
55
56
|
insecure?: boolean;
|
|
56
57
|
forwardHeaders?: string;
|
|
57
58
|
}
|
|
@@ -75,6 +76,10 @@ function program(): Command {
|
|
|
75
76
|
.option("--message <text>", "Line shown above the code in the email (env TUNNEL_AUTH_MESSAGE)")
|
|
76
77
|
.option("--session-ttl <seconds>", "Session lifetime (env TUNNEL_AUTH_SESSION_TTL)")
|
|
77
78
|
.option("--code-ttl <seconds>", "One-time-code lifetime (env TUNNEL_AUTH_CODE_TTL)")
|
|
79
|
+
.option(
|
|
80
|
+
"--session-epoch <date>",
|
|
81
|
+
"Invalidate sessions issued before this date, signing everyone out: a date, ISO timestamp, or epoch seconds (env TUNNEL_AUTH_SESSION_EPOCH)",
|
|
82
|
+
)
|
|
78
83
|
.option(
|
|
79
84
|
"--forward-headers <patterns>",
|
|
80
85
|
"Extra x- request headers tunnel traffic may forward: literal / glob / /regex/ (env TUNNEL_FORWARD_HEADERS)",
|
|
@@ -135,6 +140,7 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
135
140
|
message: opts.message,
|
|
136
141
|
sessionTtlSeconds: opts.sessionTtl ? Number(opts.sessionTtl) : undefined,
|
|
137
142
|
codeTtlSeconds: opts.codeTtl ? Number(opts.codeTtl) : undefined,
|
|
143
|
+
sessionEpoch: opts.sessionEpoch,
|
|
138
144
|
};
|
|
139
145
|
|
|
140
146
|
// 1. Spawn the wrapped app with the PRIVATE port. It binds loopback; only the
|
package/src/env.ts
CHANGED
|
@@ -52,6 +52,12 @@ export const CODE_TTL_ENV: EnvKey = ["TUNNEL_AUTH_CODE_TTL", "AUTH_CODE_TTL"];
|
|
|
52
52
|
/** HS256 signing secret for the session JWT. */
|
|
53
53
|
export const JWT_SECRET_ENV: EnvKey = ["TUNNEL_AUTH_JWT_SECRET", "AUTH_JWT_SECRET"];
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Force-clear date for sessions issued before it: every earlier cookie stops
|
|
57
|
+
* verifying. Any `Date`-parseable value, or bare epoch seconds / millis.
|
|
58
|
+
*/
|
|
59
|
+
export const SESSION_EPOCH_ENV: EnvKey = "TUNNEL_AUTH_SESSION_EPOCH";
|
|
60
|
+
|
|
55
61
|
/** The public `<subdomain>.<server>` portr should serve on. */
|
|
56
62
|
export const PUBLIC_DOMAIN_ENV: EnvKey = ["TUNNEL_PUBLIC_DOMAIN", "PUBLIC_DOMAIN"];
|
|
57
63
|
|
package/src/otp.ts
CHANGED
|
@@ -8,25 +8,20 @@
|
|
|
8
8
|
* counter (never the plaintext, never in the JWT); `verify` is constant-time on
|
|
9
9
|
* the hash, and the entry is deleted on success or once attempts are exhausted.
|
|
10
10
|
*
|
|
11
|
-
* The session JWT is
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* session. Set it in any deployment running more than one instance, or sessions
|
|
18
|
-
* minted by one will not verify on another.
|
|
11
|
+
* The session JWT is an HS256 token (via `jose`) carrying only the email. Its
|
|
12
|
+
* signing key is resolved by `./signing-key.ts`: `TUNNEL_AUTH_JWT_SECRET` when
|
|
13
|
+
* set, else a key persisted in the cache for 30 days so cookies survive the
|
|
14
|
+
* restarts a tunnel sees whenever the app it wraps reloads. A cookie signed
|
|
15
|
+
* before `TUNNEL_AUTH_SESSION_EPOCH` is refused here as well as being orphaned by
|
|
16
|
+
* the cache key, so the force-clear switch holds even for a still-current key.
|
|
19
17
|
*
|
|
20
18
|
* @module
|
|
21
19
|
*/
|
|
22
20
|
|
|
23
|
-
import { createHash,
|
|
21
|
+
import { createHash, randomInt, timingSafeEqual } from "node:crypto";
|
|
24
22
|
import { CacheManager } from "@databricks/appkit";
|
|
25
|
-
import { env, log } from "@dbx-tools/shared-core";
|
|
26
23
|
import { jwtVerify, SignJWT } from "jose";
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
const logger = log.logger("tunnel:otp");
|
|
24
|
+
import { signingKey } from "./signing-key.ts";
|
|
30
25
|
|
|
31
26
|
/** JWT issuer/audience so a token minted for this gate isn't accepted elsewhere. */
|
|
32
27
|
const JWT_AUD = "dbx-tools-tunnel-auth";
|
|
@@ -107,46 +102,34 @@ export class CodeStore {
|
|
|
107
102
|
}
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
/**
|
|
111
|
-
* Resolve the HS256 signing key. Prefers `TUNNEL_AUTH_JWT_SECRET`; when unset, mints an
|
|
112
|
-
* ephemeral per-process key (fail-open) and warns once. Memoized.
|
|
113
|
-
*/
|
|
114
|
-
let cachedKey: Uint8Array | undefined;
|
|
115
|
-
function signingKey(): Uint8Array {
|
|
116
|
-
if (cachedKey) return cachedKey;
|
|
117
|
-
const secret = env.text(JWT_SECRET_ENV);
|
|
118
|
-
if (secret) {
|
|
119
|
-
cachedKey = new TextEncoder().encode(secret);
|
|
120
|
-
} else {
|
|
121
|
-
logger.warn(
|
|
122
|
-
`${JWT_SECRET_ENV[0]} is not set - using an ephemeral per-process key; sessions will not survive a restart`,
|
|
123
|
-
);
|
|
124
|
-
cachedKey = randomBytes(32);
|
|
125
|
-
}
|
|
126
|
-
return cachedKey;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Reset the memoized key (tests, or after changing the env in-process). */
|
|
130
|
-
export function resetSigningKey(): void {
|
|
131
|
-
cachedKey = undefined;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Mint a short-lived session JWT for `email`, expiring in `ttlSeconds`. */
|
|
105
|
+
/** Mint a session JWT for `email`, expiring in `ttlSeconds`. */
|
|
135
106
|
export async function signSession(email: string, ttlSeconds: number): Promise<string> {
|
|
107
|
+
const { key } = await signingKey();
|
|
136
108
|
return new SignJWT({ email })
|
|
137
109
|
.setProtectedHeader({ alg: "HS256" })
|
|
138
110
|
.setSubject(email)
|
|
139
111
|
.setAudience(JWT_AUD)
|
|
140
112
|
.setIssuedAt()
|
|
141
113
|
.setExpirationTime(`${ttlSeconds}s`)
|
|
142
|
-
.sign(
|
|
114
|
+
.sign(key);
|
|
143
115
|
}
|
|
144
116
|
|
|
145
117
|
/** Validate a session JWT, returning the email it was minted for, or `undefined`. */
|
|
146
118
|
export async function verifySession(token: string | undefined): Promise<string | undefined> {
|
|
147
119
|
if (!token) return undefined;
|
|
148
120
|
try {
|
|
149
|
-
const {
|
|
121
|
+
const { key, epochMs } = await signingKey();
|
|
122
|
+
const { payload } = await jwtVerify(token, key, { audience: JWT_AUD });
|
|
123
|
+
// Belt and braces with the epoch-scoped cache key: that alone already
|
|
124
|
+
// orphans older keys, but an operator who moved the epoch while
|
|
125
|
+
// TUNNEL_AUTH_JWT_SECRET is set has no key rotation to rely on, and this
|
|
126
|
+
// check is what makes the switch work in that case too.
|
|
127
|
+
// Compared in whole SECONDS because `iat` has no finer resolution: against a
|
|
128
|
+
// millisecond epoch, a cookie minted in the same second as the cutoff would
|
|
129
|
+
// be refused depending on sub-second rounding.
|
|
130
|
+
if (epochMs > 0 && (payload.iat === undefined || payload.iat < Math.floor(epochMs / 1000))) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
150
133
|
return typeof payload.email === "string" ? payload.email : undefined;
|
|
151
134
|
} catch {
|
|
152
135
|
return undefined;
|