@pipeline-builder/pipeline-manager 4.1.4 → 4.1.5
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 +2 -1
- package/dist/agent/env-file.d.ts +1 -1
- package/dist/agent/env-file.js +2 -2
- package/dist/agent/post-steps.d.ts.map +1 -1
- package/dist/agent/post-steps.js +38 -18
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +19 -9
- package/dist/commands/audit-tokens.d.ts.map +1 -1
- package/dist/commands/audit-tokens.js +32 -21
- package/dist/commands/create-pat.d.ts +20 -13
- package/dist/commands/create-pat.d.ts.map +1 -1
- package/dist/commands/create-pat.js +93 -98
- package/dist/commands/login.d.ts +14 -8
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +89 -124
- package/dist/commands/setup-events.d.ts.map +1 -1
- package/dist/commands/setup-events.js +6 -5
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +9 -3
- package/dist/commands/store-token.d.ts +16 -11
- package/dist/commands/store-token.d.ts.map +1 -1
- package/dist/commands/store-token.js +213 -131
- package/dist/lambda/token-renew-handler.d.ts.map +1 -1
- package/dist/lambda/token-renew-handler.js +134 -97
- package/dist/templates/events-stack.json +1 -1
- package/dist/templates/token-renew-stack.json +9 -10
- package/dist/utils/auth-utils.d.ts +21 -38
- package/dist/utils/auth-utils.d.ts.map +1 -1
- package/dist/utils/auth-utils.js +20 -33
- package/dist/utils/command-utils.d.ts +8 -6
- package/dist/utils/command-utils.d.ts.map +1 -1
- package/dist/utils/command-utils.js +71 -15
- package/dist/utils/config-loader.d.ts +2 -1
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-loader.js +18 -8
- package/dist/utils/credential-store.d.ts +23 -0
- package/dist/utils/credential-store.d.ts.map +1 -0
- package/dist/utils/credential-store.js +92 -0
- package/dist/utils/device-auth.d.ts +59 -0
- package/dist/utils/device-auth.d.ts.map +1 -0
- package/dist/utils/device-auth.js +139 -0
- package/dist/utils/pipeline-config.js +2 -2
- package/dist/utils/platform-secret.d.ts +19 -1
- package/dist/utils/platform-secret.d.ts.map +1 -1
- package/dist/utils/platform-secret.js +26 -4
- package/dist/utils/service-account.d.ts +73 -0
- package/dist/utils/service-account.d.ts.map +1 -0
- package/dist/utils/service-account.js +158 -0
- package/package.json +4 -4
- package/dist/utils/rate-limiter.d.ts +0 -15
- package/dist/utils/rate-limiter.d.ts.map +0 -1
- package/dist/utils/rate-limiter.js +0 -164
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-renew-handler.d.ts","sourceRoot":"","sources":["../../src/lambda/token-renew-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token-renew-handler.d.ts","sourceRoot":"","sources":["../../src/lambda/token-renew-handler.ts"],"names":[],"mappings":"AAgHA,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,IAAI,CA+E5C,CAAC"}
|
|
@@ -1,125 +1,162 @@
|
|
|
1
1
|
// Copyright 2026 Pipeline Builder Contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* --schedule`.
|
|
7
|
-
* orchestrator that reuses the tested CLI — each run:
|
|
4
|
+
* Scheduled KEY ROTATOR for the service-account credential in Secrets Manager
|
|
5
|
+
* (#N2). Deployed + scheduled (once a day) by `pipeline-manager infra store-token
|
|
6
|
+
* --schedule`.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* It used to re-mint a person's machine-session JWT by npm-installing the CLI at
|
|
9
|
+
* runtime and shelling out to `store-token`. There is nothing left to re-mint:
|
|
10
|
+
* the stored credential is an opaque `pb_sa_…` key, and platform rotates it
|
|
11
|
+
* through two small pre-auth endpoints where the KEY ITSELF is the authorization
|
|
12
|
+
* (an unattended machine has no password and cannot step up). So this handler is
|
|
13
|
+
* three HTTP calls and a secret write, with no npm install, no CLI, no `/tmp`.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* THE ORDER IS THE WHOLE DESIGN. At no point may the secret name a credential
|
|
16
|
+
* that does not work:
|
|
17
|
+
*
|
|
18
|
+
* 1. ROTATE — mint a sibling key on the same account. The current key stays
|
|
19
|
+
* LIVE. Fail here and the secret is untouched: the old key still works.
|
|
20
|
+
* 2. STORE — write the new key to the secret. Fail here and the secret still
|
|
21
|
+
* holds the old key, which is still live; the new key is an orphan that
|
|
22
|
+
* expires on its own (and the next run's rotate prunes it at the cap).
|
|
23
|
+
* 3. REVOKE — retire the old key, authenticated with the NEW one. Fail here
|
|
24
|
+
* and BOTH keys work; the credential is healthy and the stale key expires
|
|
25
|
+
* on its own. Logged at ERROR so it is visible, but it does not fail the
|
|
26
|
+
* invocation: a retry would rotate again and churn keys for no gain.
|
|
27
|
+
*
|
|
28
|
+
* Revoke-then-create would invert every one of those: a failure after the revoke
|
|
29
|
+
* leaves the deployment with no working credential at all.
|
|
18
30
|
*
|
|
19
31
|
* Env: PLATFORM_SECRET_NAME, PLATFORM_BASE_URL, RENEW_DAYS (default 30),
|
|
20
|
-
*
|
|
21
|
-
*
|
|
32
|
+
* PLATFORM_VERIFY_SSL ("false" to disable TLS verification — refused in
|
|
33
|
+
* production).
|
|
22
34
|
*/
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const PM_PREFIX = `${TMP}/pm`;
|
|
29
|
-
const NPM_CACHE = `${TMP}/.npm`;
|
|
30
|
-
const NPMRC = `${TMP}/.npmrc`;
|
|
31
|
-
const CLI = `${PM_PREFIX}/node_modules/${PM_PACKAGE}/dist/cli.js`;
|
|
35
|
+
import { SecretsManagerClient, GetSecretValueCommand, PutSecretValueCommand } from '@aws-sdk/client-secrets-manager';
|
|
36
|
+
/** Per-request timeout for the platform calls. */
|
|
37
|
+
const HTTP_TIMEOUT_MS = 10_000;
|
|
38
|
+
/** Access-key prefixes platform issues. A stored JWT is not one of them. */
|
|
39
|
+
const ACCESS_KEY_PREFIXES = ['pb_sa_', 'pb_pat_'];
|
|
32
40
|
function required(name) {
|
|
33
41
|
const v = process.env[name];
|
|
34
42
|
if (!v)
|
|
35
43
|
throw new Error(`${name} environment variable is required`);
|
|
36
44
|
return v;
|
|
37
45
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
function log(level, msg, data) {
|
|
47
|
+
const line = JSON.stringify({ level, msg, ...data });
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
if (level === 'ERROR')
|
|
50
|
+
console.error(line);
|
|
51
|
+
else
|
|
52
|
+
console.log(line);
|
|
53
|
+
}
|
|
54
|
+
/** POST JSON to the platform, returning the parsed body plus the status. */
|
|
55
|
+
async function post(baseUrl, route, body) {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
|
|
47
58
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
const res = await fetch(`${baseUrl}${route}`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
|
62
|
+
body: JSON.stringify(body),
|
|
63
|
+
signal: controller.signal,
|
|
64
|
+
});
|
|
65
|
+
const parsed = await res.json().catch(() => ({}));
|
|
66
|
+
return { status: res.status, body: parsed };
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
clearTimeout(timer);
|
|
54
70
|
}
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* SECURITY: these calls carry a live credential. Refuse to disable TLS
|
|
74
|
+
* verification in production so a MITM can't harvest it. Production is this
|
|
75
|
+
* Lambda's normal mode (the rotation stack sets NODE_ENV=production), so
|
|
76
|
+
* PLATFORM_VERIFY_SSL=false is honored ONLY outside production. This inlines the
|
|
77
|
+
* same `NODE_ENV==='production'` policy as utils/tls.ts#assertSslDisableAllowed
|
|
78
|
+
* (the handler ships as a single self-contained index.mjs and can't import it).
|
|
79
|
+
*/
|
|
80
|
+
function applyTlsPolicy() {
|
|
81
|
+
if (process.env.PLATFORM_VERIFY_SSL !== 'false')
|
|
82
|
+
return;
|
|
83
|
+
if (process.env.NODE_ENV === 'production') {
|
|
84
|
+
log('WARN', 'Refusing PLATFORM_VERIFY_SSL=false in production (NODE_ENV=production) — TLS verification stays enabled');
|
|
85
|
+
return;
|
|
57
86
|
}
|
|
87
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
58
88
|
}
|
|
59
89
|
export const handler = async () => {
|
|
60
90
|
const secretName = required('PLATFORM_SECRET_NAME');
|
|
61
|
-
const
|
|
62
|
-
const days = process.env.RENEW_DAYS || '30';
|
|
63
|
-
|
|
91
|
+
const envUrl = required('PLATFORM_BASE_URL').replace(/\/+$/, '');
|
|
92
|
+
const days = Number(process.env.RENEW_DAYS || '30');
|
|
93
|
+
if (!Number.isFinite(days) || days < 1 || days > 365) {
|
|
94
|
+
throw new Error(`RENEW_DAYS must be between 1 and 365 (got "${process.env.RENEW_DAYS}")`);
|
|
95
|
+
}
|
|
64
96
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
65
|
-
|
|
66
|
-
const npmEnv = { ...process.env, HOME: TMP, npm_config_cache: NPM_CACHE };
|
|
67
|
-
// 1. Scope config: resolve @pipeline-builder from public npm regardless of any
|
|
68
|
-
// inherited registry. Written as a userconfig file (env mapping of scoped
|
|
69
|
-
// keys is unreliable).
|
|
70
|
-
writeFileSync(NPMRC, '@pipeline-builder:registry=https://registry.npmjs.org/\n');
|
|
71
|
-
// 2. Download pipeline-manager into /tmp.
|
|
72
|
-
mkdirSync(PM_PREFIX, { recursive: true });
|
|
73
|
-
// eslint-disable-next-line no-console
|
|
74
|
-
console.log(JSON.stringify({ level: 'INFO', msg: 'installing pipeline-manager', package: `${PM_PACKAGE}@${version}` }));
|
|
75
|
-
execFileSync('npm', ['install', '--prefix', PM_PREFIX, '--no-audit', '--no-fund', '--userconfig', NPMRC, `${PM_PACKAGE}@${version}`], { env: npmEnv, stdio: 'inherit' });
|
|
76
|
-
// 3. Read the current platform JWT — it authenticates the renewal request.
|
|
97
|
+
applyTlsPolicy();
|
|
77
98
|
const sm = new SecretsManagerClient({ region });
|
|
78
99
|
const current = await sm.send(new GetSecretValueCommand({ SecretId: secretName }));
|
|
79
100
|
if (!current.SecretString)
|
|
80
101
|
throw new Error(`Secret "${secretName}" is empty`);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// the environment (else derives it from the token's org).
|
|
89
|
-
const args = ['infra', 'store-token', '--region', region, '--days', days];
|
|
90
|
-
// Preserve the token's capability scope on renewal (else a scoped ingest
|
|
91
|
-
// credential re-mints as a scopeless platform token and ingest starts 403ing).
|
|
92
|
-
// The re-mint authenticates with THIS token, and a scoped token still carries
|
|
93
|
-
// a userId, so /user/generate-token accepts it and re-issues the same scope.
|
|
94
|
-
const tokenScope = decodeJwtScope(jwt);
|
|
95
|
-
if (tokenScope) {
|
|
96
|
-
args.push('--scope', tokenScope);
|
|
97
|
-
// eslint-disable-next-line no-console
|
|
98
|
-
console.log(JSON.stringify({ level: 'INFO', msg: 'renewing scoped token', scope: tokenScope, secretName }));
|
|
102
|
+
const stored = JSON.parse(current.SecretString);
|
|
103
|
+
const oldKey = stored.password;
|
|
104
|
+
if (!oldKey)
|
|
105
|
+
throw new Error(`Secret "${secretName}" missing password (service-account key)`);
|
|
106
|
+
if (!ACCESS_KEY_PREFIXES.some((p) => oldKey.startsWith(p))) {
|
|
107
|
+
throw new Error(`Secret "${secretName}" does not hold an opaque service-account key. `
|
|
108
|
+
+ 'Re-run "pipeline-manager infra store-token" to reissue it as a key (see docs/runbooks/access-key-cutover.md).');
|
|
99
109
|
}
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (isProduction) {
|
|
109
|
-
// eslint-disable-next-line no-console
|
|
110
|
-
console.log(JSON.stringify({ level: 'WARN', msg: 'Refusing PLATFORM_VERIFY_SSL=false in production (NODE_ENV=production) — TLS verification stays enabled' }));
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
args.push('--no-verify-ssl');
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
execFileSync('node', [CLI, ...args], {
|
|
117
|
-
// NODE_ENV is inherited via npmEnv (spread of process.env), so the spawned CLI
|
|
118
|
-
// enforces the same production TLS-disable refusal as this handler.
|
|
119
|
-
env: { ...npmEnv, PLATFORM_TOKEN: jwt, PLATFORM_BASE_URL: platformUrl, PLATFORM_SECRET_NAME: secretName },
|
|
120
|
-
stdio: 'inherit',
|
|
110
|
+
// The platform this credential belongs to is recorded IN the secret; the env
|
|
111
|
+
// var is the fallback for a secret written before that field existed.
|
|
112
|
+
const platformUrl = (stored.platformUrl || envUrl).replace(/\/+$/, '');
|
|
113
|
+
// ── 1. ROTATE — mint the replacement. The old key stays live. ──────────────
|
|
114
|
+
const rotate = await post(platformUrl, '/api/auth/key/rotate', {
|
|
115
|
+
key: oldKey,
|
|
116
|
+
name: `${stored.serviceAccountName || 'rotated'}-${new Date().toISOString().slice(0, 10)}`,
|
|
117
|
+
expiresIn: days * 24 * 60 * 60,
|
|
121
118
|
});
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
if (rotate.status < 200 || rotate.status >= 300) {
|
|
120
|
+
throw new Error(`Key rotation refused (${rotate.status}): ${rotate.body.message || 'unknown reason'}. `
|
|
121
|
+
+ 'The stored key is unchanged and still live.');
|
|
122
|
+
}
|
|
123
|
+
const newKey = rotate.body.data?.key;
|
|
124
|
+
const newKeyId = rotate.body.data?.keyId;
|
|
125
|
+
if (!newKey || !newKeyId)
|
|
126
|
+
throw new Error('Key rotation returned no replacement key — the stored key is unchanged');
|
|
127
|
+
const pruned = rotate.body.data?.prunedKeyIds ?? [];
|
|
128
|
+
if (pruned.length > 0) {
|
|
129
|
+
log('WARN', 'Platform retired stale sibling keys to stay under the active-key cap', { secretName, prunedKeyIds: pruned });
|
|
130
|
+
}
|
|
131
|
+
log('INFO', 'Minted the replacement key', { secretName, keyId: newKeyId });
|
|
132
|
+
// ── 2. STORE — the secret now names a live credential. ─────────────────────
|
|
133
|
+
const next = {
|
|
134
|
+
...stored,
|
|
135
|
+
password: newKey,
|
|
136
|
+
keyId: newKeyId,
|
|
137
|
+
platformUrl,
|
|
138
|
+
expiresIn: days * 24 * 60 * 60,
|
|
139
|
+
...(rotate.body.data?.expiresAt ? { expiresAt: rotate.body.data.expiresAt } : {}),
|
|
140
|
+
createdAt: new Date().toISOString(),
|
|
141
|
+
};
|
|
142
|
+
await sm.send(new PutSecretValueCommand({ SecretId: secretName, SecretString: JSON.stringify(next) }));
|
|
143
|
+
log('INFO', 'Stored the rotated key', { secretName, keyId: newKeyId });
|
|
144
|
+
// ── 3. REVOKE — retire the predecessor, using the key that replaced it. ────
|
|
145
|
+
// Best-effort by design: the credential is already healthy, and failing the
|
|
146
|
+
// invocation here would only make the next retry rotate again.
|
|
147
|
+
const oldKeyId = stored.keyId;
|
|
148
|
+
if (!oldKeyId) {
|
|
149
|
+
log('WARN', 'Secret recorded no previous keyId — nothing to revoke (it expires on its own)', { secretName });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const revoke = await post(platformUrl, '/api/auth/key/revoke', { key: newKey, keyId: oldKeyId })
|
|
153
|
+
.catch((err) => ({ status: 0, body: { message: err instanceof Error ? err.message : String(err) } }));
|
|
154
|
+
if (revoke.status < 200 || revoke.status >= 300) {
|
|
155
|
+
log('ERROR', 'Rotated and stored the new key, but could NOT revoke its predecessor — it stays valid until it expires', {
|
|
156
|
+
secretName, staleKeyId: oldKeyId, status: revoke.status, reason: revoke.body.message,
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
log('INFO', 'Rotation complete', { secretName, keyId: newKeyId, revokedKeyId: oldKeyId });
|
|
124
161
|
};
|
|
125
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tcmVuZXctaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sYW1iZGEvdG9rZW4tcmVuZXctaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBQ0gsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ25ELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTlGLE1BQU0sVUFBVSxHQUFHLG9DQUFvQyxDQUFDO0FBQ3hELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQztBQUNuQixNQUFNLFNBQVMsR0FBRyxHQUFHLEdBQUcsS0FBSyxDQUFDO0FBQzlCLE1BQU0sU0FBUyxHQUFHLEdBQUcsR0FBRyxPQUFPLENBQUM7QUFDaEMsTUFBTSxLQUFLLEdBQUcsR0FBRyxHQUFHLFNBQVMsQ0FBQztBQUM5QixNQUFNLEdBQUcsR0FBRyxHQUFHLFNBQVMsaUJBQWlCLFVBQVUsY0FBYyxDQUFDO0FBRWxFLFNBQVMsUUFBUSxDQUFDLElBQVk7SUFDNUIsTUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QixJQUFJLENBQUMsQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxJQUFJLG1DQUFtQyxDQUFDLENBQUM7SUFDcEUsT0FBTyxDQUFDLENBQUM7QUFDWCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsY0FBYyxDQUFDLEdBQVc7SUFDakMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNs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LElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQsWUFBWSxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFO1FBQ25DLCtFQUErRTtRQUMvRSxvRUFBb0U7UUFDcEUsR0FBRyxFQUFFLEVBQUUsR0FBRyxNQUFNLEVBQUUsY0FBYyxFQUFFLEdBQUcsRUFBRSxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsb0JBQW9CLEVBQUUsVUFBVSxFQUFFO1FBQ3pHLEtBQUssRUFBRSxTQUFTO0tBQ2pCLENBQUMsQ0FBQztJQUVILHNDQUFzQztJQUN0QyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSx3QkFBd0IsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDNUYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogTGFtYmRhIGhhbmRsZXIgdGhhdCByZS1taW50cyB0aGUgcGxhdGZvcm0gSldUIGluIFNlY3JldHMgTWFuYWdlciBzbyBpdCBuZXZlclxuICogbGFwc2VzLiBEZXBsb3llZCArIHNjaGVkdWxlZCAob25jZSBhIGRheSkgYnkgYHBpcGVsaW5lLW1hbmFnZXIgaW5mcmEgc3RvcmUtdG9rZW5cbiAqIC0tc2NoZWR1bGVgLiBSYXRoZXIgdGhhbiByZS1pbXBsZW1lbnRpbmcgdGhlIHRva2VuIGZsb3csIGl0IGlzIGEgdGhpblxuICogb3JjaGVzdHJhdG9yIHRoYXQgcmV1c2VzIHRoZSB0ZXN0ZWQgQ0xJIOKAlCBlYWNoIHJ1bjpcbiAqXG4gKiAgIDEuIFBvaW50IHRoZSBgQHBpcGVsaW5lLWJ1aWxkZXJgIG5wbSBzY29wZSBhdCBwdWJsaWMgbnBtLlxuICogICAyLiBEb3dubG9hZCBgQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtbWFuYWdlcmAgZnJvbSBucG0gaW50byAvdG1wLlxuICogICAzLiBSZWFkIHRoZSBjdXJyZW50IHBsYXRmb3JtIEpXVCBmcm9tIHRoZSBzZWNyZXQuXG4gKiAgIDQuIFJ1biBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlbmAsIHdoaWNoIG1pbnRzIGEgZnJlc2ggbG9uZy1saXZlZCB0b2tlblxuICogICAgICB2aWEgL2FwaS91c2VyL2dlbmVyYXRlLXRva2VuIGFuZCB3cml0ZXMgaXQgYmFjayB0byB0aGUgc2FtZSBzZWNyZXQuXG4gKlxuICogYC90bXBgIGlzIHRoZSBvbmx5IHdyaXRhYmxlIHBhdGggaW4gTGFtYmRhLCBzbyBucG0ncyBIT01FL2NhY2hlL3ByZWZpeCBhbGwgbGl2ZVxuICogdGhlcmUuIHN0b3JlLXRva2VuIGRvZXMgTk9UIGRlcGxveSB0aGUgcmVuZXdhbCBzdGFjayBieSBkZWZhdWx0IChubyAtLXNjaGVkdWxlKSxcbiAqIHNvIHRoZSByZW5ld2FsIHJ1biBuZXZlciByZWRlcGxveXMvcmVjdXJzZXMgaW50byBpdHMgb3duIHN0YWNrLlxuICpcbiAqIEVudjogUExBVEZPUk1fU0VDUkVUX05BTUUsIFBMQVRGT1JNX0JBU0VfVVJMLCBSRU5FV19EQVlTIChkZWZhdWx0IDMwKSxcbiAqICAgICAgUElQRUxJTkVfTUFOQUdFUl9WRVJTSU9OIChkZWZhdWx0IFwibGF0ZXN0XCIpLCBQTEFURk9STV9WRVJJRllfU1NMIChcImZhbHNlXCJcbiAqICAgICAgdG8gZGlzYWJsZSBUTFMgdmVyaWZpY2F0aW9uKS5cbiAqL1xuaW1wb3J0IHsgZXhlY0ZpbGVTeW5jIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJztcbmltcG9ydCB7IG1rZGlyU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0IHsgU2VjcmV0c01hbmFnZXJDbGllbnQsIEdldFNlY3JldFZhbHVlQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXInO1xuXG5jb25zdCBQTV9QQUNLQUdFID0gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLW1hbmFnZXInO1xuY29uc3QgVE1QID0gJy90bXAnO1xuY29uc3QgUE1fUFJFRklYID0gYCR7VE1QfS9wbWA7XG5jb25zdCBOUE1fQ0FDSEUgPSBgJHtUTVB9Ly5ucG1gO1xuY29uc3QgTlBNUkMgPSBgJHtUTVB9Ly5ucG1yY2A7XG5jb25zdCBDTEkgPSBgJHtQTV9QUkVGSVh9L25vZGVfbW9kdWxlcy8ke1BNX1BBQ0tBR0V9L2Rpc3QvY2xpLmpzYDtcblxuZnVuY3Rpb24gcmVxdWlyZWQobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgdiA9IHByb2Nlc3MuZW52W25hbWVdO1xuICBpZiAoIXYpIHRocm93IG5ldyBFcnJvcihgJHtuYW1lfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZGApO1xuICByZXR1cm4gdjtcbn1cblxuLyoqXG4gKiBSZWFkIHRoZSBgc2NvcGVgIGNsYWltIGZyb20gYSBKV1QgcGF5bG9hZCB3aXRob3V0IHZlcmlmeWluZyB0aGUgc2lnbmF0dXJlXG4gKiAodGhlIHBsYXRmb3JtIHJlLXZlcmlmaWVzIG9uIHVzZTsgdGhpcyBvbmx5IGRlY2lkZXMgd2hldGhlciB0byByZS1taW50IFdJVEhcbiAqIGEgc2NvcGUpLiBBIHNjb3BlZCBjcmVkZW50aWFsIChlLmcuIGByZXBvcnRpbmc6aW5nZXN0YCkgbXVzdCByZW5ldyB3aXRoIHRoZVxuICogc2FtZSBzY29wZSBvciB0aGUgZnJlc2ggdG9rZW4gd291bGQgYmUgYSBmdWxsLXByaXZpbGVnZSBwbGF0Zm9ybSB0b2tlbiBhbmRcbiAqIHRoZSBzY29wZWQgY29uc3VtZXIncyBjYWxscyB3b3VsZCBicmVhay4gUmV0dXJucyB1bmRlZmluZWQgZm9yIGFuIHVuc2NvcGVkXG4gKiAocGxhdGZvcm0pIHRva2VuIG9yIGFueSBkZWNvZGUgZmFpbHVyZS5cbiAqL1xuZnVuY3Rpb24gZGVjb2RlSnd0U2NvcGUoand0OiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICB0cnkge1xuICAgIGNvbnN0IHBheWxvYWQgPSBqd3Quc3BsaXQoJy4nKVsxXTtcbiAgICBpZiAoIXBheWxvYWQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY29uc3QganNvbiA9IEJ1ZmZlci5mcm9tKHBheWxvYWQucmVwbGFjZSgvLS9nLCAnKycpLnJlcGxhY2UoL18vZywgJy8nKSwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCd1dGY4Jyk7XG4gICAgY29uc3QgY2xhaW1zID0gSlNPTi5wYXJzZShqc29uKSBhcyB7IHNjb3BlPzogdW5rbm93biB9O1xuICAgIHJldHVybiB0eXBlb2YgY2xhaW1zLnNjb3BlID09PSAnc3RyaW5nJyAmJiBjbGFpbXMuc2NvcGUgPyBjbGFpbXMuc2NvcGUgOiB1bmRlZmluZWQ7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIGNvbnN0IHNlY3JldE5hbWUgPSByZXF1aXJlZCgnUExBVEZPUk1fU0VDUkVUX05BTUUnKTtcbiAgY29uc3QgcGxhdGZvcm1VcmwgPSByZXF1aXJlZCgnUExBVEZPUk1fQkFTRV9VUkwnKS5yZXBsYWNlKC9cXC8kLywgJycpO1xuICBjb25zdCBkYXlzID0gcHJvY2Vzcy5lbnYuUkVORVdfREFZUyB8fCAnMzAnO1xuICBjb25zdCB2ZXJzaW9uID0gcHJvY2Vzcy5lbnYuUElQRUxJTkVfTUFOQUdFUl9WRVJTSU9OIHx8ICdsYXRlc3QnO1xuICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OIHx8ICd1cy1lYXN0LTEnO1xuXG4gIC8vIG5wbSBpbiBMYW1iZGEgY2FuIG9ubHkgd3JpdGUgdW5kZXIgL3RtcDsgSE9NRS9jYWNoZS91c2VyY29uZmlnIHBvaW50IHRoZXJlLlxuICBjb25zdCBucG1FbnYgPSB7IC4uLnByb2Nlc3MuZW52LCBIT01FOiBUTVAsIG5wbV9jb25maWdfY2FjaGU6IE5QTV9DQUNIRSB9O1xuXG4gIC8vIDEuIFNjb3BlIGNvbmZpZzogcmVzb2x2ZSBAcGlwZWxpbmUtYnVpbGRlciBmcm9tIHB1YmxpYyBucG0gcmVnYXJkbGVzcyBvZiBhbnlcbiAgLy8gICAgaW5oZXJpdGVkIHJlZ2lzdHJ5LiBXcml0dGVuIGFzIGEgdXNlcmNvbmZpZyBmaWxlIChlbnYgbWFwcGluZyBvZiBzY29wZWRcbiAgLy8gICAga2V5cyBpcyB1bnJlbGlhYmxlKS5cbiAgd3JpdGVGaWxlU3luYyhOUE1SQywgJ0BwaXBlbGluZS1idWlsZGVyOnJlZ2lzdHJ5PWh0dHBzOi8vcmVnaXN0cnkubnBtanMub3JnL1xcbicpO1xuXG4gIC8vIDIuIERvd25sb2FkIHBpcGVsaW5lLW1hbmFnZXIgaW50byAvdG1wLlxuICBta2RpclN5bmMoUE1fUFJFRklYLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtc2c6ICdpbnN0YWxsaW5nIHBpcGVsaW5lLW1hbmFnZXInLCBwYWNrYWdlOiBgJHtQTV9QQUNLQUdFfUAke3ZlcnNpb259YCB9KSk7XG4gIGV4ZWNGaWxlU3luYyhcbiAgICAnbnBtJyxcbiAgICBbJ2luc3RhbGwnLCAnLS1wcmVmaXgnLCBQTV9QUkVGSVgsICctLW5vLWF1ZGl0JywgJy0tbm8tZnVuZCcsICctLXVzZXJjb25maWcnLCBOUE1SQywgYCR7UE1fUEFDS0FHRX1AJHt2ZXJzaW9ufWBdLFxuICAgIHsgZW52OiBucG1FbnYsIHN0ZGlvOiAnaW5oZXJpdCcgfSxcbiAgKTtcblxuICAvLyAzLiBSZWFkIHRoZSBjdXJyZW50IHBsYXRmb3JtIEpXVCDigJQgaXQgYXV0aGVudGljYXRlcyB0aGUgcmVuZXdhbCByZXF1ZXN0LlxuICBjb25zdCBzbSA9IG5ldyBTZWNyZXRzTWFuYWdlckNsaWVudCh7IHJlZ2lvbiB9KTtcbiAgY29uc3QgY3VycmVudCA9IGF3YWl0IHNtLnNlbmQobmV3IEdldFNlY3JldFZhbHVlQ29tbWFuZCh7IFNlY3JldElkOiBzZWNyZXROYW1lIH0pKTtcbiAgaWYgKCFjdXJyZW50LlNlY3JldFN0cmluZykgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgaXMgZW1wdHlgKTtcbiAgY29uc3Qgand0ID0gKEpTT04ucGFyc2UoY3VycmVudC5TZWNyZXRTdHJpbmcpIGFzIHsgcGFzc3dvcmQ/OiBzdHJpbmcgfSkucGFzc3dvcmQ7XG4gIGlmICghand0KSB0aHJvdyBuZXcgRXJyb3IoYFNlY3JldCBcIiR7c2VjcmV0TmFtZX1cIiBtaXNzaW5nIHBhc3N3b3JkIChKV1QpYCk7XG5cbiAgLy8gNC4gUnVuIHN0b3JlLXRva2VuICh3aXRob3V0IC0tc2NoZWR1bGUsIHRoZSBkZWZhdWx0KSBzbyBpdCBPTkxZIHJlLW1pbnRzIGFuZFxuICAvLyAgICB3cml0ZXMgdGhlIHNlY3JldCDigJQgaXQgbXVzdCBub3QgcmVkZXBsb3kgdGhpcyBzdGFjayAod291bGQgcmVjdXJzZSkuIFRoZVxuICAvLyAgICB3cml0ZSB1c2VzIHRoZSBMYW1iZGEgcm9sZSdzIGNyZWRzLlxuICAvLyBzdG9yZS10b2tlbiBubyBsb25nZXIgdGFrZXMgLS1zZWNyZXQtbmFtZTsgaXQgcmVhZHMgUExBVEZPUk1fU0VDUkVUX05BTUUgZnJvbVxuICAvLyB0aGUgZW52aXJvbm1lbnQgKGVsc2UgZGVyaXZlcyBpdCBmcm9tIHRoZSB0b2tlbidzIG9yZykuXG4gIGNvbnN0IGFyZ3MgPSBbJ2luZnJhJywgJ3N0b3JlLXRva2VuJywgJy0tcmVnaW9uJywgcmVnaW9uLCAnLS1kYXlzJywgZGF5c107XG4gIC8vIFByZXNlcnZlIHRoZSB0b2tlbidzIGNhcGFiaWxpdHkgc2NvcGUgb24gcmVuZXdhbCAoZWxzZSBhIHNjb3BlZCBpbmdlc3RcbiAgLy8gY3JlZGVudGlhbCByZS1taW50cyBhcyBhIHNjb3BlbGVzcyBwbGF0Zm9ybSB0b2tlbiBhbmQgaW5nZXN0IHN0YXJ0cyA0MDNpbmcpLlxuICAvLyBUaGUgcmUtbWludCBhdXRoZW50aWNhdGVzIHdpdGggVEhJUyB0b2tlbiwgYW5kIGEgc2NvcGVkIHRva2VuIHN0aWxsIGNhcnJpZXNcbiAgLy8gYSB1c2VySWQsIHNvIC91c2VyL2dlbmVyYXRlLXRva2VuIGFjY2VwdHMgaXQgYW5kIHJlLWlzc3VlcyB0aGUgc2FtZSBzY29wZS5cbiAgY29uc3QgdG9rZW5TY29wZSA9IGRlY29kZUp3dFNjb3BlKGp3dCk7XG4gIGlmICh0b2tlblNjb3BlKSB7XG4gICAgYXJncy5wdXNoKCctLXNjb3BlJywgdG9rZW5TY29wZSk7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IGxldmVsOiAnSU5GTycsIG1zZzogJ3JlbmV3aW5nIHNjb3BlZCB0b2tlbicsIHNjb3BlOiB0b2tlblNjb3BlLCBzZWNyZXROYW1lIH0pKTtcbiAgfVxuICAvLyBTRUNVUklUWTogdGhlIHNwYXduZWQgc3RvcmUtdG9rZW4gUE9TVHMgdGhlIEpXVCB0byB0aGUgcGxhdGZvcm0uIFJlZnVzZSB0b1xuICAvLyBkaXNhYmxlIFRMUyB2ZXJpZmljYXRpb24gaW4gcHJvZHVjdGlvbiBzbyBhIE1JVE0gY2FuJ3QgaGFydmVzdCBpdC4gUHJvZHVjdGlvblxuICAvLyBpcyB0aGlzIExhbWJkYSdzIG5vcm1hbCBtb2RlICh0aGUgcmVuZXcgc3RhY2sgc2V0cyBOT0RFX0VOVj1wcm9kdWN0aW9uKSwgc29cbiAgLy8gUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpcyBob25vcmVkIE9OTFkgb3V0c2lkZSBwcm9kdWN0aW9uLiBUaGlzIGlubGluZXMgdGhlXG4gIC8vIHNhbWUgYE5PREVfRU5WPT09J3Byb2R1Y3Rpb24nYCBwb2xpY3kgYXMgdXRpbHMvdGxzLnRzI2Fzc2VydFNzbERpc2FibGVBbGxvd2VkXG4gIC8vICh0aGUgaGFuZGxlciBzaGlwcyBhcyBhIHNpbmdsZSBzZWxmLWNvbnRhaW5lZCBpbmRleC5tanMgYW5kIGNhbid0IGltcG9ydCBpdCkuXG4gIGNvbnN0IGlzUHJvZHVjdGlvbiA9IHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSAncHJvZHVjdGlvbic7XG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9WRVJJRllfU1NMID09PSAnZmFsc2UnKSB7XG4gICAgaWYgKGlzUHJvZHVjdGlvbikge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgbGV2ZWw6ICdXQVJOJywgbXNnOiAnUmVmdXNpbmcgUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpbiBwcm9kdWN0aW9uIChOT0RFX0VOVj1wcm9kdWN0aW9uKSDigJQgVExTIHZlcmlmaWNhdGlvbiBzdGF5cyBlbmFibGVkJyB9KSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGFyZ3MucHVzaCgnLS1uby12ZXJpZnktc3NsJyk7XG4gICAgfVxuICB9XG5cbiAgZXhlY0ZpbGVTeW5jKCdub2RlJywgW0NMSSwgLi4uYXJnc10sIHtcbiAgICAvLyBOT0RFX0VOViBpcyBpbmhlcml0ZWQgdmlhIG5wbUVudiAoc3ByZWFkIG9mIHByb2Nlc3MuZW52KSwgc28gdGhlIHNwYXduZWQgQ0xJXG4gICAgLy8gZW5mb3JjZXMgdGhlIHNhbWUgcHJvZHVjdGlvbiBUTFMtZGlzYWJsZSByZWZ1c2FsIGFzIHRoaXMgaGFuZGxlci5cbiAgICBlbnY6IHsgLi4ubnBtRW52LCBQTEFURk9STV9UT0tFTjogand0LCBQTEFURk9STV9CQVNFX1VSTDogcGxhdGZvcm1VcmwsIFBMQVRGT1JNX1NFQ1JFVF9OQU1FOiBzZWNyZXROYW1lIH0sXG4gICAgc3RkaW86ICdpbmhlcml0JyxcbiAgfSk7XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtc2c6ICdwbGF0Zm9ybSB0b2tlbiByZW5ld2VkJywgc2VjcmV0TmFtZSB9KSk7XG59O1xuIl19
|
|
162
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tcmVuZXctaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sYW1iZGEvdG9rZW4tcmVuZXctaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBK0JHO0FBQ0gsT0FBTyxFQUFFLG9CQUFvQixFQUFFLHFCQUFxQixFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFckgsa0RBQWtEO0FBQ2xELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQztBQUUvQiw0RUFBNEU7QUFDNUUsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQztBQUVsRCxTQUFTLFFBQVEsQ0FBQyxJQUFZO0lBQzVCLE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDNUIsSUFBSSxDQUFDLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLEdBQUcsSUFBSSxtQ0FBbUMsQ0FBQyxDQUFDO0lBQ3BFLE9BQU8sQ0FBQyxDQUFDO0FBQ1gsQ0FBQztBQUVELFNBQVMsR0FBRyxDQUFDLEtBQWdDLEVBQUUsR0FBVyxFQUFFLElBQThCO0lBQ3hGLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNyRCxzQ0FBc0M7SUFDdEMsSUFBSSxLQUFLLEtBQUssT0FBTztRQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7O1FBQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNyRSxDQUFDO0FBb0JELDRFQUE0RTtBQUM1RSxLQUFLLFVBQVUsSUFBSSxDQUNqQixPQUFlLEVBQ2YsS0FBYSxFQUNiLElB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RUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0FBQzVGLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFNjaGVkdWxlZCBLRVkgUk9UQVRPUiBmb3IgdGhlIHNlcnZpY2UtYWNjb3VudCBjcmVkZW50aWFsIGluIFNlY3JldHMgTWFuYWdlclxuICogKCNOMikuIERlcGxveWVkICsgc2NoZWR1bGVkIChvbmNlIGEgZGF5KSBieSBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlblxuICogLS1zY2hlZHVsZWAuXG4gKlxuICogSXQgdXNlZCB0byByZS1taW50IGEgcGVyc29uJ3MgbWFjaGluZS1zZXNzaW9uIEpXVCBieSBucG0taW5zdGFsbGluZyB0aGUgQ0xJIGF0XG4gKiBydW50aW1lIGFuZCBzaGVsbGluZyBvdXQgdG8gYHN0b3JlLXRva2VuYC4gVGhlcmUgaXMgbm90aGluZyBsZWZ0IHRvIHJlLW1pbnQ6XG4gKiB0aGUgc3RvcmVkIGNyZWRlbnRpYWwgaXMgYW4gb3BhcXVlIGBwYl9zYV/igKZgIGtleSwgYW5kIHBsYXRmb3JtIHJvdGF0ZXMgaXRcbiAqIHRocm91Z2ggdHdvIHNtYWxsIHByZS1hdXRoIGVuZHBvaW50cyB3aGVyZSB0aGUgS0VZIElUU0VMRiBpcyB0aGUgYXV0aG9yaXphdGlvblxuICogKGFuIHVuYXR0ZW5kZWQgbWFjaGluZSBoYXMgbm8gcGFzc3dvcmQgYW5kIGNhbm5vdCBzdGVwIHVwKS4gU28gdGhpcyBoYW5kbGVyIGlzXG4gKiB0aHJlZSBIVFRQIGNhbGxzIGFuZCBhIHNlY3JldCB3cml0ZSwgd2l0aCBubyBucG0gaW5zdGFsbCwgbm8gQ0xJLCBubyBgL3RtcGAuXG4gKlxuICogVEhFIE9SREVSIElTIFRIRSBXSE9MRSBERVNJR04uIEF0IG5vIHBvaW50IG1heSB0aGUgc2VjcmV0IG5hbWUgYSBjcmVkZW50aWFsXG4gKiB0aGF0IGRvZXMgbm90IHdvcms6XG4gKlxuICogICAxLiBST1RBVEUg4oCUIG1pbnQgYSBzaWJsaW5nIGtleSBvbiB0aGUgc2FtZSBhY2NvdW50LiBUaGUgY3VycmVudCBrZXkgc3RheXNcbiAqICAgICAgTElWRS4gRmFpbCBoZXJlIGFuZCB0aGUgc2VjcmV0IGlzIHVudG91Y2hlZDogdGhlIG9sZCBrZXkgc3RpbGwgd29ya3MuXG4gKiAgIDIuIFNUT1JFICDigJQgd3JpdGUgdGhlIG5ldyBrZXkgdG8gdGhlIHNlY3JldC4gRmFpbCBoZXJlIGFuZCB0aGUgc2VjcmV0IHN0aWxsXG4gKiAgICAgIGhvbGRzIHRoZSBvbGQga2V5LCB3aGljaCBpcyBzdGlsbCBsaXZlOyB0aGUgbmV3IGtleSBpcyBhbiBvcnBoYW4gdGhhdFxuICogICAgICBleHBpcmVzIG9uIGl0cyBvd24gKGFuZCB0aGUgbmV4dCBydW4ncyByb3RhdGUgcHJ1bmVzIGl0IGF0IHRoZSBjYXApLlxuICogICAzLiBSRVZPS0Ug4oCUIHJldGlyZSB0aGUgb2xkIGtleSwgYXV0aGVudGljYXRlZCB3aXRoIHRoZSBORVcgb25lLiBGYWlsIGhlcmVcbiAqICAgICAgYW5kIEJPVEgga2V5cyB3b3JrOyB0aGUgY3JlZGVudGlhbCBpcyBoZWFsdGh5IGFuZCB0aGUgc3RhbGUga2V5IGV4cGlyZXNcbiAqICAgICAgb24gaXRzIG93bi4gTG9nZ2VkIGF0IEVSUk9SIHNvIGl0IGlzIHZpc2libGUsIGJ1dCBpdCBkb2VzIG5vdCBmYWlsIHRoZVxuICogICAgICBpbnZvY2F0aW9uOiBhIHJldHJ5IHdvdWxkIHJvdGF0ZSBhZ2FpbiBhbmQgY2h1cm4ga2V5cyBmb3Igbm8gZ2Fpbi5cbiAqXG4gKiBSZXZva2UtdGhlbi1jcmVhdGUgd291bGQgaW52ZXJ0IGV2ZXJ5IG9uZSBvZiB0aG9zZTogYSBmYWlsdXJlIGFmdGVyIHRoZSByZXZva2VcbiAqIGxlYXZlcyB0aGUgZGVwbG95bWVudCB3aXRoIG5vIHdvcmtpbmcgY3JlZGVudGlhbCBhdCBhbGwuXG4gKlxuICogRW52OiBQTEFURk9STV9TRUNSRVRfTkFNRSwgUExBVEZPUk1fQkFTRV9VUkwsIFJFTkVXX0RBWVMgKGRlZmF1bHQgMzApLFxuICogICAgICBQTEFURk9STV9WRVJJRllfU1NMIChcImZhbHNlXCIgdG8gZGlzYWJsZSBUTFMgdmVyaWZpY2F0aW9uIOKAlCByZWZ1c2VkIGluXG4gKiAgICAgIHByb2R1Y3Rpb24pLlxuICovXG5pbXBvcnQgeyBTZWNyZXRzTWFuYWdlckNsaWVudCwgR2V0U2VjcmV0VmFsdWVDb21tYW5kLCBQdXRTZWNyZXRWYWx1ZUNvbW1hbmQgfSBmcm9tICdAYXdzLXNkay9jbGllbnQtc2VjcmV0cy1tYW5hZ2VyJztcblxuLyoqIFBlci1yZXF1ZXN0IHRpbWVvdXQgZm9yIHRoZSBwbGF0Zm9ybSBjYWxscy4gKi9cbmNvbnN0IEhUVFBfVElNRU9VVF9NUyA9IDEwXzAwMDtcblxuLyoqIEFjY2Vzcy1rZXkgcHJlZml4ZXMgcGxhdGZvcm0gaXNzdWVzLiBBIHN0b3JlZCBKV1QgaXMgbm90IG9uZSBvZiB0aGVtLiAqL1xuY29uc3QgQUNDRVNTX0tFWV9QUkVGSVhFUyA9IFsncGJfc2FfJywgJ3BiX3BhdF8nXTtcblxuZnVuY3Rpb24gcmVxdWlyZWQobmFtZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgdiA9IHByb2Nlc3MuZW52W25hbWVdO1xuICBpZiAoIXYpIHRocm93IG5ldyBFcnJvcihgJHtuYW1lfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZGApO1xuICByZXR1cm4gdjtcbn1cblxuZnVuY3Rpb24gbG9nKGxldmVsOiAnSU5GTycgfCAnV0FSTicgfCAnRVJST1InLCBtc2c6IHN0cmluZywgZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogdm9pZCB7XG4gIGNvbnN0IGxpbmUgPSBKU09OLnN0cmluZ2lmeSh7IGxldmVsLCBtc2csIC4uLmRhdGEgfSk7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gIGlmIChsZXZlbCA9PT0gJ0VSUk9SJykgY29uc29sZS5lcnJvcihsaW5lKTsgZWxzZSBjb25zb2xlLmxvZyhsaW5lKTtcbn1cblxuLyoqIFRoZSBzdG9yZWQgc2VjcmV0LCBhcyBgc3RvcmUtdG9rZW5gIHdyaXRlcyBpdC4gKi9cbmludGVyZmFjZSBTdG9yZWRDcmVkZW50aWFsIHtcbiAgdXNlcm5hbWU/OiBzdHJpbmc7XG4gIC8qKiBUaGUgY2Fub25pY2FsIGNyZWRlbnRpYWwgZmllbGQg4oCUIGFuIG9wYXF1ZSBgcGJfc2Ff4oCmYCBrZXkgYWZ0ZXIgdGhlIGN1dG92ZXIuICovXG4gIHBhc3N3b3JkPzogc3RyaW5nO1xuICBwbGF0Zm9ybVVybD86IHN0cmluZztcbiAgb3JnYW5pemF0aW9uSWQ/OiBzdHJpbmc7XG4gIHNlcnZpY2VBY2NvdW50SWQ/OiBzdHJpbmc7XG4gIHNlcnZpY2VBY2NvdW50TmFtZT86IHN0cmluZztcbiAgLyoqIFJlY29yZCBpZCBvZiB0aGUga2V5IGluIGBwYXNzd29yZGA7IHdoYXQgc3RlcCAzIHJldGlyZXMuICovXG4gIGtleUlkPzogc3RyaW5nO1xuICBzY29wZT86IHN0cmluZyB8IG51bGw7XG4gIGV4cGlyZXNJbj86IG51bWJlcjtcbiAgZXhwaXJlc0F0Pzogc3RyaW5nO1xuICBjcmVhdGVkQXQ/OiBzdHJpbmc7XG4gIFtrZXk6IHN0cmluZ106IHVua25vd247XG59XG5cbi8qKiBQT1NUIEpTT04gdG8gdGhlIHBsYXRmb3JtLCByZXR1cm5pbmcgdGhlIHBhcnNlZCBib2R5IHBsdXMgdGhlIHN0YXR1cy4gKi9cbmFzeW5jIGZ1bmN0aW9uIHBvc3QoXG4gIGJhc2VVcmw6IHN0cmluZyxcbiAgcm91dGU6IHN0cmluZyxcbiAgYm9keTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4pOiBQcm9taXNlPHsgc3RhdHVzOiBudW1iZXI7IGJvZHk6IHsgZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+OyBtZXNzYWdlPzogc3RyaW5nIH0gfT4ge1xuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCBIVFRQX1RJTUVPVVRfTVMpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGAke2Jhc2VVcmx9JHtyb3V0ZX1gLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoYm9keSksXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuICAgIGNvbnN0IHBhcnNlZCA9IGF3YWl0IHJlcy5qc29uKCkuY2F0Y2goKCkgPT4gKHt9KSkgYXMgeyBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47IG1lc3NhZ2U/OiBzdHJpbmcgfTtcbiAgICByZXR1cm4geyBzdGF0dXM6IHJlcy5zdGF0dXMsIGJvZHk6IHBhcnNlZCB9O1xuICB9IGZpbmFsbHkge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gIH1cbn1cblxuLyoqXG4gKiBTRUNVUklUWTogdGhlc2UgY2FsbHMgY2FycnkgYSBsaXZlIGNyZWRlbnRpYWwuIFJlZnVzZSB0byBkaXNhYmxlIFRMU1xuICogdmVyaWZpY2F0aW9uIGluIHByb2R1Y3Rpb24gc28gYSBNSVRNIGNhbid0IGhhcnZlc3QgaXQuIFByb2R1Y3Rpb24gaXMgdGhpc1xuICogTGFtYmRhJ3Mgbm9ybWFsIG1vZGUgKHRoZSByb3RhdGlvbiBzdGFjayBzZXRzIE5PREVfRU5WPXByb2R1Y3Rpb24pLCBzb1xuICogUExBVEZPUk1fVkVSSUZZX1NTTD1mYWxzZSBpcyBob25vcmVkIE9OTFkgb3V0c2lkZSBwcm9kdWN0aW9uLiBUaGlzIGlubGluZXMgdGhlXG4gKiBzYW1lIGBOT0RFX0VOVj09PSdwcm9kdWN0aW9uJ2AgcG9saWN5IGFzIHV0aWxzL3Rscy50cyNhc3NlcnRTc2xEaXNhYmxlQWxsb3dlZFxuICogKHRoZSBoYW5kbGVyIHNoaXBzIGFzIGEgc2luZ2xlIHNlbGYtY29udGFpbmVkIGluZGV4Lm1qcyBhbmQgY2FuJ3QgaW1wb3J0IGl0KS5cbiAqL1xuZnVuY3Rpb24gYXBwbHlUbHNQb2xpY3koKTogdm9pZCB7XG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9WRVJJRllfU1NMICE9PSAnZmFsc2UnKSByZXR1cm47XG4gIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ3Byb2R1Y3Rpb24nKSB7XG4gICAgbG9nKCdXQVJOJywgJ1JlZnVzaW5nIFBMQVRGT1JNX1ZFUklGWV9TU0w9ZmFsc2UgaW4gcHJvZHVjdGlvbiAoTk9ERV9FTlY9cHJvZHVjdGlvbikg4oCUIFRMUyB2ZXJpZmljYXRpb24gc3RheXMgZW5hYmxlZCcpO1xuICAgIHJldHVybjtcbiAgfVxuICBwcm9jZXNzLmVudi5OT0RFX1RMU19SRUpFQ1RfVU5BVVRIT1JJWkVEID0gJzAnO1xufVxuXG5leHBvcnQgY29uc3QgaGFuZGxlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgY29uc3Qgc2VjcmV0TmFtZSA9IHJlcXVpcmVkKCdQTEFURk9STV9TRUNSRVRfTkFNRScpO1xuICBjb25zdCBlbnZVcmwgPSByZXF1aXJlZCgnUExBVEZPUk1fQkFTRV9VUkwnKS5yZXBsYWNlKC9cXC8rJC8sICcnKTtcbiAgY29uc3QgZGF5cyA9IE51bWJlcihwcm9jZXNzLmVudi5SRU5FV19EQVlTIHx8ICczMCcpO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShkYXlzKSB8fCBkYXlzIDwgMSB8fCBkYXlzID4gMzY1KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBSRU5FV19EQVlTIG11c3QgYmUgYmV0d2VlbiAxIGFuZCAzNjUgKGdvdCBcIiR7cHJvY2Vzcy5lbnYuUkVORVdfREFZU31cIilgKTtcbiAgfVxuICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OIHx8ICd1cy1lYXN0LTEnO1xuXG4gIGFwcGx5VGxzUG9saWN5KCk7XG5cbiAgY29uc3Qgc20gPSBuZXcgU2VjcmV0c01hbmFnZXJDbGllbnQoeyByZWdpb24gfSk7XG4gIGNvbnN0IGN1cnJlbnQgPSBhd2FpdCBzbS5zZW5kKG5ldyBHZXRTZWNyZXRWYWx1ZUNvbW1hbmQoeyBTZWNyZXRJZDogc2VjcmV0TmFtZSB9KSk7XG4gIGlmICghY3VycmVudC5TZWNyZXRTdHJpbmcpIHRocm93IG5ldyBFcnJvcihgU2VjcmV0IFwiJHtzZWNyZXROYW1lfVwiIGlzIGVtcHR5YCk7XG4gIGNvbnN0IHN0b3JlZCA9IEpTT04ucGFyc2UoY3VycmVudC5TZWNyZXRTdHJpbmcpIGFzIFN0b3JlZENyZWRlbnRpYWw7XG5cbiAgY29uc3Qgb2xkS2V5ID0gc3RvcmVkLnBhc3N3b3JkO1xuICBpZiAoIW9sZEtleSkgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgbWlzc2luZyBwYXNzd29yZCAoc2VydmljZS1hY2NvdW50IGtleSlgKTtcbiAgaWYgKCFBQ0NFU1NfS0VZX1BSRUZJWEVTLnNvbWUoKHApID0+IG9sZEtleS5zdGFydHNXaXRoKHApKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgZG9lcyBub3QgaG9sZCBhbiBvcGFxdWUgc2VydmljZS1hY2NvdW50IGtleS4gYFxuICAgICAgKyAnUmUtcnVuIFwicGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlblwiIHRvIHJlaXNzdWUgaXQgYXMgYSBrZXkgKHNlZSBkb2NzL3J1bmJvb2tzL2FjY2Vzcy1rZXktY3V0b3Zlci5tZCkuJyxcbiAgICApO1xuICB9XG4gIC8vIFRoZSBwbGF0Zm9ybSB0aGlzIGNyZWRlbnRpYWwgYmVsb25ncyB0byBpcyByZWNvcmRlZCBJTiB0aGUgc2VjcmV0OyB0aGUgZW52XG4gIC8vIHZhciBpcyB0aGUgZmFsbGJhY2sgZm9yIGEgc2VjcmV0IHdyaXR0ZW4gYmVmb3JlIHRoYXQgZmllbGQgZXhpc3RlZC5cbiAgY29uc3QgcGxhdGZvcm1VcmwgPSAoc3RvcmVkLnBsYXRmb3JtVXJsIHx8IGVudlVybCkucmVwbGFjZSgvXFwvKyQvLCAnJyk7XG5cbiAgLy8g4pSA4pSAIDEuIFJPVEFURSDigJQgbWludCB0aGUgcmVwbGFjZW1lbnQuIFRoZSBvbGQga2V5IHN0YXlzIGxpdmUuIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBjb25zdCByb3RhdGUgPSBhd2FpdCBwb3N0KHBsYXRmb3JtVXJsLCAnL2FwaS9hdXRoL2tleS9yb3RhdGUnLCB7XG4gICAga2V5OiBvbGRLZXksXG4gICAgbmFtZTogYCR7c3RvcmVkLnNlcnZpY2VBY2NvdW50TmFtZSB8fCAncm90YXRlZCd9LSR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpLnNsaWNlKDAsIDEwKX1gLFxuICAgIGV4cGlyZXNJbjogZGF5cyAqIDI0ICogNjAgKiA2MCxcbiAgfSk7XG4gIGlmIChyb3RhdGUuc3RhdHVzIDwgMjAwIHx8IHJvdGF0ZS5zdGF0dXMgPj0gMzAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEtleSByb3RhdGlvbiByZWZ1c2VkICgke3JvdGF0ZS5zdGF0dXN9KTogJHtyb3RhdGUuYm9keS5tZXNzYWdlIHx8ICd1bmtub3duIHJlYXNvbid9LiBgXG4gICAgICArICdUaGUgc3RvcmVkIGtleSBpcyB1bmNoYW5nZWQgYW5kIHN0aWxsIGxpdmUuJyxcbiAgICApO1xuICB9XG4gIGNvbnN0IG5ld0tleSA9IHJvdGF0ZS5ib2R5LmRhdGE/LmtleSBhcyBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGNvbnN0IG5ld0tleUlkID0gcm90YXRlLmJvZHkuZGF0YT8ua2V5SWQgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBpZiAoIW5ld0tleSB8fCAhbmV3S2V5SWQpIHRocm93IG5ldyBFcnJvcignS2V5IHJvdGF0aW9uIHJldHVybmVkIG5vIHJlcGxhY2VtZW50IGtleSDigJQgdGhlIHN0b3JlZCBrZXkgaXMgdW5jaGFuZ2VkJyk7XG4gIGNvbnN0IHBydW5lZCA9IChyb3RhdGUuYm9keS5kYXRhPy5wcnVuZWRLZXlJZHMgYXMgc3RyaW5nW10gfCB1bmRlZmluZWQpID8/IFtdO1xuICBpZiAocHJ1bmVkLmxlbmd0aCA+IDApIHtcbiAgICBsb2coJ1dBUk4nLCAnUGxhdGZvcm0gcmV0aXJlZCBzdGFsZSBzaWJsaW5nIGtleXMgdG8gc3RheSB1bmRlciB0aGUgYWN0aXZlLWtleSBjYXAnLCB7IHNlY3JldE5hbWUsIHBydW5lZEtleUlkczogcHJ1bmVkIH0pO1xuICB9XG4gIGxvZygnSU5GTycsICdNaW50ZWQgdGhlIHJlcGxhY2VtZW50IGtleScsIHsgc2VjcmV0TmFtZSwga2V5SWQ6IG5ld0tleUlkIH0pO1xuXG4gIC8vIOKUgOKUgCAyLiBTVE9SRSDigJQgdGhlIHNlY3JldCBub3cgbmFtZXMgYSBsaXZlIGNyZWRlbnRpYWwuIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBjb25zdCBuZXh0OiBTdG9yZWRDcmVkZW50aWFsID0ge1xuICAgIC4uLnN0b3JlZCxcbiAgICBwYXNzd29yZDogbmV3S2V5LFxuICAgIGtleUlkOiBuZXdLZXlJZCxcbiAgICBwbGF0Zm9ybVVybCxcbiAgICBleHBpcmVzSW46IGRheXMgKiAyNCAqIDYwICogNjAsXG4gICAgLi4uKHJvdGF0ZS5ib2R5LmRhdGE/LmV4cGlyZXNBdCA/IHsgZXhwaXJlc0F0OiByb3RhdGUuYm9keS5kYXRhLmV4cGlyZXNBdCBhcyBzdHJpbmcgfSA6IHt9KSxcbiAgICBjcmVhdGVkQXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgfTtcbiAgYXdhaXQgc20uc2VuZChuZXcgUHV0U2VjcmV0VmFsdWVDb21tYW5kKHsgU2VjcmV0SWQ6IHNlY3JldE5hbWUsIFNlY3JldFN0cmluZzogSlNPTi5zdHJpbmdpZnkobmV4dCkgfSkpO1xuICBsb2coJ0lORk8nLCAnU3RvcmVkIHRoZSByb3RhdGVkIGtleScsIHsgc2VjcmV0TmFtZSwga2V5SWQ6IG5ld0tleUlkIH0pO1xuXG4gIC8vIOKUgOKUgCAzLiBSRVZPS0Ug4oCUIHJldGlyZSB0aGUgcHJlZGVjZXNzb3IsIHVzaW5nIHRoZSBrZXkgdGhhdCByZXBsYWNlZCBpdC4g4pSA4pSA4pSA4pSAXG4gIC8vIEJlc3QtZWZmb3J0IGJ5IGRlc2lnbjogdGhlIGNyZWRlbnRpYWwgaXMgYWxyZWFkeSBoZWFsdGh5LCBhbmQgZmFpbGluZyB0aGVcbiAgLy8gaW52b2NhdGlvbiBoZXJlIHdvdWxkIG9ubHkgbWFrZSB0aGUgbmV4dCByZXRyeSByb3RhdGUgYWdhaW4uXG4gIGNvbnN0IG9sZEtleUlkID0gc3RvcmVkLmtleUlkO1xuICBpZiAoIW9sZEtleUlkKSB7XG4gICAgbG9nKCdXQVJOJywgJ1NlY3JldCByZWNvcmRlZCBubyBwcmV2aW91cyBrZXlJZCDigJQgbm90aGluZyB0byByZXZva2UgKGl0IGV4cGlyZXMgb24gaXRzIG93biknLCB7IHNlY3JldE5hbWUgfSk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHJldm9rZSA9IGF3YWl0IHBvc3QocGxhdGZvcm1VcmwsICcvYXBpL2F1dGgva2V5L3Jldm9rZScsIHsga2V5OiBuZXdLZXksIGtleUlkOiBvbGRLZXlJZCB9KVxuICAgIC5jYXRjaCgoZXJyKSA9PiAoeyBzdGF0dXM6IDAsIGJvZHk6IHsgbWVzc2FnZTogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0gfSkpO1xuICBpZiAocmV2b2tlLnN0YXR1cyA8IDIwMCB8fCByZXZva2Uuc3RhdHVzID49IDMwMCkge1xuICAgIGxvZygnRVJST1InLCAnUm90YXRlZCBhbmQgc3RvcmVkIHRoZSBuZXcga2V5LCBidXQgY291bGQgTk9UIHJldm9rZSBpdHMgcHJlZGVjZXNzb3Ig4oCUIGl0IHN0YXlzIHZhbGlkIHVudGlsIGl0IGV4cGlyZXMnLCB7XG4gICAgICBzZWNyZXROYW1lLCBzdGFsZUtleUlkOiBvbGRLZXlJZCwgc3RhdHVzOiByZXZva2Uuc3RhdHVzLCByZWFzb246IHJldm9rZS5ib2R5Lm1lc3NhZ2UsXG4gICAgfSk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGxvZygnSU5GTycsICdSb3RhdGlvbiBjb21wbGV0ZScsIHsgc2VjcmV0TmFtZSwga2V5SWQ6IG5ld0tleUlkLCByZXZva2VkS2V5SWQ6IG9sZEtleUlkIH0pO1xufTtcbiJdfQ==
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"PlatformSecretName": {
|
|
10
10
|
"Type": "String",
|
|
11
|
-
"Description": "Secrets Manager secret name containing the
|
|
11
|
+
"Description": "Secrets Manager secret name containing the reporting:ingest service-account key (e.g. pipeline-builder/{orgId}/reporting-ingest)"
|
|
12
12
|
},
|
|
13
13
|
"DoraEnabled": {
|
|
14
14
|
"Type": "String",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"AWSTemplateFormatVersion": "2010-09-09",
|
|
3
|
-
"Description": "Pipeline Builder
|
|
3
|
+
"Description": "Pipeline Builder service-account KEY ROTATION — a scheduled Lambda that replaces the pb_sa_ key in Secrets Manager before it expires (rotate, store, then revoke the predecessor), so CodeBuild/Lambda credentials never lapse. Lambda code is deployed separately via pipeline-manager infra store-token.",
|
|
4
4
|
"Parameters": {
|
|
5
5
|
"PlatformBaseUrl": {
|
|
6
6
|
"Type": "String",
|
|
@@ -8,29 +8,29 @@
|
|
|
8
8
|
},
|
|
9
9
|
"PlatformSecretName": {
|
|
10
10
|
"Type": "String",
|
|
11
|
-
"Description": "Secrets Manager secret name holding the
|
|
11
|
+
"Description": "Secrets Manager secret name holding the service-account key (e.g. pipeline-builder/{orgId}/platform)"
|
|
12
12
|
},
|
|
13
13
|
"RenewDays": {
|
|
14
14
|
"Type": "Number",
|
|
15
15
|
"Default": 30,
|
|
16
16
|
"MinValue": 1,
|
|
17
17
|
"MaxValue": 365,
|
|
18
|
-
"Description": "Lifetime in days requested for each
|
|
18
|
+
"Description": "Lifetime in days requested for each rotated key"
|
|
19
19
|
},
|
|
20
20
|
"ScheduleExpression": {
|
|
21
21
|
"Type": "String",
|
|
22
22
|
"Default": "cron(0 0 * * ? *)",
|
|
23
|
-
"Description": "EventBridge schedule expression for the
|
|
23
|
+
"Description": "EventBridge schedule expression for the rotation (default: daily at 00:00 UTC)"
|
|
24
24
|
},
|
|
25
25
|
"PipelineManagerVersion": {
|
|
26
26
|
"Type": "String",
|
|
27
27
|
"Default": "latest",
|
|
28
|
-
"Description": "Version of @pipeline-builder/pipeline-manager the
|
|
28
|
+
"Description": "Version of @pipeline-builder/pipeline-manager that deployed this stack. Provenance only — the rotator calls the platform directly and installs nothing at runtime."
|
|
29
29
|
},
|
|
30
30
|
"NameSuffix": {
|
|
31
31
|
"Type": "String",
|
|
32
32
|
"Default": "",
|
|
33
|
-
"Description": "Suffix appended to the
|
|
33
|
+
"Description": "Suffix appended to the rotation stack's resource names so a scoped-token renewal (e.g. '-reporting-ingest') coexists with the platform-token stack instead of clobbering its role/function/rule. Empty for the platform secret."
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"Resources": {
|
|
@@ -77,9 +77,8 @@
|
|
|
77
77
|
"ZipFile": "exports.handler=async()=>{console.log('Placeholder handler — run pipeline-manager infra store-token to deploy real code');return{statusCode:200}}"
|
|
78
78
|
},
|
|
79
79
|
"Role": { "Fn::GetAtt": ["LambdaRole", "Arn"] },
|
|
80
|
-
"Timeout":
|
|
81
|
-
"MemorySize":
|
|
82
|
-
"EphemeralStorage": { "Size": 1024 },
|
|
80
|
+
"Timeout": 60,
|
|
81
|
+
"MemorySize": 256,
|
|
83
82
|
"Environment": {
|
|
84
83
|
"Variables": {
|
|
85
84
|
"PLATFORM_BASE_URL": { "Ref": "PlatformBaseUrl" },
|
|
@@ -95,7 +94,7 @@
|
|
|
95
94
|
"Type": "AWS::Events::Rule",
|
|
96
95
|
"Properties": {
|
|
97
96
|
"Name": { "Fn::Sub": "pipeline-builder-token-renew${NameSuffix}" },
|
|
98
|
-
"Description": "Scheduled
|
|
97
|
+
"Description": "Scheduled service-account key rotation",
|
|
99
98
|
"State": "ENABLED",
|
|
100
99
|
"ScheduleExpression": { "Ref": "ScheduleExpression" },
|
|
101
100
|
"Targets": [{
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import https from 'https';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* the
|
|
3
|
+
* Session payload as a NON-BROWSER client receives it: the refresh token rides
|
|
4
|
+
* in the body (the browser's goes out as an HttpOnly cookie instead).
|
|
5
5
|
*/
|
|
6
|
+
export interface SessionTokens {
|
|
7
|
+
accessToken: string;
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
expiresIn?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Envelope every platform auth endpoint wraps its session tokens in. */
|
|
6
12
|
export interface AuthResponse {
|
|
7
13
|
success: boolean;
|
|
8
|
-
data:
|
|
9
|
-
accessToken: string;
|
|
10
|
-
refreshToken: string;
|
|
11
|
-
};
|
|
14
|
+
data: SessionTokens;
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
|
-
* Guard the credential-transmitting commands (`login`, `
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
+
* Guard the credential-transmitting commands (`login`, `pat`) against disabling
|
|
18
|
+
* TLS verification in production. The device flow keeps passwords off the wire
|
|
19
|
+
* entirely, but the session tokens it returns are bearer credentials — a MITM on
|
|
20
|
+
* a `prod + --no-verify-ssl` run would harvest them just the same.
|
|
17
21
|
*
|
|
18
22
|
* Unlike {@link assertSslDisableAllowed} (which throws — for paths already inside
|
|
19
23
|
* a try/catch), this prints and hard-exits, matching the pre-flight checks these
|
|
@@ -21,18 +25,6 @@ export interface AuthResponse {
|
|
|
21
25
|
* `--no-verify-ssl` still works against self-signed dev platforms.
|
|
22
26
|
*/
|
|
23
27
|
export declare function assertCredentialTlsAllowed(verifySsl: boolean | undefined): void;
|
|
24
|
-
/**
|
|
25
|
-
* Resolve credentials from flags, falling back to the
|
|
26
|
-
* PLATFORM_IDENTIFIER / PLATFORM_PASSWORD env vars (so the password need not
|
|
27
|
-
* appear in shell history / `ps`).
|
|
28
|
-
*/
|
|
29
|
-
export declare function resolveCredentials(options: {
|
|
30
|
-
identifier?: string;
|
|
31
|
-
password?: string;
|
|
32
|
-
}): {
|
|
33
|
-
identifier?: string;
|
|
34
|
-
password?: string;
|
|
35
|
-
};
|
|
36
28
|
/**
|
|
37
29
|
* Build the https agent used by the credential commands. `--verify-ssl`
|
|
38
30
|
* defaults to on; only an explicit `--no-verify-ssl` relaxes it (and only
|
|
@@ -40,22 +32,13 @@ export declare function resolveCredentials(options: {
|
|
|
40
32
|
*/
|
|
41
33
|
export declare function credentialHttpsAgent(verifySsl: boolean | undefined): https.Agent;
|
|
42
34
|
/**
|
|
43
|
-
* POST /api/auth/
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* its
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
identifier: string;
|
|
51
|
-
password: string;
|
|
52
|
-
httpsAgent: https.Agent;
|
|
53
|
-
timeout: number;
|
|
54
|
-
}): Promise<string | undefined>;
|
|
55
|
-
/**
|
|
56
|
-
* POST /api/auth/switch-org with the current access token and return the new,
|
|
57
|
-
* org-scoped access token. Prints a success line (unless `quiet`) and hard-exits
|
|
58
|
-
* on a missing token — both credential commands treat a failed switch the same.
|
|
35
|
+
* POST /api/auth/switch-org with the current access token and return the WHOLE
|
|
36
|
+
* re-issued session.
|
|
37
|
+
*
|
|
38
|
+
* The switch re-issues the same session slot, which ROTATES its refresh token —
|
|
39
|
+
* so a caller that keeps only the access token is left holding a dead refresh
|
|
40
|
+
* token. Prints a success line (unless `quiet`) and hard-exits on a missing
|
|
41
|
+
* token, since both credential commands treat a failed switch the same.
|
|
59
42
|
*/
|
|
60
43
|
export declare function switchOrganization(params: {
|
|
61
44
|
url: string;
|
|
@@ -64,5 +47,5 @@ export declare function switchOrganization(params: {
|
|
|
64
47
|
httpsAgent: https.Agent;
|
|
65
48
|
timeout: number;
|
|
66
49
|
quiet: boolean;
|
|
67
|
-
}): Promise<
|
|
50
|
+
}): Promise<SessionTokens>;
|
|
68
51
|
//# sourceMappingURL=auth-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-utils.d.ts","sourceRoot":"","sources":["../../src/utils/auth-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B;;;GAGG;AACH,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"auth-utils.d.ts","sourceRoot":"","sources":["../../src/utils/auth-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,aAAa,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAK/E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC,KAAK,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,aAAa,CAAC,CAwBzB"}
|