@dbx-tools/cli-tunnel 0.6.51 → 0.6.52
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 +33 -26
- package/index.ts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -2
- package/lib/src/cli.js +3 -3
- package/lib/src/env.d.ts +4 -3
- package/lib/src/env.js +8 -4
- package/lib/src/otp.d.ts +2 -2
- package/lib/src/otp.js +9 -9
- package/lib/src/plugin.d.ts +7 -6
- package/lib/src/plugin.js +6 -6
- package/lib/src/signing-key.d.ts +15 -12
- package/lib/src/signing-key.js +37 -48
- package/package.json +6 -6
- package/src/cli.ts +4 -4
- package/src/env.ts +7 -3
- package/src/otp.ts +8 -8
- package/src/plugin.ts +12 -11
- package/src/signing-key.ts +38 -49
package/lib/src/signing-key.d.ts
CHANGED
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
*
|
|
32
32
|
* ## Forcing every session to end
|
|
33
33
|
*
|
|
34
|
-
* {@link
|
|
35
|
-
* `--session-
|
|
34
|
+
* {@link resolveSessionCutoff} reads a date from `TUNNEL_AUTH_SESSION_CUTOFF` (or
|
|
35
|
+
* `--session-cutoff`), and that date is part of the cache KEY. Moving it forward
|
|
36
36
|
* makes every prior key unreachable, so every cookie signed against it stops
|
|
37
37
|
* verifying - the log-everyone-out switch, without having to find and flush a
|
|
38
|
-
* cache entry. The
|
|
38
|
+
* cache entry. The cutoff is also asserted against each token's `iat`, so a
|
|
39
39
|
* cookie that predates it is refused even if it was signed with the key that is
|
|
40
40
|
* somehow still current.
|
|
41
41
|
*
|
|
@@ -50,25 +50,28 @@
|
|
|
50
50
|
*/
|
|
51
51
|
export declare const KEY_TTL_SECONDS: number;
|
|
52
52
|
/**
|
|
53
|
-
* Resolve the force-clear
|
|
53
|
+
* Resolve the force-clear cutoff as epoch MILLISECONDS, or `0` when unset.
|
|
54
|
+
*
|
|
55
|
+
* Accepts whatever `object.toDate` accepts - a `Date`, `2026-08-02`, an ISO
|
|
56
|
+
* instant, epoch seconds or millis from `date +%s`, or a relative duration
|
|
57
|
+
* (`-30d`, `7 days ago`), which is the spelling an operator reaching for this
|
|
58
|
+
* usually wants: sign out everyone who signed in more than a month ago.
|
|
54
59
|
*
|
|
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
60
|
* An UNPARSEABLE value is ignored with a warning rather than throwing: this is
|
|
58
61
|
* the switch that logs a fleet back in, and failing to boot over a typo is worse
|
|
59
62
|
* than not rotating.
|
|
60
63
|
*/
|
|
61
|
-
export declare function
|
|
62
|
-
/** A resolved key plus the
|
|
64
|
+
export declare function resolveSessionCutoff(configured?: string | number | Date): number;
|
|
65
|
+
/** A resolved key plus the cutoff it is scoped to. */
|
|
63
66
|
export interface SigningKey {
|
|
64
67
|
key: Uint8Array;
|
|
65
|
-
/** Force-clear
|
|
66
|
-
|
|
68
|
+
/** Force-clear cutoff in ms; `0` when unset. Tokens older than this are refused. */
|
|
69
|
+
cutoffMs: number;
|
|
67
70
|
}
|
|
68
71
|
/**
|
|
69
72
|
* The signing key for this gate, resolved once per process.
|
|
70
73
|
*
|
|
71
|
-
* Resolved ONCE, so `
|
|
74
|
+
* Resolved ONCE, so `configuredCutoff` is honoured only on the first call - the
|
|
72
75
|
* plugin's `setup()` passes its resolved value there, before any request can
|
|
73
76
|
* reach the lazy path.
|
|
74
77
|
*
|
|
@@ -78,6 +81,6 @@ export interface SigningKey {
|
|
|
78
81
|
* so losing it costs sessions, never admission - a caller still needs a code
|
|
79
82
|
* delivered to an allow-listed address.
|
|
80
83
|
*/
|
|
81
|
-
export declare function signingKey(
|
|
84
|
+
export declare function signingKey(configuredCutoff?: string | number | Date): Promise<SigningKey>;
|
|
82
85
|
/** Reset the per-process key (tests, or after changing the env in-process). */
|
|
83
86
|
export declare function resetSigningKey(): void;
|
package/lib/src/signing-key.js
CHANGED
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
*
|
|
32
32
|
* ## Forcing every session to end
|
|
33
33
|
*
|
|
34
|
-
* {@link
|
|
35
|
-
* `--session-
|
|
34
|
+
* {@link resolveSessionCutoff} reads a date from `TUNNEL_AUTH_SESSION_CUTOFF` (or
|
|
35
|
+
* `--session-cutoff`), and that date is part of the cache KEY. Moving it forward
|
|
36
36
|
* makes every prior key unreachable, so every cookie signed against it stops
|
|
37
37
|
* verifying - the log-everyone-out switch, without having to find and flush a
|
|
38
|
-
* cache entry. The
|
|
38
|
+
* cache entry. The cutoff is also asserted against each token's `iat`, so a
|
|
39
39
|
* cookie that predates it is refused even if it was signed with the key that is
|
|
40
40
|
* somehow still current.
|
|
41
41
|
*
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
*/
|
|
44
44
|
import { randomBytes } from "node:crypto";
|
|
45
45
|
import { CacheManager } from "@databricks/appkit";
|
|
46
|
-
import { env, log } from "@dbx-tools/shared-core";
|
|
47
|
-
import { JWT_SECRET_ENV,
|
|
46
|
+
import { env, log, object } from "@dbx-tools/shared-core";
|
|
47
|
+
import { JWT_SECRET_ENV, SESSION_CUTOFF_ENV } from "./env.js";
|
|
48
48
|
const logger = log.logger("tunnel:signing-key");
|
|
49
49
|
/**
|
|
50
50
|
* How long a cached signing key lives: 30 days.
|
|
@@ -59,69 +59,58 @@ const KEY_PREFIX = "tunnel:auth:signing-key:";
|
|
|
59
59
|
/** Bytes of entropy in a generated key (256-bit, matching HS256's hash width). */
|
|
60
60
|
const KEY_BYTES = 32;
|
|
61
61
|
/**
|
|
62
|
-
* Resolve the force-clear
|
|
62
|
+
* Resolve the force-clear cutoff as epoch MILLISECONDS, or `0` when unset.
|
|
63
|
+
*
|
|
64
|
+
* Accepts whatever `object.toDate` accepts - a `Date`, `2026-08-02`, an ISO
|
|
65
|
+
* instant, epoch seconds or millis from `date +%s`, or a relative duration
|
|
66
|
+
* (`-30d`, `7 days ago`), which is the spelling an operator reaching for this
|
|
67
|
+
* usually wants: sign out everyone who signed in more than a month ago.
|
|
63
68
|
*
|
|
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
69
|
* An UNPARSEABLE value is ignored with a warning rather than throwing: this is
|
|
67
70
|
* the switch that logs a fleet back in, and failing to boot over a typo is worse
|
|
68
71
|
* than not rotating.
|
|
69
72
|
*/
|
|
70
|
-
export function
|
|
71
|
-
const raw = configured ?? env.text(
|
|
73
|
+
export function resolveSessionCutoff(configured) {
|
|
74
|
+
const raw = configured ?? env.text(SESSION_CUTOFF_ENV) ?? undefined;
|
|
72
75
|
if (raw === undefined || raw === null || raw === "")
|
|
73
76
|
return 0;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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 });
|
|
77
|
+
const date = object.toDate(raw);
|
|
78
|
+
if (!date) {
|
|
79
|
+
logger.warn(`ignoring unparseable ${env.name(SESSION_CUTOFF_ENV)}`, { value: String(raw) });
|
|
91
80
|
return 0;
|
|
92
81
|
}
|
|
93
|
-
return
|
|
82
|
+
return clampToPast(date.getTime());
|
|
94
83
|
}
|
|
95
84
|
/**
|
|
96
|
-
* Hold the cutoff at "now", because a FUTURE
|
|
85
|
+
* Hold the cutoff at "now", because a FUTURE cutoff would refuse the sessions it
|
|
97
86
|
* is about to mint as well as the old ones - an app nobody can sign in to, from a
|
|
98
87
|
* mistyped year, with the fix hidden behind understanding this flag. Clamped, a
|
|
99
88
|
* future date means what an operator setting it always meant: clear everything
|
|
100
89
|
* outstanding, then carry on.
|
|
101
90
|
*/
|
|
102
|
-
function clampToPast(
|
|
91
|
+
function clampToPast(cutoffMs) {
|
|
103
92
|
const now = Date.now();
|
|
104
|
-
if (
|
|
105
|
-
return
|
|
106
|
-
logger.warn(`${env.name(
|
|
107
|
-
configured: new Date(
|
|
93
|
+
if (cutoffMs <= now)
|
|
94
|
+
return cutoffMs;
|
|
95
|
+
logger.warn(`${env.name(SESSION_CUTOFF_ENV)} is in the future - clamping to now`, {
|
|
96
|
+
configured: new Date(cutoffMs).toISOString(),
|
|
108
97
|
});
|
|
109
98
|
return now;
|
|
110
99
|
}
|
|
111
|
-
/** The cache key for one
|
|
112
|
-
function cacheKey(
|
|
113
|
-
return `${KEY_PREFIX}${
|
|
100
|
+
/** The cache key for one cutoff, so moving the cutoff orphans every earlier key. */
|
|
101
|
+
function cacheKey(cutoffMs) {
|
|
102
|
+
return `${KEY_PREFIX}${cutoffMs}`;
|
|
114
103
|
}
|
|
115
104
|
function decode(stored) {
|
|
116
105
|
return new Uint8Array(Buffer.from(stored.secret, "base64url"));
|
|
117
106
|
}
|
|
118
107
|
/**
|
|
119
|
-
* Load the signing key for `
|
|
120
|
-
* absent. See the module docs for why the write is followed by a re-read.
|
|
108
|
+
* Load the signing key for `cutoffMs` from the cache, minting and storing one
|
|
109
|
+
* when absent. See the module docs for why the write is followed by a re-read.
|
|
121
110
|
*/
|
|
122
|
-
async function loadFromCache(
|
|
111
|
+
async function loadFromCache(cutoffMs) {
|
|
123
112
|
const cache = CacheManager.getInstanceSync();
|
|
124
|
-
const key = cacheKey(
|
|
113
|
+
const key = cacheKey(cutoffMs);
|
|
125
114
|
const existing = await cache.get(key);
|
|
126
115
|
if (existing?.secret) {
|
|
127
116
|
logger.info("reusing cached signing key", { createdAt: existing.createdAt });
|
|
@@ -147,7 +136,7 @@ let pending;
|
|
|
147
136
|
/**
|
|
148
137
|
* The signing key for this gate, resolved once per process.
|
|
149
138
|
*
|
|
150
|
-
* Resolved ONCE, so `
|
|
139
|
+
* Resolved ONCE, so `configuredCutoff` is honoured only on the first call - the
|
|
151
140
|
* plugin's `setup()` passes its resolved value there, before any request can
|
|
152
141
|
* reach the lazy path.
|
|
153
142
|
*
|
|
@@ -157,19 +146,19 @@ let pending;
|
|
|
157
146
|
* so losing it costs sessions, never admission - a caller still needs a code
|
|
158
147
|
* delivered to an allow-listed address.
|
|
159
148
|
*/
|
|
160
|
-
export function signingKey(
|
|
149
|
+
export function signingKey(configuredCutoff) {
|
|
161
150
|
pending ??= (async () => {
|
|
162
|
-
const
|
|
151
|
+
const cutoffMs = resolveSessionCutoff(configuredCutoff);
|
|
163
152
|
const configured = env.text(JWT_SECRET_ENV);
|
|
164
153
|
if (configured) {
|
|
165
|
-
return { key: new TextEncoder().encode(configured),
|
|
154
|
+
return { key: new TextEncoder().encode(configured), cutoffMs };
|
|
166
155
|
}
|
|
167
156
|
try {
|
|
168
|
-
return { key: await loadFromCache(
|
|
157
|
+
return { key: await loadFromCache(cutoffMs), cutoffMs };
|
|
169
158
|
}
|
|
170
159
|
catch (error) {
|
|
171
160
|
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)),
|
|
161
|
+
return { key: new Uint8Array(randomBytes(KEY_BYTES)), cutoffMs };
|
|
173
162
|
}
|
|
174
163
|
})();
|
|
175
164
|
return pending;
|
|
@@ -178,4 +167,4 @@ export function signingKey(configuredEpoch) {
|
|
|
178
167
|
export function resetSigningKey() {
|
|
179
168
|
pending = undefined;
|
|
180
169
|
}
|
|
181
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbmluZy1rZXkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2lnbmluZy1rZXkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBDRztBQUVILE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDMUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUU3RCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFFaEQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUVqRCxrRkFBa0Y7QUFDbEYsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUM7QUFFOUMsa0ZBQWtGO0FBQ2xGLE1BQU0sU0FBUyxHQUFHLEVBQUUsQ0FBQztBQVVyQjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxVQUFtQztJQUNyRSxNQUFNLEdBQUcsR0FBRyxVQUFVLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLFNBQVMsQ0FBQztJQUNuRSxJQUFJLEdBQUcsS0FBSyxTQUFTLElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxHQUFHLEtBQUssRUFBRTtRQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzlELE9BQU8sV0FBVyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQ3RDLENBQUM7QUFFRCxzRUFBc0U7QUFDdEUsU0FBUyxVQUFVLENBQUMs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RyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMseUdBQXlHLEVBQ3ZJLEVBQUUsS0FBSyxFQUFFLENBQ1YsQ0FBQztZQUNGLE9BQU8sRUFBRSxHQUFHLEVBQUUsSUFBSSxVQUFVLENBQUMsV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDbEUsQ0FBQztJQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDTCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FBRUQsK0VBQStFO0FBQy9FLE1BQU0sVUFBVSxlQUFlO0lBQzdCLE9BQU8sR0FBRyxTQUFTLENBQUM7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhlIGdhdGUncyBIUzI1NiBzZXNzaW9uLXNpZ25pbmcga2V5LCBwZXJzaXN0ZWQgaW4gQXBwS2l0J3MgY2FjaGUuXG4gKlxuICogVGhlIGtleSBkZWNpZGVzIHdoZXRoZXIgYSBzZXNzaW9uIENPT0tJRSBzdGlsbCB2ZXJpZmllcy4gQmVmb3JlIHRoaXMgbW9kdWxlIHRoZVxuICogZmFsbGJhY2sgd2FzIGByYW5kb21CeXRlcygzMilgIHBlciBwcm9jZXNzLCBzbyBldmVyeSByZXN0YXJ0IGludmFsaWRhdGVkIGV2ZXJ5XG4gKiBvdXRzdGFuZGluZyBjb29raWUgYW5kIGVhY2ggc2lnbmVkLWluIHVzZXIgaGFkIHRvIHJlcXVlc3QgYSBuZXcgY29kZSAtIHBhaW5mdWxcbiAqIGZvciBhIHR1bm5lbCwgd2hpY2ggcmVzdGFydHMgd2hlbmV2ZXIgdGhlIGFwcCBpdCB3cmFwcyBkb2VzLiBOb3cgdGhlIGtleSBpc1xuICogc3RvcmVkIGluIHRoZSBjYWNoZSBBcHBLaXQgYWxyZWFkeSBjb25maWd1cmVkIChtZW1vcnksIG9yIExha2ViYXNlIHdoZW4gdGhlXG4gKiBob3N0IHdpcmVzIGEgcGVyc2lzdGVudCBgQ2FjaGVTdG9yYWdlYCksIHNvIHdpdGggcGVyc2lzdGVudCBzdG9yYWdlIGEgcmVzdGFydFxuICoga2VlcHMgc2Vzc2lvbnMgYWxpdmUgZm9yIHtAbGluayBLRVlfVFRMX1NFQ09ORFN9LlxuICpcbiAqIEFuIGV4cGxpY2l0bHkgY29uZmlndXJlZCBgVFVOTkVMX0FVVEhfSldUX1NFQ1JFVGAgc3RpbGwgd2lucyBvdXRyaWdodC4gVGhhdCBpc1xuICogdGhlIHJpZ2h0IGFuc3dlciBmb3IgYSBmbGVldDogYW4gb3BlcmF0b3ItaGVsZCBzZWNyZXQgbmVlZHMgbm8gc2hhcmVkIGNhY2hlIGFuZFxuICogbm8gY29udmVyZ2VuY2UsIGFuZCBpdCBzdXJ2aXZlcyBhIGNhY2hlIGZsdXNoLlxuICpcbiAqICMjIGdldCAvIGdlbmVyYXRlIC8gZ2V0XG4gKlxuICogVHdvIGluc3RhbmNlcyBib290aW5nIGF0IG9uY2UgYm90aCBtaXNzLCBzbyBib3RoIHdvdWxkIGdlbmVyYXRlIC0gYW5kIHRoZSBsb3NlclxuICogd291bGQgc2lnbiBjb29raWVzIHdpdGggYSBrZXkgdGhlIHdpbm5lciByZWplY3RzLiBSZXNvbHV0aW9uIGlzIGEgcmUtUkVBRCBhZnRlclxuICogdGhlIHdyaXRlOiB3aGF0ZXZlciB0aGUgY2FjaGUgaG9sZHMgYWZ0ZXJ3YXJkcyBpcyB0aGUga2V5IGV2ZXJ5b25lIGFkb3B0cywgc29cbiAqIHRoZSBpbnN0YW5jZXMgY29udmVyZ2Ugb24gT05FIHZhbHVlIGluc3RlYWQgb2YgdHJ1c3RpbmcgdGhlIG9uZSB0aGV5IG1pbnRlZC5cbiAqIChgc2V0YCBpcyBub3QgY29uZGl0aW9uYWwgaW4gdGhlIGNhY2hlIEFQSSAtIHRoZXJlIGlzIG5vIGBzZXRueGAgdG8gbGVhbiBvbiAtXG4gKiBhbmQgdGhpcyBydW5zIG9uY2UgcGVyIHByb2Nlc3MsIHNvIHRoZSBleHRyYSByb3VuZC10cmlwIGlzIGZyZWUuKVxuICpcbiAqIEEgcGF0aG9sb2dpY2FsIGludGVybGVhdmUgY2FuIHN0aWxsIGNvc3QgYSBrZXk6IGlmIEEgd3JpdGVzIGJldHdlZW4gQidzIHdyaXRlXG4gKiBhbmQgQidzIHJlLXJlYWQsIEIgYWRvcHRzIEEncyBrZXkgd2hpbGUgQSBhZG9wdHMgaXRzIG93bi4gVGhlIGNvc3QgaXMgYm91bmRlZCAtXG4gKiBhIGNvb2tpZSBtaW50ZWQgaW4gdGhhdCB3aW5kb3cgZmFpbHMgdG8gdmVyaWZ5IGFuZCB0aGUgaG9sZGVyIHNpZ25zIGluIGFnYWluIC1cbiAqIGFuZCBpdCBjYW5ub3QgcHJvZHVjZSBhIGtleSBvbmUgaW5zdGFuY2UgVFJVU1RTIGJ1dCBhbm90aGVyIHJlamVjdHMgZm9yIGxvbmdlclxuICogdGhhbiB0aGUgd2luZG93IGl0c2VsZi4gU2V0IGBUVU5ORUxfQVVUSF9KV1RfU0VDUkVUYCB0byByZW1vdmUgdGhlIHJhY2VcbiAqIGVudGlyZWx5LlxuICpcbiAqICMjIEZvcmNpbmcgZXZlcnkgc2Vzc2lvbiB0byBlbmRcbiAqXG4gKiB7QGxpbmsgcmVzb2x2ZVNlc3Npb25FcG9jaH0gcmVhZHMgYSBkYXRlIGZyb20gYFRVTk5FTF9BVVRIX1NFU1NJT05fRVBPQ0hgIChvclxuICogYC0tc2Vzc2lvbi1lcG9jaGApLCBhbmQgdGhhdCBkYXRlIGlzIHBhcnQgb2YgdGhlIGNhY2hlIEtFWS4gTW92aW5nIGl0IGZvcndhcmRcbiAqIG1ha2VzIGV2ZXJ5IHByaW9yIGtleSB1bnJlYWNoYWJsZSwgc28gZXZlcnkgY29va2llIHNpZ25lZCBhZ2FpbnN0IGl0IHN0b3BzXG4gKiB2ZXJpZnlpbmcgLSB0aGUgbG9nLWV2ZXJ5b25lLW91dCBzd2l0Y2gsIHdpdGhvdXQgaGF2aW5nIHRvIGZpbmQgYW5kIGZsdXNoIGFcbiAqIGNhY2hlIGVudHJ5LiBUaGUgZXBvY2ggaXMgYWxzbyBhc3NlcnRlZCBhZ2FpbnN0IGVhY2ggdG9rZW4ncyBgaWF0YCwgc28gYVxuICogY29va2llIHRoYXQgcHJlZGF0ZXMgaXQgaXMgcmVmdXNlZCBldmVuIGlmIGl0IHdhcyBzaWduZWQgd2l0aCB0aGUga2V5IHRoYXQgaXNcbiAqIHNvbWVob3cgc3RpbGwgY3VycmVudC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgcmFuZG9tQnl0ZXMgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IENhY2hlTWFuYWdlciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IGVudiwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IEpXVF9TRUNSRVRfRU5WLCBTRVNTSU9OX0VQT0NIX0VOViB9IGZyb20gXCIuL2Vudi50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsOnNpZ25pbmcta2V5XCIpO1xuXG4vKipcbiAqIEhvdyBsb25nIGEgY2FjaGVkIHNpZ25pbmcga2V5IGxpdmVzOiAzMCBkYXlzLlxuICpcbiAqIFRoaXMgaXMgdGhlIGNlaWxpbmcgb24gaG93IGxvbmcgYSBzZXNzaW9uIGNvb2tpZSBjYW4gc3RheSB2YWxpZCBhY3Jvc3NcbiAqIHJlc3RhcnRzLCBzbyBpdCBpcyBkZWxpYmVyYXRlbHkgPj0gdGhlIGRlZmF1bHQgc2Vzc2lvbiBUVEwgLSBhIGtleSB0aGF0IGV4cGlyZWRcbiAqIGJlZm9yZSB0aGUgY29va2llcyBpdCBzaWduZWQgd291bGQgbG9nIGV2ZXJ5b25lIG91dCBmb3Igbm8gcmVhc29uLlxuICovXG5leHBvcnQgY29uc3QgS0VZX1RUTF9TRUNPTkRTID0gMzAgKiAyNCAqIDYwICogNjA7XG5cbi8qKiBDYWNoZS1rZXkgcHJlZml4IGZvciB0aGUgc2lnbmluZyBrZXksIG5hbWVzcGFjZWQgYXdheSBmcm9tIG90aGVyIGNhY2hlIHVzZS4gKi9cbmNvbnN0IEtFWV9QUkVGSVggPSBcInR1bm5lbDphdXRoOnNpZ25pbmcta2V5OlwiO1xuXG4vKiogQnl0ZXMgb2YgZW50cm9weSBpbiBhIGdlbmVyYXRlZCBrZXkgKDI1Ni1iaXQsIG1hdGNoaW5nIEhTMjU2J3MgaGFzaCB3aWR0aCkuICovXG5jb25zdCBLRVlfQllURVMgPSAzMjtcblxuLyoqIFdoYXQgdGhlIGNhY2hlIHN0b3JlczogdGhlIGtleSBwbHVzIHdoZW4gaXQgd2FzIG1pbnRlZCwgZm9yIG9ic2VydmFiaWxpdHkuICovXG5pbnRlcmZhY2UgU3RvcmVkS2V5IHtcbiAgLyoqIEJhc2U2NHVybCBvZiB0aGUgcmF3IGtleSBieXRlcy4gKi9cbiAgc2VjcmV0OiBzdHJpbmc7XG4gIC8qKiBXaGVuIHRoaXMga2V5IHdhcyBnZW5lcmF0ZWQsIElTTy04NjAxLiAqL1xuICBjcmVhdGVkQXQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBmb3JjZS1jbGVhciBlcG9jaCBhcyBlcG9jaCBNSUxMSVNFQ09ORFMsIG9yIGAwYCB3aGVuIHVuc2V0LlxuICpcbiAqIEFjY2VwdHMgYW55dGhpbmcgYERhdGVgIHBhcnNlcyAoYDIwMjYtMDgtMDJgLCBhbiBJU08gdGltZXN0YW1wKSBwbHVzIGEgYmFyZVxuICogZXBvY2gtc2Vjb25kcyAvIGVwb2NoLW1pbGxpcyBudW1iZXIsIHNvIGEgdmFsdWUgcGFzdGVkIGZyb20gYGRhdGUgKyVzYCB3b3Jrcy5cbiAqIEFuIFVOUEFSU0VBQkxFIHZhbHVlIGlzIGlnbm9yZWQgd2l0aCBhIHdhcm5pbmcgcmF0aGVyIHRoYW4gdGhyb3dpbmc6IHRoaXMgaXNcbiAqIHRoZSBzd2l0Y2ggdGhhdCBsb2dzIGEgZmxlZXQgYmFjayBpbiwgYW5kIGZhaWxpbmcgdG8gYm9vdCBvdmVyIGEgdHlwbyBpcyB3b3JzZVxuICogdGhhbiBub3Qgcm90YXRpbmcuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlU2Vzc2lvbkVwb2NoKGNvbmZpZ3VyZWQ/OiBzdHJpbmcgfCBudW1iZXIgfCBEYXRlKTogbnVtYmVyIHtcbiAgY29uc3QgcmF3ID0gY29uZmlndXJlZCA/PyBlbnYudGV4dChTRVNTSU9OX0VQT0NIX0VOVikgPz8gdW5kZWZpbmVkO1xuICBpZiAocmF3ID09PSB1bmRlZmluZWQgfHwgcmF3ID09PSBudWxsIHx8IHJhdyA9PT0gXCJcIikgcmV0dXJuIDA7XG4gIHJldHVybiBjbGFtcFRvUGFzdChwYXJzZUVwb2NoKHJhdykpO1xufVxuXG4vKiogUGFyc2UgdGhlIGFjY2VwdGVkIGVwb2NoIHNwZWxsaW5ncyB0byBtcywgb3IgYDBgIHdoZW4gdW51c2FibGUuICovXG5mdW5jdGlvbiBwYXJzZUVwb2NoKHJhdzogc3RyaW5nIHwgbnVtYmVyIHwgRGF0ZSk6IG51bWJlciB7XG4gIGlmIChyYXcgaW5zdGFuY2VvZiBEYXRlKSByZXR1cm4gTnVtYmVyLmlzTmFOKHJhdy5nZXRUaW1lKCkpID8gMCA6IHJhdy5nZXRUaW1lKCk7XG5cbiAgY29uc3QgdGV4dCA9IFN0cmluZyhyYXcpLnRyaW0oKTtcbiAgLy8gQSBiYXJlIG51bWJlciBpcyBhIHRpbWVzdGFtcCwgbm90IGEgZGF0ZSBzdHJpbmc6IGBEYXRlLnBhcnNlKFwiMTc4NS4uLlwiKWBcbiAgLy8gd291bGQgcmVhZCBpdCBhcyBhIFlFQVIuIFZhbHVlcyBiZWxvdyB+MWUxMSBhcmUgc2Vjb25kcyAoYW55IG1pbGxpc1xuICAvLyB0aW1lc3RhbXAgc2luY2UgMTk3MyBpcyBsYXJnZXIpLCB3aGljaCBpcyB3aGF0IGBkYXRlICslc2AgcHJpbnRzLlxuICBpZiAoL15cXGQrJC8udGVzdCh0ZXh0KSkge1xuICAgIGNvbnN0IG51bWVyaWMgPSBOdW1iZXIodGV4dCk7XG4gICAgcmV0dXJuIG51bWVyaWMgPCAxZTExID8gbnVtZXJpYyAqIDEwMDAgOiBudW1lcmljO1xuICB9XG4gIGNvbnN0IHBhcnNlZCA9IERhdGUucGFyc2UodGV4dCk7XG4gIGlmIChOdW1iZXIuaXNOYU4ocGFyc2VkKSkge1xuICAgIGxvZ2dlci53YXJuKGBpZ25vcmluZyB1bnBhcnNlYWJsZSAke2Vudi5uYW1lKFNFU1NJT05fRVBPQ0hfRU5WKX1gLCB7IHZhbHVlOiB0ZXh0IH0pO1xuICAgIHJldHVybiAwO1xuICB9XG4gIHJldHVybiBwYXJzZWQ7XG59XG5cbi8qKlxuICogSG9sZCB0aGUgY3V0b2ZmIGF0IFwibm93XCIsIGJlY2F1c2UgYSBGVVRVUkUgZXBvY2ggd291bGQgcmVmdXNlIHRoZSBzZXNzaW9ucyBpdFxuICogaXMgYWJvdXQgdG8gbWludCBhcyB3ZWxsIGFzIHRoZSBvbGQgb25lcyAtIGFuIGFwcCBub2JvZHkgY2FuIHNpZ24gaW4gdG8sIGZyb20gYVxuICogbWlzdHlwZWQgeWVhciwgd2l0aCB0aGUgZml4IGhpZGRlbiBiZWhpbmQgdW5kZXJzdGFuZGluZyB0aGlzIGZsYWcuIENsYW1wZWQsIGFcbiAqIGZ1dHVyZSBkYXRlIG1lYW5zIHdoYXQgYW4gb3BlcmF0b3Igc2V0dGluZyBpdCBhbHdheXMgbWVhbnQ6IGNsZWFyIGV2ZXJ5dGhpbmdcbiAqIG91dHN0YW5kaW5nLCB0aGVuIGNhcnJ5IG9uLlxuICovXG5mdW5jdGlvbiBjbGFtcFRvUGFzdChlcG9jaE1zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBpZiAoZXBvY2hNcyA8PSBub3cpIHJldHVybiBlcG9jaE1zO1xuICBsb2dnZXIud2FybihgJHtlbnYubmFtZShTRVNTSU9OX0VQT0NIX0VOVil9IGlzIGluIHRoZSBmdXR1cmUgLSBjbGFtcGluZyB0byBub3dgLCB7XG4gICAgY29uZmlndXJlZDogbmV3IERhdGUoZXBvY2hNcykudG9JU09TdHJpbmcoKSxcbiAgfSk7XG4gIHJldHVybiBub3c7XG59XG5cbi8qKiBUaGUgY2FjaGUga2V5IGZvciBvbmUgZXBvY2gsIHNvIG1vdmluZyB0aGUgZXBvY2ggb3JwaGFucyBldmVyeSBlYXJsaWVyIGtleS4gKi9cbmZ1bmN0aW9uIGNhY2hlS2V5KGVwb2NoTXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBgJHtLRVlfUFJFRklYfSR7ZXBvY2hNc31gO1xufVxuXG5mdW5jdGlvbiBkZWNvZGUoc3RvcmVkOiBTdG9yZWRLZXkpOiBVaW50OEFycmF5IHtcbiAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEJ1ZmZlci5mcm9tKHN0b3JlZC5zZWNyZXQsIFwiYmFzZTY0dXJsXCIpKTtcbn1cblxuLyoqXG4gKiBMb2FkIHRoZSBzaWduaW5nIGtleSBmb3IgYGVwb2NoTXNgIGZyb20gdGhlIGNhY2hlLCBtaW50aW5nIGFuZCBzdG9yaW5nIG9uZSB3aGVuXG4gKiBhYnNlbnQuIFNlZSB0aGUgbW9kdWxlIGRvY3MgZm9yIHdoeSB0aGUgd3JpdGUgaXMgZm9sbG93ZWQgYnkgYSByZS1yZWFkLlxuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkRnJvbUNhY2hlKGVwb2NoTXM6IG51bWJlcik6IFByb21pc2U8VWludDhBcnJheT4ge1xuICBjb25zdCBjYWNoZSA9IENhY2hlTWFuYWdlci5nZXRJbnN0YW5jZVN5bmMoKTtcbiAgY29uc3Qga2V5ID0gY2FjaGVLZXkoZXBvY2hNcyk7XG5cbiAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBjYWNoZS5nZXQ8U3RvcmVkS2V5PihrZXkpO1xuICBpZiAoZXhpc3Rpbmc/LnNlY3JldCkge1xuICAgIGxvZ2dlci5pbmZvKFwicmV1c2luZyBjYWNoZWQgc2lnbmluZyBrZXlcIiwgeyBjcmVhdGVkQXQ6IGV4aXN0aW5nLmNyZWF0ZWRBdCB9KTtcbiAgICByZXR1cm4gZGVjb2RlKGV4aXN0aW5nKTtcbiAgfVxuXG4gIGNvbnN0IG1pbnRlZDogU3RvcmVkS2V5ID0ge1xuICAgIHNlY3JldDogQnVmZmVyLmZyb20ocmFuZG9tQnl0ZXMoS0VZX0JZVEVTKSkudG9TdHJpbmcoXCJiYXNlNjR1cmxcIiksXG4gICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gIH07XG4gIGF3YWl0IGNhY2hlLnNldChrZXksIG1pbnRlZCwgeyB0dGw6IEtFWV9UVExfU0VDT05EUyB9KTtcblxuICAvLyBSZS1yZWFkIHJhdGhlciB0aGFuIHRydXN0aW5nIGBtaW50ZWRgOiBhbm90aGVyIGluc3RhbmNlIHRoYXQgcmFjZWQgdGhpcyBib290XG4gIC8vIG1heSBoYXZlIHdyaXR0ZW4gZmlyc3QsIGFuZCBhZG9wdGluZyB3aGF0ZXZlciBpcyBzdG9yZWQgaXMgd2hhdCBtYWtlcyB0aGUgdHdvXG4gIC8vIGNvbnZlcmdlIG9uIG9uZSBrZXkuXG4gIGNvbnN0IHNldHRsZWQgPSAoYXdhaXQgY2FjaGUuZ2V0PFN0b3JlZEtleT4oa2V5KSkgPz8gbWludGVkO1xuICBsb2dnZXIuaW5mbyhcInN0b3JlZCBhIG5ldyBzaWduaW5nIGtleVwiLCB7XG4gICAgY3JlYXRlZEF0OiBzZXR0bGVkLmNyZWF0ZWRBdCxcbiAgICB0dGxTZWNvbmRzOiBLRVlfVFRMX1NFQ09ORFMsXG4gICAgYWRvcHRlZDogc2V0dGxlZC5jcmVhdGVkQXQgPT09IG1pbnRlZC5jcmVhdGVkQXQgPyBcIm93blwiIDogXCJjb25jdXJyZW50LWluc3RhbmNlXCIsXG4gIH0pO1xuICByZXR1cm4gZGVjb2RlKHNldHRsZWQpO1xufVxuXG4vKiogQSByZXNvbHZlZCBrZXkgcGx1cyB0aGUgZXBvY2ggaXQgaXMgc2NvcGVkIHRvLiAqL1xuZXhwb3J0IGludGVyZmFjZSBTaWduaW5nS2V5IHtcbiAga2V5OiBVaW50OEFycmF5O1xuICAvKiogRm9yY2UtY2xlYXIgZXBvY2ggaW4gbXM7IGAwYCB3aGVuIHVuc2V0LiBUb2tlbnMgb2xkZXIgdGhhbiB0aGlzIGFyZSByZWZ1c2VkLiAqL1xuICBlcG9jaE1zOiBudW1iZXI7XG59XG5cbmxldCBwZW5kaW5nOiBQcm9taXNlPFNpZ25pbmdLZXk+IHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIFRoZSBzaWduaW5nIGtleSBmb3IgdGhpcyBnYXRlLCByZXNvbHZlZCBvbmNlIHBlciBwcm9jZXNzLlxuICpcbiAqIFJlc29sdmVkIE9OQ0UsIHNvIGBjb25maWd1cmVkRXBvY2hgIGlzIGhvbm91cmVkIG9ubHkgb24gdGhlIGZpcnN0IGNhbGwgLSB0aGVcbiAqIHBsdWdpbidzIGBzZXR1cCgpYCBwYXNzZXMgaXRzIHJlc29sdmVkIHZhbHVlIHRoZXJlLCBiZWZvcmUgYW55IHJlcXVlc3QgY2FuXG4gKiByZWFjaCB0aGUgbGF6eSBwYXRoLlxuICpcbiAqIGBUVU5ORUxfQVVUSF9KV1RfU0VDUkVUYCB3aGVuIHNldCwgZWxzZSB0aGUgY2FjaGUtYmFja2VkIGtleS4gQSBjYWNoZSB0aGF0IGlzXG4gKiB1bmF2YWlsYWJsZSBkZWdyYWRlcyB0byBhbiBlcGhlbWVyYWwgcGVyLXByb2Nlc3Mga2V5ICh0aGUgcHJldmlvdXMgYmVoYXZpb3VyKVxuICogcmF0aGVyIHRoYW4gcmVmdXNpbmcgdG8gc2lnbjogdGhlIGtleSBvbmx5IHZhbGlkYXRlcyBhbiBBTFJFQURZLWlzc3VlZCBzZXNzaW9uLFxuICogc28gbG9zaW5nIGl0IGNvc3RzIHNlc3Npb25zLCBuZXZlciBhZG1pc3Npb24gLSBhIGNhbGxlciBzdGlsbCBuZWVkcyBhIGNvZGVcbiAqIGRlbGl2ZXJlZCB0byBhbiBhbGxvdy1saXN0ZWQgYWRkcmVzcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNpZ25pbmdLZXkoY29uZmlndXJlZEVwb2NoPzogc3RyaW5nIHwgbnVtYmVyIHwgRGF0ZSk6IFByb21pc2U8U2lnbmluZ0tleT4ge1xuICBwZW5kaW5nID8/PSAoYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGVwb2NoTXMgPSByZXNvbHZlU2Vzc2lvbkVwb2NoKGNvbmZpZ3VyZWRFcG9jaCk7XG4gICAgY29uc3QgY29uZmlndXJlZCA9IGVudi50ZXh0KEpXVF9TRUNSRVRfRU5WKTtcbiAgICBpZiAoY29uZmlndXJlZCkge1xuICAgICAgcmV0dXJuIHsga2V5OiBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUoY29uZmlndXJlZCksIGVwb2NoTXMgfTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiB7IGtleTogYXdhaXQgbG9hZEZyb21DYWNoZShlcG9jaE1zKSwgZXBvY2hNcyB9O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIud2FybihcbiAgICAgICAgYG5vICR7ZW52Lm5hbWUoSldUX1NFQ1JFVF9FTlYpfSBhbmQgdGhlIGNhY2hlIGlzIHVuYXZhaWxhYmxlIC0gdXNpbmcgYW4gZXBoZW1lcmFsIHBlci1wcm9jZXNzIGtleTsgc2Vzc2lvbnMgd2lsbCBub3Qgc3Vydml2ZSBhIHJlc3RhcnRgLFxuICAgICAgICB7IGVycm9yIH0sXG4gICAgICApO1xuICAgICAgcmV0dXJuIHsga2V5OiBuZXcgVWludDhBcnJheShyYW5kb21CeXRlcyhLRVlfQllURVMpKSwgZXBvY2hNcyB9O1xuICAgIH1cbiAgfSkoKTtcbiAgcmV0dXJuIHBlbmRpbmc7XG59XG5cbi8qKiBSZXNldCB0aGUgcGVyLXByb2Nlc3Mga2V5ICh0ZXN0cywgb3IgYWZ0ZXIgY2hhbmdpbmcgdGhlIGVudiBpbi1wcm9jZXNzKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNldFNpZ25pbmdLZXkoKTogdm9pZCB7XG4gIHBlbmRpbmcgPSB1bmRlZmluZWQ7XG59XG4iXX0=
|
|
170
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbmluZy1rZXkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2lnbmluZy1rZXkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBDRztBQUVILE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDMUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzFELE9BQU8sRUFBRSxjQUFjLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFOUQsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBRWhEOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFakQsa0ZBQWtGO0FBQ2xGLE1BQU0sVUFBVSxHQUFHLDBCQUEwQixDQUFDO0FBRTlDLGtGQUFrRjtBQUNsRixNQUFNLFNBQVMsR0FBRyxFQUFFLENBQUM7QUFVckI7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQUMsVUFBbUM7SUFDdEUsTUFBTSxHQUFHLEdBQUcsVUFBVSxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxTQUFTLENBQUM7SUFDcEUsSUFBSSxHQUFHLEtBQUssU0FBUyxJQUFJLEdBQUcsS0FBSyxJQUFJLElBQUksR0FBRyxLQUFLLEVBQUU7UUFBRSxPQUFPLENBQUMsQ0FBQztJQUU5RCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hDLElBQUksQ0FBQyxJQUFJLEVBQUUs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LE9BQU8sS0FBSyxDQUFDLEtBQUssSUFBSSxFQUFFO1FBQ3RCLE1BQU0sUUFBUSxHQUFHLG9CQUFvQixDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDeEQsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUM1QyxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ2YsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLFdBQVcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQztRQUNqRSxDQUFDO1FBQ0QsSUFBSSxDQUFDO1lBQ0gsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGFBQWEsQ0FBQyxRQUFRLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQztRQUMxRCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxJQUFJLENBQ1QsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyx5R0FBeUcsRUFDdkksRUFBRSxLQUFLLEVBQUUsQ0FDVixDQUFDO1lBQ0YsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLFVBQVUsQ0FBQyxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQztRQUNuRSxDQUFDO0lBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUNMLE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCwrRUFBK0U7QUFDL0UsTUFBTSxVQUFVLGVBQWU7SUFDN0IsT0FBTyxHQUFHLFNBQVMsQ0FBQztBQUN0QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGUgZ2F0ZSdzIEhTMjU2IHNlc3Npb24tc2lnbmluZyBrZXksIHBlcnNpc3RlZCBpbiBBcHBLaXQncyBjYWNoZS5cbiAqXG4gKiBUaGUga2V5IGRlY2lkZXMgd2hldGhlciBhIHNlc3Npb24gQ09PS0lFIHN0aWxsIHZlcmlmaWVzLiBCZWZvcmUgdGhpcyBtb2R1bGUgdGhlXG4gKiBmYWxsYmFjayB3YXMgYHJhbmRvbUJ5dGVzKDMyKWAgcGVyIHByb2Nlc3MsIHNvIGV2ZXJ5IHJlc3RhcnQgaW52YWxpZGF0ZWQgZXZlcnlcbiAqIG91dHN0YW5kaW5nIGNvb2tpZSBhbmQgZWFjaCBzaWduZWQtaW4gdXNlciBoYWQgdG8gcmVxdWVzdCBhIG5ldyBjb2RlIC0gcGFpbmZ1bFxuICogZm9yIGEgdHVubmVsLCB3aGljaCByZXN0YXJ0cyB3aGVuZXZlciB0aGUgYXBwIGl0IHdyYXBzIGRvZXMuIE5vdyB0aGUga2V5IGlzXG4gKiBzdG9yZWQgaW4gdGhlIGNhY2hlIEFwcEtpdCBhbHJlYWR5IGNvbmZpZ3VyZWQgKG1lbW9yeSwgb3IgTGFrZWJhc2Ugd2hlbiB0aGVcbiAqIGhvc3Qgd2lyZXMgYSBwZXJzaXN0ZW50IGBDYWNoZVN0b3JhZ2VgKSwgc28gd2l0aCBwZXJzaXN0ZW50IHN0b3JhZ2UgYSByZXN0YXJ0XG4gKiBrZWVwcyBzZXNzaW9ucyBhbGl2ZSBmb3Ige0BsaW5rIEtFWV9UVExfU0VDT05EU30uXG4gKlxuICogQW4gZXhwbGljaXRseSBjb25maWd1cmVkIGBUVU5ORUxfQVVUSF9KV1RfU0VDUkVUYCBzdGlsbCB3aW5zIG91dHJpZ2h0LiBUaGF0IGlzXG4gKiB0aGUgcmlnaHQgYW5zd2VyIGZvciBhIGZsZWV0OiBhbiBvcGVyYXRvci1oZWxkIHNlY3JldCBuZWVkcyBubyBzaGFyZWQgY2FjaGUgYW5kXG4gKiBubyBjb252ZXJnZW5jZSwgYW5kIGl0IHN1cnZpdmVzIGEgY2FjaGUgZmx1c2guXG4gKlxuICogIyMgZ2V0IC8gZ2VuZXJhdGUgLyBnZXRcbiAqXG4gKiBUd28gaW5zdGFuY2VzIGJvb3RpbmcgYXQgb25jZSBib3RoIG1pc3MsIHNvIGJvdGggd291bGQgZ2VuZXJhdGUgLSBhbmQgdGhlIGxvc2VyXG4gKiB3b3VsZCBzaWduIGNvb2tpZXMgd2l0aCBhIGtleSB0aGUgd2lubmVyIHJlamVjdHMuIFJlc29sdXRpb24gaXMgYSByZS1SRUFEIGFmdGVyXG4gKiB0aGUgd3JpdGU6IHdoYXRldmVyIHRoZSBjYWNoZSBob2xkcyBhZnRlcndhcmRzIGlzIHRoZSBrZXkgZXZlcnlvbmUgYWRvcHRzLCBzb1xuICogdGhlIGluc3RhbmNlcyBjb252ZXJnZSBvbiBPTkUgdmFsdWUgaW5zdGVhZCBvZiB0cnVzdGluZyB0aGUgb25lIHRoZXkgbWludGVkLlxuICogKGBzZXRgIGlzIG5vdCBjb25kaXRpb25hbCBpbiB0aGUgY2FjaGUgQVBJIC0gdGhlcmUgaXMgbm8gYHNldG54YCB0byBsZWFuIG9uIC1cbiAqIGFuZCB0aGlzIHJ1bnMgb25jZSBwZXIgcHJvY2Vzcywgc28gdGhlIGV4dHJhIHJvdW5kLXRyaXAgaXMgZnJlZS4pXG4gKlxuICogQSBwYXRob2xvZ2ljYWwgaW50ZXJsZWF2ZSBjYW4gc3RpbGwgY29zdCBhIGtleTogaWYgQSB3cml0ZXMgYmV0d2VlbiBCJ3Mgd3JpdGVcbiAqIGFuZCBCJ3MgcmUtcmVhZCwgQiBhZG9wdHMgQSdzIGtleSB3aGlsZSBBIGFkb3B0cyBpdHMgb3duLiBUaGUgY29zdCBpcyBib3VuZGVkIC1cbiAqIGEgY29va2llIG1pbnRlZCBpbiB0aGF0IHdpbmRvdyBmYWlscyB0byB2ZXJpZnkgYW5kIHRoZSBob2xkZXIgc2lnbnMgaW4gYWdhaW4gLVxuICogYW5kIGl0IGNhbm5vdCBwcm9kdWNlIGEga2V5IG9uZSBpbnN0YW5jZSBUUlVTVFMgYnV0IGFub3RoZXIgcmVqZWN0cyBmb3IgbG9uZ2VyXG4gKiB0aGFuIHRoZSB3aW5kb3cgaXRzZWxmLiBTZXQgYFRVTk5FTF9BVVRIX0pXVF9TRUNSRVRgIHRvIHJlbW92ZSB0aGUgcmFjZVxuICogZW50aXJlbHkuXG4gKlxuICogIyMgRm9yY2luZyBldmVyeSBzZXNzaW9uIHRvIGVuZFxuICpcbiAqIHtAbGluayByZXNvbHZlU2Vzc2lvbkN1dG9mZn0gcmVhZHMgYSBkYXRlIGZyb20gYFRVTk5FTF9BVVRIX1NFU1NJT05fQ1VUT0ZGYCAob3JcbiAqIGAtLXNlc3Npb24tY3V0b2ZmYCksIGFuZCB0aGF0IGRhdGUgaXMgcGFydCBvZiB0aGUgY2FjaGUgS0VZLiBNb3ZpbmcgaXQgZm9yd2FyZFxuICogbWFrZXMgZXZlcnkgcHJpb3Iga2V5IHVucmVhY2hhYmxlLCBzbyBldmVyeSBjb29raWUgc2lnbmVkIGFnYWluc3QgaXQgc3RvcHNcbiAqIHZlcmlmeWluZyAtIHRoZSBsb2ctZXZlcnlvbmUtb3V0IHN3aXRjaCwgd2l0aG91dCBoYXZpbmcgdG8gZmluZCBhbmQgZmx1c2ggYVxuICogY2FjaGUgZW50cnkuIFRoZSBjdXRvZmYgaXMgYWxzbyBhc3NlcnRlZCBhZ2FpbnN0IGVhY2ggdG9rZW4ncyBgaWF0YCwgc28gYVxuICogY29va2llIHRoYXQgcHJlZGF0ZXMgaXQgaXMgcmVmdXNlZCBldmVuIGlmIGl0IHdhcyBzaWduZWQgd2l0aCB0aGUga2V5IHRoYXQgaXNcbiAqIHNvbWVob3cgc3RpbGwgY3VycmVudC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgcmFuZG9tQnl0ZXMgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IENhY2hlTWFuYWdlciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IGVudiwgbG9nLCBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgSldUX1NFQ1JFVF9FTlYsIFNFU1NJT05fQ1VUT0ZGX0VOViB9IGZyb20gXCIuL2Vudi50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsOnNpZ25pbmcta2V5XCIpO1xuXG4vKipcbiAqIEhvdyBsb25nIGEgY2FjaGVkIHNpZ25pbmcga2V5IGxpdmVzOiAzMCBkYXlzLlxuICpcbiAqIFRoaXMgaXMgdGhlIGNlaWxpbmcgb24gaG93IGxvbmcgYSBzZXNzaW9uIGNvb2tpZSBjYW4gc3RheSB2YWxpZCBhY3Jvc3NcbiAqIHJlc3RhcnRzLCBzbyBpdCBpcyBkZWxpYmVyYXRlbHkgPj0gdGhlIGRlZmF1bHQgc2Vzc2lvbiBUVEwgLSBhIGtleSB0aGF0IGV4cGlyZWRcbiAqIGJlZm9yZSB0aGUgY29va2llcyBpdCBzaWduZWQgd291bGQgbG9nIGV2ZXJ5b25lIG91dCBmb3Igbm8gcmVhc29uLlxuICovXG5leHBvcnQgY29uc3QgS0VZX1RUTF9TRUNPTkRTID0gMzAgKiAyNCAqIDYwICogNjA7XG5cbi8qKiBDYWNoZS1rZXkgcHJlZml4IGZvciB0aGUgc2lnbmluZyBrZXksIG5hbWVzcGFjZWQgYXdheSBmcm9tIG90aGVyIGNhY2hlIHVzZS4gKi9cbmNvbnN0IEtFWV9QUkVGSVggPSBcInR1bm5lbDphdXRoOnNpZ25pbmcta2V5OlwiO1xuXG4vKiogQnl0ZXMgb2YgZW50cm9weSBpbiBhIGdlbmVyYXRlZCBrZXkgKDI1Ni1iaXQsIG1hdGNoaW5nIEhTMjU2J3MgaGFzaCB3aWR0aCkuICovXG5jb25zdCBLRVlfQllURVMgPSAzMjtcblxuLyoqIFdoYXQgdGhlIGNhY2hlIHN0b3JlczogdGhlIGtleSBwbHVzIHdoZW4gaXQgd2FzIG1pbnRlZCwgZm9yIG9ic2VydmFiaWxpdHkuICovXG5pbnRlcmZhY2UgU3RvcmVkS2V5IHtcbiAgLyoqIEJhc2U2NHVybCBvZiB0aGUgcmF3IGtleSBieXRlcy4gKi9cbiAgc2VjcmV0OiBzdHJpbmc7XG4gIC8qKiBXaGVuIHRoaXMga2V5IHdhcyBnZW5lcmF0ZWQsIElTTy04NjAxLiAqL1xuICBjcmVhdGVkQXQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBmb3JjZS1jbGVhciBjdXRvZmYgYXMgZXBvY2ggTUlMTElTRUNPTkRTLCBvciBgMGAgd2hlbiB1bnNldC5cbiAqXG4gKiBBY2NlcHRzIHdoYXRldmVyIGBvYmplY3QudG9EYXRlYCBhY2NlcHRzIC0gYSBgRGF0ZWAsIGAyMDI2LTA4LTAyYCwgYW4gSVNPXG4gKiBpbnN0YW50LCBlcG9jaCBzZWNvbmRzIG9yIG1pbGxpcyBmcm9tIGBkYXRlICslc2AsIG9yIGEgcmVsYXRpdmUgZHVyYXRpb25cbiAqIChgLTMwZGAsIGA3IGRheXMgYWdvYCksIHdoaWNoIGlzIHRoZSBzcGVsbGluZyBhbiBvcGVyYXRvciByZWFjaGluZyBmb3IgdGhpc1xuICogdXN1YWxseSB3YW50czogc2lnbiBvdXQgZXZlcnlvbmUgd2hvIHNpZ25lZCBpbiBtb3JlIHRoYW4gYSBtb250aCBhZ28uXG4gKlxuICogQW4gVU5QQVJTRUFCTEUgdmFsdWUgaXMgaWdub3JlZCB3aXRoIGEgd2FybmluZyByYXRoZXIgdGhhbiB0aHJvd2luZzogdGhpcyBpc1xuICogdGhlIHN3aXRjaCB0aGF0IGxvZ3MgYSBmbGVldCBiYWNrIGluLCBhbmQgZmFpbGluZyB0byBib290IG92ZXIgYSB0eXBvIGlzIHdvcnNlXG4gKiB0aGFuIG5vdCByb3RhdGluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVTZXNzaW9uQ3V0b2ZmKGNvbmZpZ3VyZWQ/OiBzdHJpbmcgfCBudW1iZXIgfCBEYXRlKTogbnVtYmVyIHtcbiAgY29uc3QgcmF3ID0gY29uZmlndXJlZCA/PyBlbnYudGV4dChTRVNTSU9OX0NVVE9GRl9FTlYpID8/IHVuZGVmaW5lZDtcbiAgaWYgKHJhdyA9PT0gdW5kZWZpbmVkIHx8IHJhdyA9PT0gbnVsbCB8fCByYXcgPT09IFwiXCIpIHJldHVybiAwO1xuXG4gIGNvbnN0IGRhdGUgPSBvYmplY3QudG9EYXRlKHJhdyk7XG4gIGlmICghZGF0ZSkge1xuICAgIGxvZ2dlci53YXJuKGBpZ25vcmluZyB1bnBhcnNlYWJsZSAke2Vudi5uYW1lKFNFU1NJT05fQ1VUT0ZGX0VOVil9YCwgeyB2YWx1ZTogU3RyaW5nKHJhdykgfSk7XG4gICAgcmV0dXJuIDA7XG4gIH1cbiAgcmV0dXJuIGNsYW1wVG9QYXN0KGRhdGUuZ2V0VGltZSgpKTtcbn1cblxuLyoqXG4gKiBIb2xkIHRoZSBjdXRvZmYgYXQgXCJub3dcIiwgYmVjYXVzZSBhIEZVVFVSRSBjdXRvZmYgd291bGQgcmVmdXNlIHRoZSBzZXNzaW9ucyBpdFxuICogaXMgYWJvdXQgdG8gbWludCBhcyB3ZWxsIGFzIHRoZSBvbGQgb25lcyAtIGFuIGFwcCBub2JvZHkgY2FuIHNpZ24gaW4gdG8sIGZyb20gYVxuICogbWlzdHlwZWQgeWVhciwgd2l0aCB0aGUgZml4IGhpZGRlbiBiZWhpbmQgdW5kZXJzdGFuZGluZyB0aGlzIGZsYWcuIENsYW1wZWQsIGFcbiAqIGZ1dHVyZSBkYXRlIG1lYW5zIHdoYXQgYW4gb3BlcmF0b3Igc2V0dGluZyBpdCBhbHdheXMgbWVhbnQ6IGNsZWFyIGV2ZXJ5dGhpbmdcbiAqIG91dHN0YW5kaW5nLCB0aGVuIGNhcnJ5IG9uLlxuICovXG5mdW5jdGlvbiBjbGFtcFRvUGFzdChjdXRvZmZNczogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgaWYgKGN1dG9mZk1zIDw9IG5vdykgcmV0dXJuIGN1dG9mZk1zO1xuICBsb2dnZXIud2FybihgJHtlbnYubmFtZShTRVNTSU9OX0NVVE9GRl9FTlYpfSBpcyBpbiB0aGUgZnV0dXJlIC0gY2xhbXBpbmcgdG8gbm93YCwge1xuICAgIGNvbmZpZ3VyZWQ6IG5ldyBEYXRlKGN1dG9mZk1zKS50b0lTT1N0cmluZygpLFxuICB9KTtcbiAgcmV0dXJuIG5vdztcbn1cblxuLyoqIFRoZSBjYWNoZSBrZXkgZm9yIG9uZSBjdXRvZmYsIHNvIG1vdmluZyB0aGUgY3V0b2ZmIG9ycGhhbnMgZXZlcnkgZWFybGllciBrZXkuICovXG5mdW5jdGlvbiBjYWNoZUtleShjdXRvZmZNczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAke0tFWV9QUkVGSVh9JHtjdXRvZmZNc31gO1xufVxuXG5mdW5jdGlvbiBkZWNvZGUoc3RvcmVkOiBTdG9yZWRLZXkpOiBVaW50OEFycmF5IHtcbiAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEJ1ZmZlci5mcm9tKHN0b3JlZC5zZWNyZXQsIFwiYmFzZTY0dXJsXCIpKTtcbn1cblxuLyoqXG4gKiBMb2FkIHRoZSBzaWduaW5nIGtleSBmb3IgYGN1dG9mZk1zYCBmcm9tIHRoZSBjYWNoZSwgbWludGluZyBhbmQgc3RvcmluZyBvbmVcbiAqIHdoZW4gYWJzZW50LiBTZWUgdGhlIG1vZHVsZSBkb2NzIGZvciB3aHkgdGhlIHdyaXRlIGlzIGZvbGxvd2VkIGJ5IGEgcmUtcmVhZC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gbG9hZEZyb21DYWNoZShjdXRvZmZNczogbnVtYmVyKTogUHJvbWlzZTxVaW50OEFycmF5PiB7XG4gIGNvbnN0IGNhY2hlID0gQ2FjaGVNYW5hZ2VyLmdldEluc3RhbmNlU3luYygpO1xuICBjb25zdCBrZXkgPSBjYWNoZUtleShjdXRvZmZNcyk7XG5cbiAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBjYWNoZS5nZXQ8U3RvcmVkS2V5PihrZXkpO1xuICBpZiAoZXhpc3Rpbmc/LnNlY3JldCkge1xuICAgIGxvZ2dlci5pbmZvKFwicmV1c2luZyBjYWNoZWQgc2lnbmluZyBrZXlcIiwgeyBjcmVhdGVkQXQ6IGV4aXN0aW5nLmNyZWF0ZWRBdCB9KTtcbiAgICByZXR1cm4gZGVjb2RlKGV4aXN0aW5nKTtcbiAgfVxuXG4gIGNvbnN0IG1pbnRlZDogU3RvcmVkS2V5ID0ge1xuICAgIHNlY3JldDogQnVmZmVyLmZyb20ocmFuZG9tQnl0ZXMoS0VZX0JZVEVTKSkudG9TdHJpbmcoXCJiYXNlNjR1cmxcIiksXG4gICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gIH07XG4gIGF3YWl0IGNhY2hlLnNldChrZXksIG1pbnRlZCwgeyB0dGw6IEtFWV9UVExfU0VDT05EUyB9KTtcblxuICAvLyBSZS1yZWFkIHJhdGhlciB0aGFuIHRydXN0aW5nIGBtaW50ZWRgOiBhbm90aGVyIGluc3RhbmNlIHRoYXQgcmFjZWQgdGhpcyBib290XG4gIC8vIG1heSBoYXZlIHdyaXR0ZW4gZmlyc3QsIGFuZCBhZG9wdGluZyB3aGF0ZXZlciBpcyBzdG9yZWQgaXMgd2hhdCBtYWtlcyB0aGUgdHdvXG4gIC8vIGNvbnZlcmdlIG9uIG9uZSBrZXkuXG4gIGNvbnN0IHNldHRsZWQgPSAoYXdhaXQgY2FjaGUuZ2V0PFN0b3JlZEtleT4oa2V5KSkgPz8gbWludGVkO1xuICBsb2dnZXIuaW5mbyhcInN0b3JlZCBhIG5ldyBzaWduaW5nIGtleVwiLCB7XG4gICAgY3JlYXRlZEF0OiBzZXR0bGVkLmNyZWF0ZWRBdCxcbiAgICB0dGxTZWNvbmRzOiBLRVlfVFRMX1NFQ09ORFMsXG4gICAgYWRvcHRlZDogc2V0dGxlZC5jcmVhdGVkQXQgPT09IG1pbnRlZC5jcmVhdGVkQXQgPyBcIm93blwiIDogXCJjb25jdXJyZW50LWluc3RhbmNlXCIsXG4gIH0pO1xuICByZXR1cm4gZGVjb2RlKHNldHRsZWQpO1xufVxuXG4vKiogQSByZXNvbHZlZCBrZXkgcGx1cyB0aGUgY3V0b2ZmIGl0IGlzIHNjb3BlZCB0by4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2lnbmluZ0tleSB7XG4gIGtleTogVWludDhBcnJheTtcbiAgLyoqIEZvcmNlLWNsZWFyIGN1dG9mZiBpbiBtczsgYDBgIHdoZW4gdW5zZXQuIFRva2VucyBvbGRlciB0aGFuIHRoaXMgYXJlIHJlZnVzZWQuICovXG4gIGN1dG9mZk1zOiBudW1iZXI7XG59XG5cbmxldCBwZW5kaW5nOiBQcm9taXNlPFNpZ25pbmdLZXk+IHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIFRoZSBzaWduaW5nIGtleSBmb3IgdGhpcyBnYXRlLCByZXNvbHZlZCBvbmNlIHBlciBwcm9jZXNzLlxuICpcbiAqIFJlc29sdmVkIE9OQ0UsIHNvIGBjb25maWd1cmVkQ3V0b2ZmYCBpcyBob25vdXJlZCBvbmx5IG9uIHRoZSBmaXJzdCBjYWxsIC0gdGhlXG4gKiBwbHVnaW4ncyBgc2V0dXAoKWAgcGFzc2VzIGl0cyByZXNvbHZlZCB2YWx1ZSB0aGVyZSwgYmVmb3JlIGFueSByZXF1ZXN0IGNhblxuICogcmVhY2ggdGhlIGxhenkgcGF0aC5cbiAqXG4gKiBgVFVOTkVMX0FVVEhfSldUX1NFQ1JFVGAgd2hlbiBzZXQsIGVsc2UgdGhlIGNhY2hlLWJhY2tlZCBrZXkuIEEgY2FjaGUgdGhhdCBpc1xuICogdW5hdmFpbGFibGUgZGVncmFkZXMgdG8gYW4gZXBoZW1lcmFsIHBlci1wcm9jZXNzIGtleSAodGhlIHByZXZpb3VzIGJlaGF2aW91cilcbiAqIHJhdGhlciB0aGFuIHJlZnVzaW5nIHRvIHNpZ246IHRoZSBrZXkgb25seSB2YWxpZGF0ZXMgYW4gQUxSRUFEWS1pc3N1ZWQgc2Vzc2lvbixcbiAqIHNvIGxvc2luZyBpdCBjb3N0cyBzZXNzaW9ucywgbmV2ZXIgYWRtaXNzaW9uIC0gYSBjYWxsZXIgc3RpbGwgbmVlZHMgYSBjb2RlXG4gKiBkZWxpdmVyZWQgdG8gYW4gYWxsb3ctbGlzdGVkIGFkZHJlc3MuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzaWduaW5nS2V5KGNvbmZpZ3VyZWRDdXRvZmY/OiBzdHJpbmcgfCBudW1iZXIgfCBEYXRlKTogUHJvbWlzZTxTaWduaW5nS2V5PiB7XG4gIHBlbmRpbmcgPz89IChhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgY3V0b2ZmTXMgPSByZXNvbHZlU2Vzc2lvbkN1dG9mZihjb25maWd1cmVkQ3V0b2ZmKTtcbiAgICBjb25zdCBjb25maWd1cmVkID0gZW52LnRleHQoSldUX1NFQ1JFVF9FTlYpO1xuICAgIGlmIChjb25maWd1cmVkKSB7XG4gICAgICByZXR1cm4geyBrZXk6IG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZShjb25maWd1cmVkKSwgY3V0b2ZmTXMgfTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiB7IGtleTogYXdhaXQgbG9hZEZyb21DYWNoZShjdXRvZmZNcyksIGN1dG9mZk1zIH07XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci53YXJuKFxuICAgICAgICBgbm8gJHtlbnYubmFtZShKV1RfU0VDUkVUX0VOVil9IGFuZCB0aGUgY2FjaGUgaXMgdW5hdmFpbGFibGUgLSB1c2luZyBhbiBlcGhlbWVyYWwgcGVyLXByb2Nlc3Mga2V5OyBzZXNzaW9ucyB3aWxsIG5vdCBzdXJ2aXZlIGEgcmVzdGFydGAsXG4gICAgICAgIHsgZXJyb3IgfSxcbiAgICAgICk7XG4gICAgICByZXR1cm4geyBrZXk6IG5ldyBVaW50OEFycmF5KHJhbmRvbUJ5dGVzKEtFWV9CWVRFUykpLCBjdXRvZmZNcyB9O1xuICAgIH1cbiAgfSkoKTtcbiAgcmV0dXJuIHBlbmRpbmc7XG59XG5cbi8qKiBSZXNldCB0aGUgcGVyLXByb2Nlc3Mga2V5ICh0ZXN0cywgb3IgYWZ0ZXIgY2hhbmdpbmcgdGhlIGVudiBpbi1wcm9jZXNzKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNldFNpZ25pbmdLZXkoKTogdm9pZCB7XG4gIHBlbmRpbmcgPSB1bmRlZmluZWQ7XG59XG4iXX0=
|
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.52",
|
|
33
|
+
"@dbx-tools/core": "0.6.52",
|
|
34
|
+
"@dbx-tools/email": "0.6.52",
|
|
35
|
+
"@dbx-tools/shared-core": "0.6.52",
|
|
36
|
+
"@dbx-tools/shared-email": "0.6.52",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"http-proxy-3": "^1.23.3",
|
|
39
39
|
"jose": "^6.2.3"
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"./package.json": "./package.json"
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
|
-
"version": "0.6.
|
|
103
|
+
"version": "0.6.52",
|
|
104
104
|
"types": "./lib/index.d.ts",
|
|
105
105
|
"type": "module",
|
|
106
106
|
"exports": {
|
package/src/cli.ts
CHANGED
|
@@ -52,7 +52,7 @@ interface TunnelOpts {
|
|
|
52
52
|
message?: string;
|
|
53
53
|
sessionTtl?: string;
|
|
54
54
|
codeTtl?: string;
|
|
55
|
-
|
|
55
|
+
sessionCutoff?: string;
|
|
56
56
|
insecure?: boolean;
|
|
57
57
|
forwardHeaders?: string;
|
|
58
58
|
}
|
|
@@ -77,8 +77,8 @@ function program(): Command {
|
|
|
77
77
|
.option("--session-ttl <seconds>", "Session lifetime (env TUNNEL_AUTH_SESSION_TTL)")
|
|
78
78
|
.option("--code-ttl <seconds>", "One-time-code lifetime (env TUNNEL_AUTH_CODE_TTL)")
|
|
79
79
|
.option(
|
|
80
|
-
"--session-
|
|
81
|
-
"Invalidate sessions issued before this
|
|
80
|
+
"--session-cutoff <when>",
|
|
81
|
+
"Invalidate sessions issued before this point, signing everyone out: a date, ISO instant, epoch seconds, or relative duration like -30d (env TUNNEL_AUTH_SESSION_CUTOFF)",
|
|
82
82
|
)
|
|
83
83
|
.option(
|
|
84
84
|
"--forward-headers <patterns>",
|
|
@@ -140,7 +140,7 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
140
140
|
message: opts.message,
|
|
141
141
|
sessionTtlSeconds: opts.sessionTtl ? Number(opts.sessionTtl) : undefined,
|
|
142
142
|
codeTtlSeconds: opts.codeTtl ? Number(opts.codeTtl) : undefined,
|
|
143
|
-
|
|
143
|
+
sessionCutoff: opts.sessionCutoff,
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
// 1. Spawn the wrapped app with the PRIVATE port. It binds loopback; only the
|
package/src/env.ts
CHANGED
|
@@ -53,10 +53,14 @@ export const CODE_TTL_ENV: EnvKey = ["TUNNEL_AUTH_CODE_TTL", "AUTH_CODE_TTL"];
|
|
|
53
53
|
export const JWT_SECRET_ENV: EnvKey = ["TUNNEL_AUTH_JWT_SECRET", "AUTH_JWT_SECRET"];
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Force-clear
|
|
57
|
-
*
|
|
56
|
+
* Force-clear cutoff: every session issued BEFORE it stops verifying. Anything
|
|
57
|
+
* `object.toDate` accepts - a date, an ISO instant, epoch seconds/millis, or a
|
|
58
|
+
* relative duration (`-30d`, `7 days ago`).
|
|
58
59
|
*/
|
|
59
|
-
export const
|
|
60
|
+
export const SESSION_CUTOFF_ENV: EnvKey = [
|
|
61
|
+
"TUNNEL_AUTH_SESSION_CUTOFF",
|
|
62
|
+
"TUNNEL_AUTH_SESSION_EPOCH",
|
|
63
|
+
];
|
|
60
64
|
|
|
61
65
|
/** The public `<subdomain>.<server>` portr should serve on. */
|
|
62
66
|
export const PUBLIC_DOMAIN_ENV: EnvKey = ["TUNNEL_PUBLIC_DOMAIN", "PUBLIC_DOMAIN"];
|
package/src/otp.ts
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* signing key is resolved by `./signing-key.ts`: `TUNNEL_AUTH_JWT_SECRET` when
|
|
13
13
|
* set, else a key persisted in the cache for 30 days so cookies survive the
|
|
14
14
|
* restarts a tunnel sees whenever the app it wraps reloads. A cookie signed
|
|
15
|
-
* before `
|
|
16
|
-
* the cache key, so the force-clear switch holds even for a still-current key.
|
|
15
|
+
* before `TUNNEL_AUTH_SESSION_CUTOFF` is refused here as well as being orphaned
|
|
16
|
+
* by the cache key, so the force-clear switch holds even for a still-current key.
|
|
17
17
|
*
|
|
18
18
|
* @module
|
|
19
19
|
*/
|
|
@@ -118,16 +118,16 @@ export async function signSession(email: string, ttlSeconds: number): Promise<st
|
|
|
118
118
|
export async function verifySession(token: string | undefined): Promise<string | undefined> {
|
|
119
119
|
if (!token) return undefined;
|
|
120
120
|
try {
|
|
121
|
-
const { key,
|
|
121
|
+
const { key, cutoffMs } = await signingKey();
|
|
122
122
|
const { payload } = await jwtVerify(token, key, { audience: JWT_AUD });
|
|
123
|
-
// Belt and braces with the
|
|
124
|
-
// orphans older keys, but an operator who moved the
|
|
123
|
+
// Belt and braces with the cutoff-scoped cache key: that alone already
|
|
124
|
+
// orphans older keys, but an operator who moved the cutoff while
|
|
125
125
|
// TUNNEL_AUTH_JWT_SECRET is set has no key rotation to rely on, and this
|
|
126
126
|
// check is what makes the switch work in that case too.
|
|
127
127
|
// Compared in whole SECONDS because `iat` has no finer resolution: against a
|
|
128
|
-
// millisecond
|
|
129
|
-
//
|
|
130
|
-
if (
|
|
128
|
+
// millisecond cutoff, a cookie minted in the same second as it would be
|
|
129
|
+
// refused depending on sub-second rounding.
|
|
130
|
+
if (cutoffMs > 0 && (payload.iat === undefined || payload.iat < Math.floor(cutoffMs / 1000))) {
|
|
131
131
|
return undefined;
|
|
132
132
|
}
|
|
133
133
|
return typeof payload.email === "string" ? payload.email : undefined;
|
package/src/plugin.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { Plugin, toPlugin, type BasePluginConfig, type PluginManifest } from "@databricks/appkit";
|
|
20
|
-
import { brand, env, log, string } from "@dbx-tools/shared-core";
|
|
20
|
+
import { brand, env, log, object, string } from "@dbx-tools/shared-core";
|
|
21
21
|
import type { AuthStatus } from "@dbx-tools/shared-email";
|
|
22
22
|
import { looksLikeEmail, matchesAllowlist } from "./allowlist.ts";
|
|
23
23
|
import {
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "./env.ts";
|
|
31
31
|
import { CodeStore, signSession, verifySession } from "./otp.ts";
|
|
32
32
|
import { RateLimiter } from "./rate-limit.ts";
|
|
33
|
-
import { KEY_TTL_SECONDS,
|
|
33
|
+
import { KEY_TTL_SECONDS, resolveSessionCutoff, signingKey } from "./signing-key.ts";
|
|
34
34
|
|
|
35
35
|
const logger = log.logger("tunnel:auth");
|
|
36
36
|
|
|
@@ -76,13 +76,14 @@ export interface AuthGateConfig extends BasePluginConfig {
|
|
|
76
76
|
/** Max verify attempts per issued code. Default 5. */
|
|
77
77
|
maxAttempts?: number;
|
|
78
78
|
/**
|
|
79
|
-
* Force-clear
|
|
80
|
-
* it forward signs everyone out. Env
|
|
79
|
+
* Force-clear cutoff: every session issued BEFORE it stops verifying, so moving
|
|
80
|
+
* it forward signs everyone out. Env TUNNEL_AUTH_SESSION_CUTOFF.
|
|
81
81
|
*
|
|
82
|
-
*
|
|
83
|
-
* seconds
|
|
82
|
+
* Anything `object.toDate` accepts: a `Date`, `2026-08-02`, an ISO instant,
|
|
83
|
+
* epoch seconds/millis, or a relative duration (`-30d`, `7 days ago`). Unset
|
|
84
|
+
* means no cutoff.
|
|
84
85
|
*/
|
|
85
|
-
|
|
86
|
+
sessionCutoff?: string | number | Date;
|
|
86
87
|
/** Deliver a code to an address. Wired by the app to the email plugin. */
|
|
87
88
|
sendCode?: (email: string, code: string, opts: SendCodeOptions) => Promise<void>;
|
|
88
89
|
}
|
|
@@ -111,7 +112,7 @@ export interface ResolvedAuthGateConfig {
|
|
|
111
112
|
codeTtlSeconds: number;
|
|
112
113
|
maxAttempts: number;
|
|
113
114
|
/** Force-clear cutoff in epoch ms; `0` when unset. */
|
|
114
|
-
|
|
115
|
+
sessionCutoffMs: number;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
const DEFAULTS = {
|
|
@@ -148,7 +149,7 @@ export function resolveAuthGateConfig(config: AuthGateConfig): ResolvedAuthGateC
|
|
|
148
149
|
),
|
|
149
150
|
codeTtlSeconds: env.positiveInt(config.codeTtlSeconds, CODE_TTL_ENV, DEFAULTS.codeTtlSeconds),
|
|
150
151
|
maxAttempts: config.maxAttempts ?? DEFAULTS.maxAttempts,
|
|
151
|
-
|
|
152
|
+
sessionCutoffMs: resolveSessionCutoff(config.sessionCutoff),
|
|
152
153
|
};
|
|
153
154
|
}
|
|
154
155
|
|
|
@@ -193,11 +194,11 @@ export class AuthGatePlugin extends Plugin<AuthGateConfig> {
|
|
|
193
194
|
// Resolve the signing key HERE rather than lazily on the first sign-in, so a
|
|
194
195
|
// cache that cannot hold it (and the resulting "sessions won't survive a
|
|
195
196
|
// restart" warning) shows up in the startup log, not hours later.
|
|
196
|
-
const {
|
|
197
|
+
const { cutoffMs } = await signingKey(this.resolved.sessionCutoffMs);
|
|
197
198
|
logger.info("ready", {
|
|
198
199
|
patterns: this.resolved.allow.length,
|
|
199
200
|
sessionTtlSeconds: this.resolved.sessionTtlSeconds,
|
|
200
|
-
...(
|
|
201
|
+
...object.optional("sessionCutoff", cutoffMs > 0 ? new Date(cutoffMs).toISOString() : null),
|
|
201
202
|
});
|
|
202
203
|
}
|
|
203
204
|
|