sliftutils 1.7.127 → 1.7.129
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/bin/addmachine.js +9 -0
- package/bin/machineclient.js +9 -0
- package/bin/machineserver.js +10 -0
- package/index.d.ts +15 -0
- package/misc/dist/ownIPs.ts.cache +43 -0
- package/misc/dist/strings.ts.cache +2 -2
- package/misc/https/dist/certs.ts.cache +2 -2
- package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
- package/misc/ownIPs.d.ts +11 -0
- package/misc/ownIPs.ts +34 -0
- package/package.json +9 -3
- package/security/authorizedKeys/authorizedKeys.ts +34 -0
- package/security/authorizedKeys/daemon/authLog.ts +12 -5
- package/security/authorizedKeys/daemon/changes.ts +16 -1
- package/security/authorizedKeys/daemon/daemon.ts +50 -40
- package/security/authorizedKeys/daemon/dist/changes.ts.cache +40 -0
- package/security/authorizedKeys/daemon/dist/git.ts.cache +121 -0
- package/security/authorizedKeys/daemon/dist/notify.ts.cache +39 -0
- package/security/authorizedKeys/daemon/dist/paths.ts.cache +29 -0
- package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +114 -0
- package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
- package/security/authorizedKeys/daemon/dist/sessions.ts.cache +147 -0
- package/security/authorizedKeys/daemon/dist/state.ts.cache +74 -0
- package/security/authorizedKeys/daemon/dist/trust.ts.cache +295 -0
- package/security/authorizedKeys/daemon/git.ts +3 -3
- package/security/authorizedKeys/daemon/notify.ts +8 -2
- package/security/authorizedKeys/daemon/repoFiles.ts +118 -0
- package/security/authorizedKeys/daemon/revocation.ts +0 -0
- package/security/authorizedKeys/daemon/rootKeys.ts +77 -10
- package/security/authorizedKeys/daemon/state.ts +25 -7
- package/security/authorizedKeys/daemon/trust.ts +37 -16
- package/security/authorizedKeys/daemon/userKeys.ts +5 -4
- package/security/authorizedKeys/dist/authorizedKeys.ts.cache +37 -3
- package/security/authorizedKeys/dist/revokeSource.ts.cache +17 -4
- package/security/authorizedKeys/dist/sources.ts.cache +52 -8
- package/security/authorizedKeys/dist/unrevoke.ts.cache +113 -17
- package/security/authorizedKeys/revokeSource.ts +12 -2
- package/security/authorizedKeys/secureSSH.ts +66 -12
- package/security/authorizedKeys/sources.ts +47 -5
- package/security/authorizedKeys/unrevoke.ts +176 -16
- package/security/helpers/dist/remoteSSH.ts.cache +19 -9
- package/security/machines/addMachine.ts +83 -0
- package/security/machines/dist/addMachine.ts.cache +88 -0
- package/security/machines/dist/identity.ts.cache +176 -0
- package/security/machines/dist/machines.ts.cache +322 -0
- package/security/machines/dist/trustClient.ts.cache +48 -0
- package/security/machines/dist/trustState.ts.cache +57 -0
- package/security/machines/identity.ts +199 -0
- package/security/machines/machines.ts +355 -0
- package/security/machines/trustClient.ts +51 -0
- package/security/machines/trustServer.ts +85 -0
- package/security/machines/trustState.ts +53 -0
- package/security/notifications/discord.ts +14 -5
- package/security/notifications/dist/discord.ts.cache +17 -8
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
-
//exports.sourceRepoPath = exports.sourceKeyPath = exports.sourceName = exports.REPOS_DIR = exports.
|
|
7
|
-
|
|
6
|
+
//exports.findSourceKey = exports.findKey = exports.sourceRepoPath = exports.legacySourceKeyPath = exports.sourceKeyPath = exports.sourceName = exports.keysDir = exports.REPOS_DIR = exports.LEGACY_REPO_KEYS_DIR = exports.KEYS_DIR_NAME = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
// PORTED CODE: nothing here is duplicated any more, the daemon imports it directly. Kept in one
|
|
11
|
+
// place because the daemon and the deploy have to agree on where a source's key and checkout live.
|
|
12
|
+
// The user's own folder, so this works the same on a machine that has no /etc. The daemon runs as
|
|
13
|
+
// root, so on a host this is root's home.
|
|
14
|
+
exports.KEYS_DIR_NAME = "authorized_keys";
|
|
15
|
+
// Where keys used to go. Still read, so a host set up before this keeps working, but nothing is
|
|
16
|
+
// written here any more.
|
|
17
|
+
exports.LEGACY_REPO_KEYS_DIR = "/etc/portsecure/repo-keys";
|
|
8
18
|
exports.REPOS_DIR = "/var/lib/portsecure/authorized-keys-repos";
|
|
19
|
+
function keysDir() {
|
|
20
|
+
return path_1.default.join(os_1.default.homedir(), exports.KEYS_DIR_NAME);
|
|
21
|
+
}
|
|
22
|
+
exports.keysDir = keysDir;
|
|
9
23
|
/** A repo url reduced to something usable as a file name. Derived rather than configured, so the
|
|
10
24
|
daemon and the deploy script always agree on where a source's key and checkout live. */
|
|
11
25
|
function sourceName(repoURL) {
|
|
@@ -13,12 +27,42 @@ function sourceName(repoURL) {
|
|
|
13
27
|
}
|
|
14
28
|
exports.sourceName = sourceName;
|
|
15
29
|
function sourceKeyPath(repoURL) {
|
|
16
|
-
return
|
|
30
|
+
return path_1.default.join(keysDir(), sourceName(repoURL));
|
|
17
31
|
}
|
|
18
32
|
exports.sourceKeyPath = sourceKeyPath;
|
|
33
|
+
function legacySourceKeyPath(repoURL) {
|
|
34
|
+
return `${exports.LEGACY_REPO_KEYS_DIR}/${sourceName(repoURL)}`;
|
|
35
|
+
}
|
|
36
|
+
exports.legacySourceKeyPath = legacySourceKeyPath;
|
|
19
37
|
function sourceRepoPath(repoURL) {
|
|
20
38
|
return `${exports.REPOS_DIR}/${sourceName(repoURL)}`;
|
|
21
39
|
}
|
|
22
40
|
exports.sourceRepoPath = sourceRepoPath;
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
async function pathExists(filePath) {
|
|
42
|
+
try {
|
|
43
|
+
await promises_1.default.access(filePath);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** The key for a source, wherever it happens to be. The user folder is where they go now, and the
|
|
51
|
+
old location is still read so a host that predates the move keeps working. */
|
|
52
|
+
async function findKey(config) {
|
|
53
|
+
let { current, legacy } = config;
|
|
54
|
+
if (await pathExists(current)) {
|
|
55
|
+
return current;
|
|
56
|
+
}
|
|
57
|
+
if (await pathExists(legacy)) {
|
|
58
|
+
return legacy;
|
|
59
|
+
}
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
exports.findKey = findKey;
|
|
63
|
+
async function findSourceKey(repoURL) {
|
|
64
|
+
return await findKey({ current: sourceKeyPath(repoURL), legacy: legacySourceKeyPath(repoURL) });
|
|
65
|
+
}
|
|
66
|
+
exports.findSourceKey = findSourceKey;
|
|
67
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNvdXJjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMkRBQTZCO0FBQzdCLDRDQUFvQjtBQUNwQixnREFBd0I7QUFFeEIsZ0dBQWdHO0FBQ2hHLG1HQUFtRztBQUVuRyxrR0FBa0c7QUFDbEcsMENBQTBDO0FBQzdCLFFBQUEsYUFBYSxHQUFHLGlCQUFpQixDQUFDO0FBQy9DLGdHQUFnRztBQUNoRyx5QkFBeUI7QUFDWixRQUFBLG9CQUFvQixHQUFHLDJCQUEyQixDQUFDO0FBQ25ELFFBQUEsU0FBUyxHQUFHLDJDQUEyQyxDQUFDO0FBRXJFLFNBQWdCLE9BQU87SUFDbkIsT0FBTyxjQUFJLENBQUMsSUFBSSxDQUFDLFlBQUUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxxQkFBYSxDQUFDLENBQUM7QUFDbEQsQ0FBQztBQUZELDBCQUVDO0FBRUQ7MkZBQzJGO0FBQzNGLFNBQWdCLFVBQVUsQ0FBQyxPQUFlO0lBQ3RDLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3hGLENBQUM7QUFGRCxnQ0FFQztBQUVELFNBQWdCLGFBQWEsQ0FBQyxPQUFlO0lBQ3pDLE9BQU8sY0FBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDO0FBRkQsc0NBRUM7QUFFRCxTQUFnQixtQkFBbUIsQ0FBQyxPQUFlO0lBQy9DLE9BQU8sR0FBRyw0QkFBb0IsSUFBSSxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztBQUM1RCxDQUFDO0FBRkQsa0RBRUM7QUFFRCxTQUFnQixjQUFjLENBQUMsT0FBZTtJQUMxQyxPQUFPLEdBQUcsaUJBQVMsSUFBSSxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztBQUNqRCxDQUFDO0FBRkQsd0NBRUM7QUFFRCxLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQ3RDLElBQUksQ0FBQztRQUNELE1BQU0sa0JBQUUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUM7SUFDaEIsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCxPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0FBQ0wsQ0FBQztBQUVEO2lGQUNpRjtBQUMxRSxLQUFLLFVBQVUsT0FBTyxDQUFDLE1BQTJDO0lBQ3JFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDO0lBQ2pDLElBQUksTUFBTSxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUM1QixPQUFPLE9BQU8sQ0FBQztJQUNuQixDQUFDO0lBQ0QsSUFBSSxNQUFNLFVBQVUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQzNCLE9BQU8sTUFBTSxDQUFDO0lBQ2xCLENBQUM7SUFDRCxPQUFPLEVBQUUsQ0FBQztBQUNkLENBQUM7QUFURCwwQkFTQztBQUVNLEtBQUssVUFBVSxhQUFhLENBQUMsT0FBZTtJQUMvQyxPQUFPLE1BQU0sT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsRUFBRSxNQUFNLEVBQUUsbUJBQW1CLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3BHLENBQUM7QUFGRCxzQ0FFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5cbi8vIFBPUlRFRCBDT0RFOiBub3RoaW5nIGhlcmUgaXMgZHVwbGljYXRlZCBhbnkgbW9yZSwgdGhlIGRhZW1vbiBpbXBvcnRzIGl0IGRpcmVjdGx5LiBLZXB0IGluIG9uZVxuLy8gcGxhY2UgYmVjYXVzZSB0aGUgZGFlbW9uIGFuZCB0aGUgZGVwbG95IGhhdmUgdG8gYWdyZWUgb24gd2hlcmUgYSBzb3VyY2UncyBrZXkgYW5kIGNoZWNrb3V0IGxpdmUuXG5cbi8vIFRoZSB1c2VyJ3Mgb3duIGZvbGRlciwgc28gdGhpcyB3b3JrcyB0aGUgc2FtZSBvbiBhIG1hY2hpbmUgdGhhdCBoYXMgbm8gL2V0Yy4gVGhlIGRhZW1vbiBydW5zIGFzXG4vLyByb290LCBzbyBvbiBhIGhvc3QgdGhpcyBpcyByb290J3MgaG9tZS5cbmV4cG9ydCBjb25zdCBLRVlTX0RJUl9OQU1FID0gXCJhdXRob3JpemVkX2tleXNcIjtcbi8vIFdoZXJlIGtleXMgdXNlZCB0byBnby4gU3RpbGwgcmVhZCwgc28gYSBob3N0IHNldCB1cCBiZWZvcmUgdGhpcyBrZWVwcyB3b3JraW5nLCBidXQgbm90aGluZyBpc1xuLy8gd3JpdHRlbiBoZXJlIGFueSBtb3JlLlxuZXhwb3J0IGNvbnN0IExFR0FDWV9SRVBPX0tFWVNfRElSID0gXCIvZXRjL3BvcnRzZWN1cmUvcmVwby1rZXlzXCI7XG5leHBvcnQgY29uc3QgUkVQT1NfRElSID0gXCIvdmFyL2xpYi9wb3J0c2VjdXJlL2F1dGhvcml6ZWQta2V5cy1yZXBvc1wiO1xuXG5leHBvcnQgZnVuY3Rpb24ga2V5c0RpcigpIHtcbiAgICByZXR1cm4gcGF0aC5qb2luKG9zLmhvbWVkaXIoKSwgS0VZU19ESVJfTkFNRSk7XG59XG5cbi8qKiBBIHJlcG8gdXJsIHJlZHVjZWQgdG8gc29tZXRoaW5nIHVzYWJsZSBhcyBhIGZpbGUgbmFtZS4gRGVyaXZlZCByYXRoZXIgdGhhbiBjb25maWd1cmVkLCBzbyB0aGVcbiAgICBkYWVtb24gYW5kIHRoZSBkZXBsb3kgc2NyaXB0IGFsd2F5cyBhZ3JlZSBvbiB3aGVyZSBhIHNvdXJjZSdzIGtleSBhbmQgY2hlY2tvdXQgbGl2ZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzb3VyY2VOYW1lKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiByZXBvVVJMLnJlcGxhY2UoL1teQS1aYS16MC05XSsvZywgXCItXCIpLnJlcGxhY2UoL14tK3wtKyQvZywgXCJcIikudG9Mb3dlckNhc2UoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNvdXJjZUtleVBhdGgocmVwb1VSTDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHBhdGguam9pbihrZXlzRGlyKCksIHNvdXJjZU5hbWUocmVwb1VSTCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbGVnYWN5U291cmNlS2V5UGF0aChyZXBvVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYCR7TEVHQUNZX1JFUE9fS0VZU19ESVJ9LyR7c291cmNlTmFtZShyZXBvVVJMKX1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc291cmNlUmVwb1BhdGgocmVwb1VSTDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGAke1JFUE9TX0RJUn0vJHtzb3VyY2VOYW1lKHJlcG9VUkwpfWA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHBhdGhFeGlzdHMoZmlsZVBhdGg6IHN0cmluZykge1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmFjY2VzcyhmaWxlUGF0aCk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuLyoqIFRoZSBrZXkgZm9yIGEgc291cmNlLCB3aGVyZXZlciBpdCBoYXBwZW5zIHRvIGJlLiBUaGUgdXNlciBmb2xkZXIgaXMgd2hlcmUgdGhleSBnbyBub3csIGFuZCB0aGVcbiAgICBvbGQgbG9jYXRpb24gaXMgc3RpbGwgcmVhZCBzbyBhIGhvc3QgdGhhdCBwcmVkYXRlcyB0aGUgbW92ZSBrZWVwcyB3b3JraW5nLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbmRLZXkoY29uZmlnOiB7IGN1cnJlbnQ6IHN0cmluZzsgbGVnYWN5OiBzdHJpbmcgfSkge1xuICAgIGxldCB7IGN1cnJlbnQsIGxlZ2FjeSB9ID0gY29uZmlnO1xuICAgIGlmIChhd2FpdCBwYXRoRXhpc3RzKGN1cnJlbnQpKSB7XG4gICAgICAgIHJldHVybiBjdXJyZW50O1xuICAgIH1cbiAgICBpZiAoYXdhaXQgcGF0aEV4aXN0cyhsZWdhY3kpKSB7XG4gICAgICAgIHJldHVybiBsZWdhY3k7XG4gICAgfVxuICAgIHJldHVybiBcIlwiO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluZFNvdXJjZUtleShyZXBvVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYXdhaXQgZmluZEtleSh7IGN1cnJlbnQ6IHNvdXJjZUtleVBhdGgocmVwb1VSTCksIGxlZ2FjeTogbGVnYWN5U291cmNlS2V5UGF0aChyZXBvVVJMKSB9KTtcbn1cbiJdfQ==
|
|
68
|
+
/* _JS_SOURCE_HASH = "d66a91022052069dc8a7c111312ca09502fb4974baa795ec49d4d0ebed0668bf"; */
|
|
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
-
//exports.main = exports.revokedKeysInRepo = exports.readRemoteRevocations = void 0;
|
|
6
|
+
//exports.main = exports.revokedKeysInRepo = exports.readRemoteRevocations = exports.revocationIP = void 0;
|
|
7
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const runPromise_1 = require("socket-function/src/runPromise");
|
|
11
11
|
const authorizedKeys_1 = require("./authorizedKeys");
|
|
12
12
|
const revokeSource_1 = require("./revokeSource");
|
|
13
|
+
const sources_1 = require("./sources");
|
|
13
14
|
const signFiles_1 = require("../signedFiles/signFiles");
|
|
14
15
|
const authorizedKeys_2 = require("./authorizedKeys");
|
|
15
16
|
const paths_1 = require("../helpers/paths");
|
|
@@ -26,17 +27,31 @@ passes. It signs the result, and with ${GIT_KEYWORD} it commits and pushes it to
|
|
|
26
27
|
|
|
27
28
|
Keys that were revoked should normally be deleted from the repo instead. Unrevoking only matters
|
|
28
29
|
for a key you still want.`;
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
/** Older revocation files carry the address under attempt only. */
|
|
31
|
+
function revocationIP(revocation) {
|
|
32
|
+
var _a;
|
|
33
|
+
return revocation.ip || ((_a = revocation.attempt) === null || _a === void 0 ? void 0 : _a.ip) || "";
|
|
34
|
+
}
|
|
35
|
+
exports.revocationIP = revocationIP;
|
|
36
|
+
/** Every revocation the revoke repo lists. Cloned read only into a temp directory.
|
|
37
|
+
|
|
38
|
+
A person running this has their own access to the repo, so their credentials are what is used.
|
|
39
|
+
On a machine that is already a portsecure host there may be no such credentials, but the source
|
|
40
|
+
deploy key is right there, and the key derived from it is one that repo definitely accepts. */
|
|
32
41
|
async function readRemoteRevocations(config) {
|
|
33
42
|
let { sourceURL } = config;
|
|
34
43
|
let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "unrevoke-"));
|
|
35
44
|
let repoPath = path_1.default.join(temporaryDirectory, "repo");
|
|
36
|
-
let
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
let cloneArgs = ["clone", "--depth", "1", (0, revokeSource_1.revokeRepoURL)(sourceURL), repoPath];
|
|
46
|
+
let sourceKey = await (0, sources_1.findSourceKey)(sourceURL);
|
|
47
|
+
if (sourceKey) {
|
|
48
|
+
let derived = (0, revokeSource_1.deriveRevokeKey)(await promises_1.default.readFile(sourceKey, "utf8"));
|
|
49
|
+
let derivedKeyPath = path_1.default.join(temporaryDirectory, "key");
|
|
50
|
+
await promises_1.default.writeFile(derivedKeyPath, derived.privateKeyFile, { mode: 0o600 });
|
|
51
|
+
let sshCommand = `ssh -i ${derivedKeyPath} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new`;
|
|
52
|
+
cloneArgs = ["-c", `core.sshCommand=${sshCommand}`, ...cloneArgs];
|
|
53
|
+
}
|
|
54
|
+
let clone = await (0, spawn_1.spawnPromise)({ command: "git", args: cloneArgs });
|
|
40
55
|
if (clone.status !== 0) {
|
|
41
56
|
await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
|
|
42
57
|
throw new Error(`Expected to be able to read ${(0, revokeSource_1.revokeRepoURL)(sourceURL)}.\n`
|
|
@@ -71,6 +86,10 @@ async function revokedKeysInRepo(config) {
|
|
|
71
86
|
continue;
|
|
72
87
|
}
|
|
73
88
|
let parsed = JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8"));
|
|
89
|
+
for (let allowed of parsed.allowed || []) {
|
|
90
|
+
unrevoked.add(`${allowed.fingerprint} ${allowed.ip}`);
|
|
91
|
+
}
|
|
92
|
+
// Named ids, for the unrevokes written before this was about pairs.
|
|
74
93
|
for (let revocationId of parsed.revocationIds || []) {
|
|
75
94
|
unrevoked.add(revocationId);
|
|
76
95
|
}
|
|
@@ -79,13 +98,47 @@ async function revokedKeysInRepo(config) {
|
|
|
79
98
|
catch (e) {
|
|
80
99
|
// Nothing has been unrevoked.
|
|
81
100
|
}
|
|
82
|
-
let stillRevoked = revocations.filter(revocation => !unrevoked.has(revocation.revocationId)
|
|
101
|
+
let stillRevoked = revocations.filter(revocation => !unrevoked.has(revocation.revocationId)
|
|
102
|
+
&& !unrevoked.has(`${revocation.fingerprint} ${revocationIP(revocation)}`));
|
|
83
103
|
let keys = (0, authorizedKeys_1.normalizeKeys)(await promises_1.default.readFile(path_1.default.join(repoPath, "authorized_keys"), "utf8").catch(() => ""));
|
|
84
104
|
return stillRevoked
|
|
85
105
|
.filter(revocation => keys.some(key => (0, authorizedKeys_1.keyFingerprint)(key) === revocation.fingerprint))
|
|
86
106
|
.map(revocation => ({ revocation, key: keys.find(key => (0, authorizedKeys_1.keyFingerprint)(key) === revocation.fingerprint) || "" }));
|
|
87
107
|
}
|
|
88
108
|
exports.revokedKeysInRepo = revokedKeysInRepo;
|
|
109
|
+
/** Writes the addresses into the from= list of each key in the repo's authorized_keys, and says
|
|
110
|
+
which ones were actually new. Only the lines being changed are touched, so comments, ordering
|
|
111
|
+
and any key not involved come out of this byte for byte the same. */
|
|
112
|
+
async function allowAddressesInRepo(config) {
|
|
113
|
+
let { repoPath, addressesByFingerprint } = config;
|
|
114
|
+
let added = new Map();
|
|
115
|
+
let filePath = path_1.default.join(repoPath, "authorized_keys");
|
|
116
|
+
let contents;
|
|
117
|
+
try {
|
|
118
|
+
contents = await promises_1.default.readFile(filePath, "utf8");
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
// A repo holding its keys as separate .pub files. Nothing to edit in one place, so the
|
|
122
|
+
// caller's listing is all whoever ran this gets.
|
|
123
|
+
console.log(`No authorized_keys in ${repoPath}, so the addresses were not added to any key.`);
|
|
124
|
+
return added;
|
|
125
|
+
}
|
|
126
|
+
let lines = contents.split("\n");
|
|
127
|
+
for (let index = 0; index < lines.length; index++) {
|
|
128
|
+
let fingerprint = (0, authorizedKeys_1.keyFingerprint)(lines[index]);
|
|
129
|
+
let addresses = fingerprint && addressesByFingerprint.get(fingerprint);
|
|
130
|
+
if (!addresses || !addresses.length) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
let result = (0, authorizedKeys_1.allowAddresses)(lines[index], addresses);
|
|
134
|
+
lines[index] = result.keyLine;
|
|
135
|
+
added.set(fingerprint, [...(added.get(fingerprint) || []), ...result.added]);
|
|
136
|
+
}
|
|
137
|
+
if ([...added.values()].some(list => list.length)) {
|
|
138
|
+
await promises_1.default.writeFile(filePath, lines.join("\n"));
|
|
139
|
+
}
|
|
140
|
+
return added;
|
|
141
|
+
}
|
|
89
142
|
/** The repo named, or the one we are standing in. Checked the same way securessh checks it: a git
|
|
90
143
|
repo that actually holds keys and has an origin to clone from. */
|
|
91
144
|
async function resolveKeysRepo(named) {
|
|
@@ -112,7 +165,6 @@ async function resolveKeysRepo(named) {
|
|
|
112
165
|
return { repoPath, originURL };
|
|
113
166
|
}
|
|
114
167
|
async function main() {
|
|
115
|
-
var _a;
|
|
116
168
|
let argv = process.argv.slice(2);
|
|
117
169
|
let pushToGit = argv.includes(GIT_KEYWORD);
|
|
118
170
|
let positional = argv.filter(arg => arg !== GIT_KEYWORD);
|
|
@@ -132,21 +184,65 @@ async function main() {
|
|
|
132
184
|
await promises_1.default.writeFile(path_1.default.join(directory, `${unrevokeId}.json`), JSON.stringify({
|
|
133
185
|
unrevokeId,
|
|
134
186
|
createdAt: new Date().toISOString(),
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
187
|
+
// What this file says: each of these keys is allowed from this address. Pairs, not keys,
|
|
188
|
+
// and not a blanket "allow everything again" - a key forgiven here for one address is
|
|
189
|
+
// still revoked the moment it is used from another.
|
|
190
|
+
allowed: revocations.map(revocation => ({
|
|
191
|
+
fingerprint: revocation.fingerprint,
|
|
192
|
+
ip: revocationIP(revocation),
|
|
193
|
+
})),
|
|
138
194
|
revocations: revocations.map(revocation => ({
|
|
139
195
|
revocationId: revocation.revocationId,
|
|
140
196
|
fingerprint: revocation.fingerprint,
|
|
197
|
+
ip: revocationIP(revocation),
|
|
141
198
|
revokedAt: revocation.revokedAt,
|
|
142
199
|
revokedBy: revocation.revokedBy,
|
|
143
200
|
attempt: revocation.attempt,
|
|
144
201
|
})),
|
|
145
202
|
}, undefined, 4) + "\n");
|
|
146
|
-
|
|
203
|
+
// Named the way the notifications name them. A fingerprint is not in authorized_keys and
|
|
204
|
+
// cannot be worked out by eye, so on its own it tells whoever is reading this nothing about
|
|
205
|
+
// which of their keys is being talked about.
|
|
206
|
+
let keysByFingerprint = new Map();
|
|
207
|
+
for (let key of await (0, authorizedKeys_2.readRepoKeys)(repoPath).catch(() => [])) {
|
|
208
|
+
keysByFingerprint.set((0, authorizedKeys_1.keyFingerprint)(key), key);
|
|
209
|
+
}
|
|
210
|
+
let describeKey = (revocation) => {
|
|
211
|
+
let key = keysByFingerprint.get(revocation.fingerprint) || revocation.key || "";
|
|
212
|
+
return key && (0, authorizedKeys_1.summarizeKey)(key) || `a key no longer in this repo (${revocation.fingerprint})`;
|
|
213
|
+
};
|
|
214
|
+
let addressesByFingerprint = new Map();
|
|
147
215
|
for (let revocation of revocations) {
|
|
148
|
-
|
|
216
|
+
let ip = revocationIP(revocation);
|
|
217
|
+
let existing = addressesByFingerprint.get(revocation.fingerprint) || [];
|
|
218
|
+
if (ip && !existing.includes(ip)) {
|
|
219
|
+
existing.push(ip);
|
|
220
|
+
}
|
|
221
|
+
addressesByFingerprint.set(revocation.fingerprint, existing);
|
|
222
|
+
}
|
|
223
|
+
// Cancelling the revocation on its own would leave sshd refusing that address and the machines
|
|
224
|
+
// revoking the key all over again, so the addresses go into the key's from= here. We are in
|
|
225
|
+
// the repo that owns authorized_keys, so there is nothing to hand back to whoever ran this.
|
|
226
|
+
let added = await allowAddressesInRepo({ repoPath, addressesByFingerprint });
|
|
227
|
+
console.log("");
|
|
228
|
+
for (let [fingerprint, addresses] of addressesByFingerprint) {
|
|
229
|
+
let revocation = revocations.find(entry => entry.fingerprint === fingerprint);
|
|
230
|
+
console.log(`Unfreezing ${describeKey(revocation)}`);
|
|
231
|
+
console.log(`Allowing IP ${addresses.join(", ") || "(no address recorded)"}`);
|
|
232
|
+
let addedHere = added.get(fingerprint) || [];
|
|
233
|
+
if (addedHere.length) {
|
|
234
|
+
console.log(` added to that key's from= list in authorized_keys: ${addedHere.join(", ")}`);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
console.log(` that key's from= list already allowed ${addresses.join(", ") ? "them" : "everything"}`);
|
|
238
|
+
}
|
|
239
|
+
for (let entry of revocations.filter(entry => entry.fingerprint === fingerprint)) {
|
|
240
|
+
console.log(` frozen by ${entry.revokedBy || "?"} ${entry.revokedAt || "at an unrecorded time"}`);
|
|
241
|
+
}
|
|
242
|
+
console.log("");
|
|
149
243
|
}
|
|
244
|
+
console.log(`Every other address stays frozen, and one of these keys used from anywhere else`);
|
|
245
|
+
console.log(`is frozen again.`);
|
|
150
246
|
// Signed here rather than left as a step to remember. An unrevoke nobody signed does nothing at
|
|
151
247
|
// all, and the repo would sit there looking done while every machine ignored it.
|
|
152
248
|
await (0, signFiles_1.signRepo)({ repoPath });
|
|
@@ -163,5 +259,5 @@ async function main() {
|
|
|
163
259
|
console.log(`keys work again.`);
|
|
164
260
|
}
|
|
165
261
|
exports.main = main;
|
|
166
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5yZXZva2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ1bnJldm9rZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QiwrREFBNEQ7QUFDNUQscURBQWlFO0FBQ2pFLGlEQUErQztBQUMvQyx3REFBb0Q7QUFDcEQscURBQWdEO0FBQ2hELDRDQUE4QztBQUM5Qyw0Q0FBZ0Q7QUFFaEQsTUFBTSxhQUFhLEdBQUcsV0FBVyxDQUFDO0FBQ2xDLE1BQU0sZUFBZSxHQUFHLGFBQWEsQ0FBQztBQUN0QyxNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUM7QUFDMUIsTUFBTSxjQUFjLEdBQUcsZUFBZSxDQUFDO0FBQ3ZDLE1BQU0sS0FBSyxHQUFHLHFDQUFxQyxXQUFXOzs7O3dDQUl0QixXQUFXOzs7MEJBR3pCLENBQUM7QUFXM0I7O3FEQUVxRDtBQUM5QyxLQUFLLFVBQVUscUJBQXFCLENBQUMsTUFBNkI7SUFDckUsSUFBSSxFQUFFLFNBQVMsRUFBRSxHQUFHLE1BQU0sQ0FBQztJQUMzQixJQUFJLGtCQUFrQixHQUFHLE1BQU0sa0JBQUUsQ0FBQyxPQUFPLENBQUMsY0FBSSxDQUFDLElBQUksQ0FBQyxZQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQztJQUMvRSxJQUFJLFFBQVEsR0FBRyxjQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3JELElBQUksS0FBSyxHQUFHLE1BQU0sSUFBQSxvQkFBWSxFQUFDO1FBQzNCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsSUFBSSxFQUFFLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsSUFBQSw0QkFBYSxFQUFDLFNBQVMsQ0FBQyxFQUFFLFFBQVEsQ0FBQztLQUN0RSxDQUFDLENBQUM7SUFDSCxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFF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Q0FBQztRQUMvRixPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDLENBQUM7UUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQ0FBc0MsY0FBYyxxQkFBcUIsQ0FBQyxDQUFDO1FBQ3ZGLE9BQU87SUFDWCxDQUFDO0lBQ0QsTUFBTSxJQUFBLHVCQUFVLEVBQUMsWUFBWSxFQUFFLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDbEQsTUFBTSxJQUFBLHVCQUFVLEVBQUMsa0JBQWtCLGNBQWMsR0FBRyxFQUFFLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDekUsTUFBTSxJQUFBLHVCQUFVLEVBQUMsVUFBVSxFQUFFLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDaEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpRkFBaUYsQ0FBQyxDQUFDO0lBQy9GLE9BQU8sQ0FBQyxHQUFHLENBQUMsa0JBQWtCLENBQUMsQ0FBQztBQUNwQyxDQUFDO0FBdERELG9CQXNEQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBydW5Qcm9taXNlIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvcnVuUHJvbWlzZVwiO1xuaW1wb3J0IHsga2V5RmluZ2VycHJpbnQsIG5vcm1hbGl6ZUtleXMgfSBmcm9tIFwiLi9hdXRob3JpemVkS2V5c1wiO1xuaW1wb3J0IHsgcmV2b2tlUmVwb1VSTCB9IGZyb20gXCIuL3Jldm9rZVNvdXJjZVwiO1xuaW1wb3J0IHsgc2lnblJlcG8gfSBmcm9tIFwiLi4vc2lnbmVkRmlsZXMvc2lnbkZpbGVzXCI7XG5pbXBvcnQgeyByZWFkUmVwb0tleXMgfSBmcm9tIFwiLi9hdXRob3JpemVkS2V5c1wiO1xuaW1wb3J0IHsgZXhwYW5kSG9tZSB9IGZyb20gXCIuLi9oZWxwZXJzL3BhdGhzXCI7XG5pbXBvcnQgeyBzcGF3blByb21pc2UgfSBmcm9tIFwiLi4vaGVscGVycy9zcGF3blwiO1xuXG5jb25zdCBVTlJFVk9LRVNfRElSID0gXCJ1bnJldm9rZWRcIjtcbmNvbnN0IFJFVk9DQVRJT05TX0RJUiA9IFwicmV2b2NhdGlvbnNcIjtcbmNvbnN0IEdJVF9LRVlXT1JEID0gXCJnaXRcIjtcbmNvbnN0IENPTU1JVF9NRVNTQUdFID0gXCJ1bnJldm9rZSBrZXlzXCI7XG5jb25zdCBVU0FHRSA9IGBVc2FnZTogeWFybiB1bnJldm9rZSBba2V5cy1yZXBvXSBbJHtHSVRfS0VZV09SRH1dXG5cblJ1biB0aGlzIGluIGEga2V5cyByZXBvLCBvciBuYW1lIG9uZS4gSXQgcmVhZHMgdGhhdCByZXBvJ3MgcmV2b2tlIHJlcG8gYW5kIHdyaXRlcyBvbmUgdW5yZXZva2UgZmlsZVxubmFtaW5nIGV2ZXJ5IHJldm9jYXRpb24gaW4gaXQsIHNvIHRoZSBrZXlzIGFyZSBhY2NlcHRlZCBhZ2FpbiBvbmNlIGVhY2ggbWFjaGluZSdzIGhvdXIgbG9uZyB3YWl0XG5wYXNzZXMuIEl0IHNpZ25zIHRoZSByZXN1bHQsIGFuZCB3aXRoICR7R0lUX0tFWVdPUkR9IGl0IGNvbW1pdHMgYW5kIHB1c2hlcyBpdCB0b28uXG5cbktleXMgdGhhdCB3ZXJlIHJldm9rZWQgc2hvdWxkIG5vcm1hbGx5IGJlIGRlbGV0ZWQgZnJvbSB0aGUgcmVwbyBpbnN0ZWFkLiBVbnJldm9raW5nIG9ubHkgbWF0dGVyc1xuZm9yIGEga2V5IHlvdSBzdGlsbCB3YW50LmA7XG5cbmV4cG9ydCB0eXBlIFJldm9jYXRpb24gPSB7XG4gICAgcmV2b2NhdGlvbklkOiBzdHJpbmc7XG4gICAgZmluZ2VycHJpbnQ6IHN0cmluZztcbiAgICBrZXk/OiBzdHJpbmc7XG4gICAgcmV2b2tlZEF0Pzogc3RyaW5nO1xuICAgIHJldm9rZWRCeT86IHN0cmluZztcbiAgICBhdHRlbXB0PzogeyBpcD86IHN0cmluZzsgdXNlcj86IHN0cmluZzsgcmVxdWlyZWQ/OiBzdHJpbmcgfTtcbn07XG5cbi8qKiBFdmVyeSByZXZvY2F0aW9uIHRoZSByZXZva2UgcmVwbyBsaXN0cy4gQ2xvbmVkIHJlYWQgb25seSBpbnRvIGEgdGVtcCBkaXJlY3RvcnksIHdpdGggd2hhdGV2ZXJcbiAgICBjcmVkZW50aWFscyB0aGlzIG1hY2hpbmUgYWxyZWFkeSBoYXMgLSB0aGUgZGVyaXZlZCBkZXBsb3kga2V5IGlzIGZvciBzZXJ2ZXJzLCBhbmQgYSBwZXJzb25cbiAgICBydW5uaW5nIHRoaXMgaGFzIHRoZWlyIG93biBhY2Nlc3MgdG8gdGhlIHJlcG8uICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFJlbW90ZVJldm9jYXRpb25zKGNvbmZpZzogeyBzb3VyY2VVUkw6IHN0cmluZyB9KSB7XG4gICAgbGV0IHsgc291cmNlVVJMIH0gPSBjb25maWc7XG4gICAgbGV0IHRlbXBvcmFyeURpcmVjdG9yeSA9IGF3YWl0IGZzLm1rZHRlbXAocGF0aC5qb2luKG9zLnRtcGRpcigpLCBcInVucmV2b2tlLVwiKSk7XG4gICAgbGV0IHJlcG9QYXRoID0gcGF0aC5qb2luKHRlbXBvcmFyeURpcmVjdG9yeSwgXCJyZXBvXCIpO1xuICAgIGxldCBjbG9uZSA9IGF3YWl0IHNwYXduUHJvbWlzZSh7XG4gICAgICAgIGNvbW1hbmQ6IFwiZ2l0XCIsXG4gICAgICAgIGFyZ3M6IFtcImNsb25lXCIsIFwiLS1kZXB0aFwiLCBcIjFcIiwgcmV2b2tlUmVwb1VSTChzb3VyY2VVUkwpLCByZXBvUGF0aF0sXG4gICAgfSk7XG4gICAgaWYgKGNsb25lLnN0YXR1cyAhPT0gMCkge1xuICAgICAgICBhd2FpdCBmcy5ybSh0ZW1wb3JhcnlEaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIHRvIGJlIGFibGUgdG8gcmVhZCAke3Jldm9rZVJlcG9VUkwoc291cmNlVVJMKX0uXFxuYFxuICAgICAgICAgICAgKyBgJHsoY2xvbmUuc3Rkb3V0ICsgY2xvbmUuc3RkZXJyKS50cmltKCl9YFxuICAgICAgICApO1xuICAgIH1cbiAgICBsZXQgcmV2b2NhdGlvbnM6IFJldm9jYXRpb25bXSA9IFtdO1xuICAgIGxldCBkaXJlY3RvcnkgPSBwYXRoLmpvaW4ocmVwb1BhdGgsIFJFVk9DQVRJT05TX0RJUik7XG4gICAgdHJ5IHtcbiAgICAgICAgZm9yIChsZXQgbmFtZSBvZiAoYXdhaXQgZnMucmVhZGRpcihkaXJlY3RvcnkpKS5zb3J0KCkpIHtcbiAgICAgICAgICAgIGlmIChuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgICAgICByZXZvY2F0aW9ucy5wdXNoKEpTT04ucGFyc2UoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKGRpcmVjdG9yeSwgbmFtZSksIFwidXRmOFwiKSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAvLyBObyByZXZvY2F0aW9ucyBkaXJlY3RvcnkgYXQgYWxsIGp1c3QgbWVhbnMgbm90aGluZyBoYXMgZXZlciBiZWVuIHJldm9rZWQuXG4gICAgfVxuICAgIGF3YWl0IGZzLnJtKHRlbXBvcmFyeURpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgIHJldHVybiByZXZvY2F0aW9ucztcbn1cblxuLyoqIFdoaWNoIGtleXMgaW4gYSByZXBvIGFyZSByZXZva2VkLiBXaGF0IHNpZ25maWxlcyBhbmQgc2VjdXJlc3NoIHJlZnVzZSBvdmVyLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJldm9rZWRLZXlzSW5SZXBvKGNvbmZpZzogeyByZXBvUGF0aDogc3RyaW5nOyBzb3VyY2VVUkw6IHN0cmluZyB9KSB7XG4gICAgbGV0IHsgcmVwb1BhdGgsIHNvdXJjZVVSTCB9ID0gY29uZmlnO1xuICAgIGxldCByZXZvY2F0aW9ucyA9IGF3YWl0IHJlYWRSZW1vdGVSZXZvY2F0aW9ucyh7IHNvdXJjZVVSTCB9KTtcbiAgICBsZXQgcmV2b2tlZEZpbmdlcnByaW50cyA9IG5ldyBTZXQocmV2b2NhdGlvbnMubWFwKHJldm9jYXRpb24gPT4gcmV2b2NhdGlvbi5maW5nZXJwcmludCkpO1xuICAgIGxldCB1bnJldm9rZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICB0cnkge1xuICAgICAgICBsZXQgZGlyZWN0b3J5ID0gcGF0aC5qb2luKHJlcG9QYXRoLCBVTlJFVk9LRVNfRElSKTtcbiAgICAgICAgZm9yIChsZXQgbmFtZSBvZiBhd2FpdCBmcy5yZWFkZGlyKGRpcmVjdG9yeSkpIHtcbiAgICAgICAgICAgIGlmICghbmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gSlNPTi5wYXJzZShhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBuYW1lKSwgXCJ1dGY4XCIpKTtcbiAgICAgICAgICAgIGZvciAobGV0IHJldm9jYXRpb25JZCBvZiBwYXJzZWQucmV2b2NhdGlvbklkcyB8fCBbXSkge1xuICAgICAgICAgICAgICAgIHVucmV2b2tlZC5hZGQocmV2b2NhdGlvbklkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gTm90aGluZyBoYXMgYmVlbiB1bnJldm9rZWQuXG4gICAgfVxuICAgIGxldCBzdGlsbFJldm9rZWQgPSByZXZvY2F0aW9ucy5maWx0ZXIocmV2b2NhdGlvbiA9PiAhdW5yZXZva2VkLmhhcyhyZXZvY2F0aW9uLnJldm9jYXRpb25JZCkpO1xuICAgIGxldCBrZXlzID0gbm9ybWFsaXplS2V5cyhhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4ocmVwb1BhdGgsIFwiYXV0aG9yaXplZF9rZXlzXCIpLCBcInV0ZjhcIikuY2F0Y2goKCkgPT4gXCJcIikpO1xuICAgIHJldHVybiBzdGlsbFJldm9rZWRcbiAgICAgICAgLmZpbHRlcihyZXZvY2F0aW9uID0+IGtleXMuc29tZShrZXkgPT4ga2V5RmluZ2VycHJpbnQoa2V5KSA9PT0gcmV2b2NhdGlvbi5maW5nZXJwcmludCkpXG4gICAgICAgIC5tYXAocmV2b2NhdGlvbiA9PiAoeyByZXZvY2F0aW9uLCBrZXk6IGtleXMuZmluZChrZXkgPT4ga2V5RmluZ2VycHJpbnQoa2V5KSA9PT0gcmV2b2NhdGlvbi5maW5nZXJwcmludCkgfHwgXCJcIiB9KSk7XG59XG5cbi8qKiBUaGUgcmVwbyBuYW1lZCwgb3IgdGhlIG9uZSB3ZSBhcmUgc3RhbmRpbmcgaW4uIENoZWNrZWQgdGhlIHNhbWUgd2F5IHNlY3VyZXNzaCBjaGVja3MgaXQ6IGEgZ2l0XG4gICAgcmVwbyB0aGF0IGFjdHVhbGx5IGhvbGRzIGtleXMgYW5kIGhhcyBhbiBvcmlnaW4gdG8gY2xvbmUgZnJvbS4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVLZXlzUmVwbyhuYW1lZDogc3RyaW5nIHwgdW5kZWZpbmVkKSB7XG4gICAgbGV0IGN3ZCA9IG5hbWVkICYmIGV4cGFuZEhvbWUobmFtZWQpIHx8IHVuZGVmaW5lZDtcbiAgICAvLyBzcGF3blByb21pc2UgcmF0aGVyIHRoYW4gcnVuUHJvbWlzZSwgYmVjYXVzZSB0aGVzZSB0d28gYXJlIHJlYWQgZm9yIHRoZWlyIHZhbHVlLiBydW5Qcm9taXNlXG4gICAgLy8gcmV0dXJucyBzdGRvdXQgYW5kIHN0ZGVyciBqb2luZWQsIHNvIG9uZSBnaXQgd2FybmluZyBlbmRzIHVwIGdsdWVkIHRvIHRoZSBmcm9udCBvZiB0aGUgcGF0aC5cbiAgICBsZXQgdG9wTGV2ZWwgPSBhd2FpdCBzcGF3blByb21pc2UoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBbXCJyZXYtcGFyc2VcIiwgXCItLXNob3ctdG9wbGV2ZWxcIl0sIGN3ZCB9KTtcbiAgICBsZXQgcmVwb1BhdGggPSB0b3BMZXZlbC5zdGRvdXQudHJpbSgpO1xuICAgIGlmICh0b3BMZXZlbC5zdGF0dXMgIT09IDAgfHwgIXJlcG9QYXRoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBFeHBlY3RlZCAke25hbWVkIHx8IFwidGhlIGN1cnJlbnQgZGlyZWN0b3J5XCJ9IHRvIGJlIGluc2lkZSBhIGdpdCByZXBvLCBpdCBpcyBub3QuXFxuYFxuICAgICAgICAgICAgKyBgJHsodG9wTGV2ZWwuc3Rkb3V0ICsgdG9wTGV2ZWwuc3RkZXJyKS50cmltKCl9XFxuJHtVU0FHRX1gXG4gICAgICAgICk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHJlYWRSZXBvS2V5cyhyZXBvUGF0aCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7cmVwb1BhdGh9IHRvIGJlIGEga2V5cyByZXBvLCBpdCBob2xkcyBubyBrZXlzLlxcbiR7ZX1cXG4ke1VTQUdFfWApO1xuICAgIH1cbiAgICBsZXQgb3JpZ2luID0gYXdhaXQgc3Bhd25Qcm9taXNlKHsgY29tbWFuZDogXCJnaXRcIiwgYXJnczogW1wicmVtb3RlXCIsIFwiZ2V0LXVybFwiLCBcIm9yaWdpblwiXSwgY3dkOiByZXBvUGF0aCB9KTtcbiAgICBsZXQgb3JpZ2luVVJMID0gb3JpZ2luLnN0ZG91dC50cmltKCk7XG4gICAgaWYgKG9yaWdpbi5zdGF0dXMgIT09IDAgfHwgIW9yaWdpblVSTCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7cmVwb1BhdGh9IHRvIGhhdmUgYW4gb3JpZ2luIHJlbW90ZSwgaXQgaGFzIG5vbmUuXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcmVwb1BhdGgsIG9yaWdpblVSTCB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFpbigpIHtcbiAgICBsZXQgYXJndiA9IHByb2Nlc3MuYXJndi5zbGljZSgyKTtcbiAgICBsZXQgcHVzaFRvR2l0ID0gYXJndi5pbmNsdWRlcyhHSVRfS0VZV09SRCk7XG4gICAgbGV0IHBvc2l0aW9uYWwgPSBhcmd2LmZpbHRlcihhcmcgPT4gYXJnICE9PSBHSVRfS0VZV09SRCk7XG4gICAgaWYgKHBvc2l0aW9uYWwubGVuZ3RoID4gMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGF0IG1vc3QgYSBrZXlzIHJlcG8sIHdhcyAke3Bvc2l0aW9uYWwubGVuZ3RofSBhcmd1bWVudChzKVxcbiR7VVNBR0V9YCk7XG4gICAgfVxuICAgIGxldCB7IHJlcG9QYXRoLCBvcmlnaW5VUkwgfSA9IGF3YWl0IHJlc29sdmVLZXlzUmVwbyhwb3NpdGlvbmFsWzBdKTtcblxuICAgIGxldCByZXZvY2F0aW9ucyA9IGF3YWl0IHJlYWRSZW1vdGVSZXZvY2F0aW9ucyh7IHNvdXJjZVVSTDogb3JpZ2luVVJMIH0pO1xuICAgIGlmICghcmV2b2NhdGlvbnMubGVuZ3RoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGAke3Jldm9rZVJlcG9VUkwob3JpZ2luVVJMKX0gbGlzdHMgbm8gcmV2b2NhdGlvbnMsIHNvIHRoZXJlIGlzIG5vdGhpbmcgdG8gdW5kby5gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGxldCBkaXJlY3RvcnkgPSBwYXRoLmpvaW4ocmVwb1BhdGgsIFVOUkVWT0tFU19ESVIpO1xuICAgIGF3YWl0IGZzLm1rZGlyKGRpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgbGV0IHN0YW1wID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnJlcGxhY2UoL1s6Ll0vZywgXCItXCIpO1xuICAgIGxldCB1bnJldm9rZUlkID0gYCR7c3RhbXB9LXVucmV2b2tlYDtcbiAgICBhd2FpdCBmcy53cml0ZUZpbGUocGF0aC5qb2luKGRpcmVjdG9yeSwgYCR7dW5yZXZva2VJZH0uanNvbmApLCBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgIHVucmV2b2tlSWQsXG4gICAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICAvLyBOYW1lZCBvbmUgYnkgb25lIHJhdGhlciB0aGFuIGFzIGEgYmxhbmtldCBcImFsbG93IGV2ZXJ5dGhpbmcgYWdhaW5cIiwgc28gdGhpcyBmaWxlIG9ubHlcbiAgICAgICAgLy8gZXZlciB1bmRvZXMgdGhlIHJldm9jYXRpb25zIHRoYXQgZXhpc3RlZCB3aGVuIGl0IHdhcyB3cml0dGVuLlxuICAgICAgICByZXZvY2F0aW9uSWRzOiByZXZvY2F0aW9ucy5tYXAocmV2b2NhdGlvbiA9PiByZXZvY2F0aW9uLnJldm9jYXRpb25JZCksXG4gICAgICAgIHJldm9jYXRpb25zOiByZXZvY2F0aW9ucy5tYXAocmV2b2NhdGlvbiA9PiAoe1xuICAgICAgICAgICAgcmV2b2NhdGlvbklkOiByZXZvY2F0aW9uLnJldm9jYXRpb25JZCxcbiAgICAgICAgICAgIGZpbmdlcnByaW50OiByZXZvY2F0aW9uLmZpbmdlcnByaW50LFxuICAgICAgICAgICAgcmV2b2tlZEF0OiByZXZvY2F0aW9uLnJldm9rZWRBdCxcbiAgICAgICAgICAgIHJldm9rZWRCeTogcmV2b2NhdGlvbi5yZXZva2VkQnksXG4gICAgICAgICAgICBhdHRlbXB0OiByZXZvY2F0aW9uLmF0dGVtcHQsXG4gICAgICAgIH0pKSxcbiAgICB9LCB1bmRlZmluZWQsIDQpICsgXCJcXG5cIik7XG5cbiAgICBjb25zb2xlLmxvZyhgV3JvdGUgJHtVTlJFVk9LRVNfRElSfS8ke3VucmV2b2tlSWR9Lmpzb24gY292ZXJpbmcgJHtyZXZvY2F0aW9ucy5sZW5ndGh9IHJldm9jYXRpb24ocyk6YCk7XG4gICAgZm9yIChsZXQgcmV2b2NhdGlvbiBvZiByZXZvY2F0aW9ucykge1xuICAgICAgICBjb25zb2xlLmxvZyhgICAke3Jldm9jYXRpb24uZmluZ2VycHJpbnR9IHJldm9rZWQgYnkgJHtyZXZvY2F0aW9uLnJldm9rZWRCeSB8fCBcIj9cIn0gZnJvbSAke3Jldm9jYXRpb24uYXR0ZW1wdD8uaXAgfHwgXCI/XCJ9YCk7XG4gICAgfVxuXG4gICAgLy8gU2lnbmVkIGhlcmUgcmF0aGVyIHRoYW4gbGVmdCBhcyBhIHN0ZXAgdG8gcmVtZW1iZXIuIEFuIHVucmV2b2tlIG5vYm9keSBzaWduZWQgZG9lcyBub3RoaW5nIGF0XG4gICAgLy8gYWxsLCBhbmQgdGhlIHJlcG8gd291bGQgc2l0IHRoZXJlIGxvb2tpbmcgZG9uZSB3aGlsZSBldmVyeSBtYWNoaW5lIGlnbm9yZWQgaXQuXG4gICAgYXdhaXQgc2lnblJlcG8oeyByZXBvUGF0aCB9KTtcblxuICAgIGlmICghcHVzaFRvR2l0KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBcXG5Db21taXQgYW5kIHB1c2ggaXQsIGFuZCBlYWNoIG1hY2hpbmUgd2lsbCB3YWl0IGFuIGhvdXIgYWZ0ZXIgc2VlaW5nIGl0IGJlZm9yZWApO1xuICAgICAgICBjb25zb2xlLmxvZyhgdGhvc2Uga2V5cyB3b3JrIGFnYWluOmApO1xuICAgICAgICBjb25zb2xlLmxvZyhgXFxgXFxgXFxgXFxuZ2l0IGFkZCAtQVxcbmdpdCBjb21taXQgLW0gXCIke0NPTU1JVF9NRVNTQUdFfVwiXFxuZ2l0IHB1c2hcXG5cXGBcXGBcXGBgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBhd2FpdCBydW5Qcm9taXNlKGBnaXQgYWRkIC1BYCwgeyBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGF3YWl0IHJ1blByb21pc2UoYGdpdCBjb21taXQgLW0gXCIke0NPTU1JVF9NRVNTQUdFfVwiYCwgeyBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGF3YWl0IHJ1blByb21pc2UoYGdpdCBwdXNoYCwgeyBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGNvbnNvbGUubG9nKGBcXG5Db21taXR0ZWQgYW5kIHB1c2hlZC4gRWFjaCBtYWNoaW5lIHdhaXRzIGFuIGhvdXIgYWZ0ZXIgc2VlaW5nIGl0IGJlZm9yZSB0aG9zZWApO1xuICAgIGNvbnNvbGUubG9nKGBrZXlzIHdvcmsgYWdhaW4uYCk7XG59XG4iXX0=
|
|
167
|
-
/* _JS_SOURCE_HASH = "
|
|
262
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5yZXZva2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ1bnJldm9rZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QiwrREFBNEQ7QUFDNUQscURBQStGO0FBQy9GLGlEQUFnRTtBQUNoRSx1Q0FBMEM7QUFDMUMsd0RBQW9EO0FBQ3BELHFEQUFnRDtBQUNoRCw0Q0FBOEM7QUFDOUMsNENBQWdEO0FBRWhELE1BQU0sYUFBYSxHQUFHLFdBQVcsQ0FBQztBQUNsQyxNQUFNLGVBQWUsR0FBRyxhQUFhLENBQUM7QUFDdEMsTUFBTSxXQUFXLEdBQUcsS0FBSyxDQUFDO0FBQzFCLE1BQU0sY0FBYyxHQUFHLGVBQWUsQ0FBQztBQUN2QyxNQUFNLEtBQUssR0FBRyxxQ0FBcUMsV0FBVzs7Ozt3Q0FJdEIsV0FBVzs7OzBCQUd6QixDQUFDO0FBZTNCLG1FQUFtRTtBQUNuRSxTQUFnQixZQUFZLENBQUMsVUFBc0I7O0lBQy9DLE9BQU8sVUFBVSxDQUFDLEVBQUUsS0FBSSxNQUFBLFVBQVUsQ0FBQyxPQUFPLDBDQUFFLEVBQUUsQ0FBQSxJQUFJLEVBQUUsQ0FBQztBQUN6RCxDQUFDO0FBRkQsb0NBRUM7QUFFRDs7OztrR0FJa0c7QUFDM0YsS0FBSyxVQUFVLHFCQUFxQixDQUFDLE1BQTZCO0lBQ3JFLElBQUksRUFBRSxTQUFTLEVBQUUsR0FBRyxNQUFNLENBQUM7SUFDM0IsSUFBSSxrQkFBa0IsR0FBRyxNQUFNLGtCQUFFLENBQUMsT0FBTyxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsWUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7SUFDL0UsSUFBSSxRQUFRLEdBQUcsY0FBSSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNyRCxJQUFJLFNBQVMsR0FBRyxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsR0FB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LENBQUMsaUZBQWlGLENBQUMsQ0FBQztJQUMvRixPQUFPLENBQUMsR0FBRyxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUFDcEMsQ0FBQztBQXRHRCxvQkFzR0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgb3MgZnJvbSBcIm9zXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHsgcnVuUHJvbWlzZSB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL3J1blByb21pc2VcIjtcbmltcG9ydCB7IGFsbG93QWRkcmVzc2VzLCBrZXlGaW5nZXJwcmludCwgbm9ybWFsaXplS2V5cywgc3VtbWFyaXplS2V5IH0gZnJvbSBcIi4vYXV0aG9yaXplZEtleXNcIjtcbmltcG9ydCB7IGRlcml2ZVJldm9rZUtleSwgcmV2b2tlUmVwb1VSTCB9IGZyb20gXCIuL3Jldm9rZVNvdXJjZVwiO1xuaW1wb3J0IHsgZmluZFNvdXJjZUtleSB9IGZyb20gXCIuL3NvdXJjZXNcIjtcbmltcG9ydCB7IHNpZ25SZXBvIH0gZnJvbSBcIi4uL3NpZ25lZEZpbGVzL3NpZ25GaWxlc1wiO1xuaW1wb3J0IHsgcmVhZFJlcG9LZXlzIH0gZnJvbSBcIi4vYXV0aG9yaXplZEtleXNcIjtcbmltcG9ydCB7IGV4cGFuZEhvbWUgfSBmcm9tIFwiLi4vaGVscGVycy9wYXRoc1wiO1xuaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4uL2hlbHBlcnMvc3Bhd25cIjtcblxuY29uc3QgVU5SRVZPS0VTX0RJUiA9IFwidW5yZXZva2VkXCI7XG5jb25zdCBSRVZPQ0FUSU9OU19ESVIgPSBcInJldm9jYXRpb25zXCI7XG5jb25zdCBHSVRfS0VZV09SRCA9IFwiZ2l0XCI7XG5jb25zdCBDT01NSVRfTUVTU0FHRSA9IFwidW5yZXZva2Uga2V5c1wiO1xuY29uc3QgVVNBR0UgPSBgVXNhZ2U6IHlhcm4gdW5yZXZva2UgW2tleXMtcmVwb10gWyR7R0lUX0tFWVdPUkR9XVxuXG5SdW4gdGhpcyBpbiBhIGtleXMgcmVwbywgb3IgbmFtZSBvbmUuIEl0IHJlYWRzIHRoYXQgcmVwbydzIHJldm9rZSByZXBvIGFuZCB3cml0ZXMgb25lIHVucmV2b2tlIGZpbGVcbm5hbWluZyBldmVyeSByZXZvY2F0aW9uIGluIGl0LCBzbyB0aGUga2V5cyBhcmUgYWNjZXB0ZWQgYWdhaW4gb25jZSBlYWNoIG1hY2hpbmUncyBob3VyIGxvbmcgd2FpdFxucGFzc2VzLiBJdCBzaWducyB0aGUgcmVzdWx0LCBhbmQgd2l0aCAke0dJVF9LRVlXT1JEfSBpdCBjb21taXRzIGFuZCBwdXNoZXMgaXQgdG9vLlxuXG5LZXlzIHRoYXQgd2VyZSByZXZva2VkIHNob3VsZCBub3JtYWxseSBiZSBkZWxldGVkIGZyb20gdGhlIHJlcG8gaW5zdGVhZC4gVW5yZXZva2luZyBvbmx5IG1hdHRlcnNcbmZvciBhIGtleSB5b3Ugc3RpbGwgd2FudC5gO1xuXG5leHBvcnQgdHlwZSBSZXZvY2F0aW9uID0ge1xuICAgIHJldm9jYXRpb25JZDogc3RyaW5nO1xuICAgIGZpbmdlcnByaW50OiBzdHJpbmc7XG4gICAgLy8gVGhlIGFkZHJlc3MgdGhlIGtleSB3YXMgdXNlZCBmcm9tLiBBIHJldm9jYXRpb24gaXMgYWJvdXQgdGhpcyBwYWlyLCBhbmQgYW4gdW5yZXZva2UgdW5kb2VzXG4gICAgLy8gaXQgYnkgYWxsb3dpbmcgdGhhdCBzYW1lIHBhaXIgLSB3aGljaCBpcyB3aHkgdW5yZXZva2luZyBuZXZlciBtYWtlcyBhIGtleSB1bnJldm9rYWJsZTogdXNlZFxuICAgIC8vIGZyb20gc29tZSBvdGhlciBhZGRyZXNzLCBpdCBpcyByZXZva2VkIGFnYWluLlxuICAgIGlwPzogc3RyaW5nO1xuICAgIGtleT86IHN0cmluZztcbiAgICByZXZva2VkQXQ/OiBzdHJpbmc7XG4gICAgcmV2b2tlZEJ5Pzogc3RyaW5nO1xuICAgIGF0dGVtcHQ/OiB7IGlwPzogc3RyaW5nOyB1c2VyPzogc3RyaW5nOyByZXF1aXJlZD86IHN0cmluZyB9O1xufTtcblxuLyoqIE9sZGVyIHJldm9jYXRpb24gZmlsZXMgY2FycnkgdGhlIGFkZHJlc3MgdW5kZXIgYXR0ZW1wdCBvbmx5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJldm9jYXRpb25JUChyZXZvY2F0aW9uOiBSZXZvY2F0aW9uKSB7XG4gICAgcmV0dXJuIHJldm9jYXRpb24uaXAgfHwgcmV2b2NhdGlvbi5hdHRlbXB0Py5pcCB8fCBcIlwiO1xufVxuXG4vKiogRXZlcnkgcmV2b2NhdGlvbiB0aGUgcmV2b2tlIHJlcG8gbGlzdHMuIENsb25lZCByZWFkIG9ubHkgaW50byBhIHRlbXAgZGlyZWN0b3J5LlxuXG4gICAgQSBwZXJzb24gcnVubmluZyB0aGlzIGhhcyB0aGVpciBvd24gYWNjZXNzIHRvIHRoZSByZXBvLCBzbyB0aGVpciBjcmVkZW50aWFscyBhcmUgd2hhdCBpcyB1c2VkLlxuICAgIE9uIGEgbWFjaGluZSB0aGF0IGlzIGFscmVhZHkgYSBwb3J0c2VjdXJlIGhvc3QgdGhlcmUgbWF5IGJlIG5vIHN1Y2ggY3JlZGVudGlhbHMsIGJ1dCB0aGUgc291cmNlXG4gICAgZGVwbG95IGtleSBpcyByaWdodCB0aGVyZSwgYW5kIHRoZSBrZXkgZGVyaXZlZCBmcm9tIGl0IGlzIG9uZSB0aGF0IHJlcG8gZGVmaW5pdGVseSBhY2NlcHRzLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRSZW1vdGVSZXZvY2F0aW9ucyhjb25maWc6IHsgc291cmNlVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHNvdXJjZVVSTCB9ID0gY29uZmlnO1xuICAgIGxldCB0ZW1wb3JhcnlEaXJlY3RvcnkgPSBhd2FpdCBmcy5ta2R0ZW1wKHBhdGguam9pbihvcy50bXBkaXIoKSwgXCJ1bnJldm9rZS1cIikpO1xuICAgIGxldCByZXBvUGF0aCA9IHBhdGguam9pbih0ZW1wb3JhcnlEaXJlY3RvcnksIFwicmVwb1wiKTtcbiAgICBsZXQgY2xvbmVBcmdzID0gW1wiY2xvbmVcIiwgXCItLWRlcHRoXCIsIFwiMVwiLCByZXZva2VSZXBvVVJMKHNvdXJjZVVSTCksIHJlcG9QYXRoXTtcbiAgICBsZXQgc291cmNlS2V5ID0gYXdhaXQgZmluZFNvdXJjZUtleShzb3VyY2VVUkwpO1xuICAgIGlmIChzb3VyY2VLZXkpIHtcbiAgICAgICAgbGV0IGRlcml2ZWQgPSBkZXJpdmVSZXZva2VLZXkoYXdhaXQgZnMucmVhZEZpbGUoc291cmNlS2V5LCBcInV0ZjhcIikpO1xuICAgICAgICBsZXQgZGVyaXZlZEtleVBhdGggPSBwYXRoLmpvaW4odGVtcG9yYXJ5RGlyZWN0b3J5LCBcImtleVwiKTtcbiAgICAgICAgYXdhaXQgZnMud3JpdGVGaWxlKGRlcml2ZWRLZXlQYXRoLCBkZXJpdmVkLnByaXZhdGVLZXlGaWxlLCB7IG1vZGU6IDBvNjAwIH0pO1xuICAgICAgICBsZXQgc3NoQ29tbWFuZCA9IGBzc2ggLWkgJHtkZXJpdmVkS2V5UGF0aH0gLW8gSWRlbnRpdGllc09ubHk9eWVzIC1vIEJhdGNoTW9kZT15ZXMgLW8gU3RyaWN0SG9zdEtleUNoZWNraW5nPWFjY2VwdC1uZXdgO1xuICAgICAgICBjbG9uZUFyZ3MgPSBbXCItY1wiLCBgY29yZS5zc2hDb21tYW5kPSR7c3NoQ29tbWFuZH1gLCAuLi5jbG9uZUFyZ3NdO1xuICAgIH1cbiAgICBsZXQgY2xvbmUgPSBhd2FpdCBzcGF3blByb21pc2UoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBjbG9uZUFyZ3MgfSk7XG4gICAgaWYgKGNsb25lLnN0YXR1cyAhPT0gMCkge1xuICAgICAgICBhd2FpdCBmcy5ybSh0ZW1wb3JhcnlEaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIHRvIGJlIGFibGUgdG8gcmVhZCAke3Jldm9rZVJlcG9VUkwoc291cmNlVVJMKX0uXFxuYFxuICAgICAgICAgICAgKyBgJHsoY2xvbmUuc3Rkb3V0ICsgY2xvbmUuc3RkZXJyKS50cmltKCl9YFxuICAgICAgICApO1xuICAgIH1cbiAgICBsZXQgcmV2b2NhdGlvbnM6IFJldm9jYXRpb25bXSA9IFtdO1xuICAgIGxldCBkaXJlY3RvcnkgPSBwYXRoLmpvaW4ocmVwb1BhdGgsIFJFVk9DQVRJT05TX0RJUik7XG4gICAgdHJ5IHtcbiAgICAgICAgZm9yIChsZXQgbmFtZSBvZiAoYXdhaXQgZnMucmVhZGRpcihkaXJlY3RvcnkpKS5zb3J0KCkpIHtcbiAgICAgICAgICAgIGlmIChuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgICAgICByZXZvY2F0aW9ucy5wdXNoKEpTT04ucGFyc2UoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKGRpcmVjdG9yeSwgbmFtZSksIFwidXRmOFwiKSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAvLyBObyByZXZvY2F0aW9ucyBkaXJlY3RvcnkgYXQgYWxsIGp1c3QgbWVhbnMgbm90aGluZyBoYXMgZXZlciBiZWVuIHJldm9rZWQuXG4gICAgfVxuICAgIGF3YWl0IGZzLnJtKHRlbXBvcmFyeURpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgIHJldHVybiByZXZvY2F0aW9ucztcbn1cblxuLyoqIFdoaWNoIGtleXMgaW4gYSByZXBvIGFyZSByZXZva2VkLiBXaGF0IHNpZ25maWxlcyBhbmQgc2VjdXJlc3NoIHJlZnVzZSBvdmVyLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJldm9rZWRLZXlzSW5SZXBvKGNvbmZpZzogeyByZXBvUGF0aDogc3RyaW5nOyBzb3VyY2VVUkw6IHN0cmluZyB9KSB7XG4gICAgbGV0IHsgcmVwb1BhdGgsIHNvdXJjZVVSTCB9ID0gY29uZmlnO1xuICAgIGxldCByZXZvY2F0aW9ucyA9IGF3YWl0IHJlYWRSZW1vdGVSZXZvY2F0aW9ucyh7IHNvdXJjZVVSTCB9KTtcbiAgICBsZXQgcmV2b2tlZEZpbmdlcnByaW50cyA9IG5ldyBTZXQocmV2b2NhdGlvbnMubWFwKHJldm9jYXRpb24gPT4gcmV2b2NhdGlvbi5maW5nZXJwcmludCkpO1xuICAgIGxldCB1bnJldm9rZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICB0cnkge1xuICAgICAgICBsZXQgZGlyZWN0b3J5ID0gcGF0aC5qb2luKHJlcG9QYXRoLCBVTlJFVk9LRVNfRElSKTtcbiAgICAgICAgZm9yIChsZXQgbmFtZSBvZiBhd2FpdCBmcy5yZWFkZGlyKGRpcmVjdG9yeSkpIHtcbiAgICAgICAgICAgIGlmICghbmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gSlNPTi5wYXJzZShhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBuYW1lKSwgXCJ1dGY4XCIpKTtcbiAgICAgICAgICAgIGZvciAobGV0IGFsbG93ZWQgb2YgcGFyc2VkLmFsbG93ZWQgfHwgW10pIHtcbiAgICAgICAgICAgICAgICB1bnJldm9rZWQuYWRkKGAke2FsbG93ZWQuZmluZ2VycHJpbnR9ICR7YWxsb3dlZC5pcH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIE5hbWVkIGlkcywgZm9yIHRoZSB1bnJldm9rZXMgd3JpdHRlbiBiZWZvcmUgdGhpcyB3YXMgYWJvdXQgcGFpcnMuXG4gICAgICAgICAgICBmb3IgKGxldCByZXZvY2F0aW9uSWQgb2YgcGFyc2VkLnJldm9jYXRpb25JZHMgfHwgW10pIHtcbiAgICAgICAgICAgICAgICB1bnJldm9rZWQuYWRkKHJldm9jYXRpb25JZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIE5vdGhpbmcgaGFzIGJlZW4gdW5yZXZva2VkLlxuICAgIH1cbiAgICBsZXQgc3RpbGxSZXZva2VkID0gcmV2b2NhdGlvbnMuZmlsdGVyKHJldm9jYXRpb24gPT4gIXVucmV2b2tlZC5oYXMocmV2b2NhdGlvbi5yZXZvY2F0aW9uSWQpXG4gICAgICAgICYmICF1bnJldm9rZWQuaGFzKGAke3Jldm9jYXRpb24uZmluZ2VycHJpbnR9ICR7cmV2b2NhdGlvbklQKHJldm9jYXRpb24pfWApKTtcbiAgICBsZXQga2V5cyA9IG5vcm1hbGl6ZUtleXMoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKSwgXCJ1dGY4XCIpLmNhdGNoKCgpID0+IFwiXCIpKTtcbiAgICByZXR1cm4gc3RpbGxSZXZva2VkXG4gICAgICAgIC5maWx0ZXIocmV2b2NhdGlvbiA9PiBrZXlzLnNvbWUoa2V5ID0+IGtleUZpbmdlcnByaW50KGtleSkgPT09IHJldm9jYXRpb24uZmluZ2VycHJpbnQpKVxuICAgICAgICAubWFwKHJldm9jYXRpb24gPT4gKHsgcmV2b2NhdGlvbiwga2V5OiBrZXlzLmZpbmQoa2V5ID0+IGtleUZpbmdlcnByaW50KGtleSkgPT09IHJldm9jYXRpb24uZmluZ2VycHJpbnQpIHx8IFwiXCIgfSkpO1xufVxuXG4vKiogV3JpdGVzIHRoZSBhZGRyZXNzZXMgaW50byB0aGUgZnJvbT0gbGlzdCBvZiBlYWNoIGtleSBpbiB0aGUgcmVwbydzIGF1dGhvcml6ZWRfa2V5cywgYW5kIHNheXNcbiAgICB3aGljaCBvbmVzIHdlcmUgYWN0dWFsbHkgbmV3LiBPbmx5IHRoZSBsaW5lcyBiZWluZyBjaGFuZ2VkIGFyZSB0b3VjaGVkLCBzbyBjb21tZW50cywgb3JkZXJpbmdcbiAgICBhbmQgYW55IGtleSBub3QgaW52b2x2ZWQgY29tZSBvdXQgb2YgdGhpcyBieXRlIGZvciBieXRlIHRoZSBzYW1lLiAqL1xuYXN5bmMgZnVuY3Rpb24gYWxsb3dBZGRyZXNzZXNJblJlcG8oY29uZmlnOiB7XG4gICAgcmVwb1BhdGg6IHN0cmluZztcbiAgICBhZGRyZXNzZXNCeUZpbmdlcnByaW50OiBNYXA8c3RyaW5nLCBzdHJpbmdbXT47XG59KSB7XG4gICAgbGV0IHsgcmVwb1BhdGgsIGFkZHJlc3Nlc0J5RmluZ2VycHJpbnQgfSA9IGNvbmZpZztcbiAgICBsZXQgYWRkZWQgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nW10+KCk7XG4gICAgbGV0IGZpbGVQYXRoID0gcGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKTtcbiAgICBsZXQgY29udGVudHM7XG4gICAgdHJ5IHtcbiAgICAgICAgY29udGVudHMgPSBhd2FpdCBmcy5yZWFkRmlsZShmaWxlUGF0aCwgXCJ1dGY4XCIpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gQSByZXBvIGhvbGRpbmcgaXRzIGtleXMgYXMgc2VwYXJhdGUgLnB1YiBmaWxlcy4gTm90aGluZyB0byBlZGl0IGluIG9uZSBwbGFjZSwgc28gdGhlXG4gICAgICAgIC8vIGNhbGxlcidzIGxpc3RpbmcgaXMgYWxsIHdob2V2ZXIgcmFuIHRoaXMgZ2V0cy5cbiAgICAgICAgY29uc29sZS5sb2coYE5vIGF1dGhvcml6ZWRfa2V5cyBpbiAke3JlcG9QYXRofSwgc28gdGhlIGFkZHJlc3NlcyB3ZXJlIG5vdCBhZGRlZCB0byBhbnkga2V5LmApO1xuICAgICAgICByZXR1cm4gYWRkZWQ7XG4gICAgfVxuICAgIGxldCBsaW5lcyA9IGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpO1xuICAgIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCBsaW5lcy5sZW5ndGg7IGluZGV4KyspIHtcbiAgICAgICAgbGV0IGZpbmdlcnByaW50ID0ga2V5RmluZ2VycHJpbnQobGluZXNbaW5kZXhdKTtcbiAgICAgICAgbGV0IGFkZHJlc3NlcyA9IGZpbmdlcnByaW50ICYmIGFkZHJlc3Nlc0J5RmluZ2VycHJpbnQuZ2V0KGZpbmdlcnByaW50KTtcbiAgICAgICAgaWYgKCFhZGRyZXNzZXMgfHwgIWFkZHJlc3Nlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGxldCByZXN1bHQgPSBhbGxvd0FkZHJlc3NlcyhsaW5lc1tpbmRleF0sIGFkZHJlc3Nlcyk7XG4gICAgICAgIGxpbmVzW2luZGV4XSA9IHJlc3VsdC5rZXlMaW5lO1xuICAgICAgICBhZGRlZC5zZXQoZmluZ2VycHJpbnQsIFsuLi4oYWRkZWQuZ2V0KGZpbmdlcnByaW50KSB8fCBbXSksIC4uLnJlc3VsdC5hZGRlZF0pO1xuICAgIH1cbiAgICBpZiAoWy4uLmFkZGVkLnZhbHVlcygpXS5zb21lKGxpc3QgPT4gbGlzdC5sZW5ndGgpKSB7XG4gICAgICAgIGF3YWl0IGZzLndyaXRlRmlsZShmaWxlUGF0aCwgbGluZXMuam9pbihcIlxcblwiKSk7XG4gICAgfVxuICAgIHJldHVybiBhZGRlZDtcbn1cblxuLyoqIFRoZSByZXBvIG5hbWVkLCBvciB0aGUgb25lIHdlIGFyZSBzdGFuZGluZyBpbi4gQ2hlY2tlZCB0aGUgc2FtZSB3YXkgc2VjdXJlc3NoIGNoZWNrcyBpdDogYSBnaXRcbiAgICByZXBvIHRoYXQgYWN0dWFsbHkgaG9sZHMga2V5cyBhbmQgaGFzIGFuIG9yaWdpbiB0byBjbG9uZSBmcm9tLiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZUtleXNSZXBvKG5hbWVkOiBzdHJpbmcgfCB1bmRlZmluZWQpIHtcbiAgICBsZXQgY3dkID0gbmFtZWQgJiYgZXhwYW5kSG9tZShuYW1lZCkgfHwgdW5kZWZpbmVkO1xuICAgIC8vIHNwYXduUHJvbWlzZSByYXRoZXIgdGhhbiBydW5Qcm9taXNlLCBiZWNhdXNlIHRoZXNlIHR3byBhcmUgcmVhZCBmb3IgdGhlaXIgdmFsdWUuIHJ1blByb21pc2VcbiAgICAvLyByZXR1cm5zIHN0ZG91dCBhbmQgc3RkZXJyIGpvaW5lZCwgc28gb25lIGdpdCB3YXJuaW5nIGVuZHMgdXAgZ2x1ZWQgdG8gdGhlIGZyb250IG9mIHRoZSBwYXRoLlxuICAgIGxldCB0b3BMZXZlbCA9IGF3YWl0IHNwYXduUHJvbWlzZSh7IGNvbW1hbmQ6IFwiZ2l0XCIsIGFyZ3M6IFtcInJldi1wYXJzZVwiLCBcIi0tc2hvdy10b3BsZXZlbFwiXSwgY3dkIH0pO1xuICAgIGxldCByZXBvUGF0aCA9IHRvcExldmVsLnN0ZG91dC50cmltKCk7XG4gICAgaWYgKHRvcExldmVsLnN0YXR1cyAhPT0gMCB8fCAhcmVwb1BhdGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkICR7bmFtZWQgfHwgXCJ0aGUgY3VycmVudCBkaXJlY3RvcnlcIn0gdG8gYmUgaW5zaWRlIGEgZ2l0IHJlcG8sIGl0IGlzIG5vdC5cXG5gXG4gICAgICAgICAgICArIGAkeyh0b3BMZXZlbC5zdGRvdXQgKyB0b3BMZXZlbC5zdGRlcnIpLnRyaW0oKX1cXG4ke1VTQUdFfWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgcmVhZFJlcG9LZXlzKHJlcG9QYXRoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtyZXBvUGF0aH0gdG8gYmUgYSBrZXlzIHJlcG8sIGl0IGhvbGRzIG5vIGtleXMuXFxuJHtlfVxcbiR7VVNBR0V9YCk7XG4gICAgfVxuICAgIGxldCBvcmlnaW4gPSBhd2FpdCBzcGF3blByb21pc2UoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBbXCJyZW1vdGVcIiwgXCJnZXQtdXJsXCIsIFwib3JpZ2luXCJdLCBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGxldCBvcmlnaW5VUkwgPSBvcmlnaW4uc3Rkb3V0LnRyaW0oKTtcbiAgICBpZiAob3JpZ2luLnN0YXR1cyAhPT0gMCB8fCAhb3JpZ2luVVJMKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtyZXBvUGF0aH0gdG8gaGF2ZSBhbiBvcmlnaW4gcmVtb3RlLCBpdCBoYXMgbm9uZS5cXG4ke1VTQUdFfWApO1xuICAgIH1cbiAgICByZXR1cm4geyByZXBvUGF0aCwgb3JpZ2luVVJMIH07XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBtYWluKCkge1xuICAgIGxldCBhcmd2ID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpO1xuICAgIGxldCBwdXNoVG9HaXQgPSBhcmd2LmluY2x1ZGVzKEdJVF9LRVlXT1JEKTtcbiAgICBsZXQgcG9zaXRpb25hbCA9IGFyZ3YuZmlsdGVyKGFyZyA9PiBhcmcgIT09IEdJVF9LRVlXT1JEKTtcbiAgICBpZiAocG9zaXRpb25hbC5sZW5ndGggPiAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXQgbW9zdCBhIGtleXMgcmVwbywgd2FzICR7cG9zaXRpb25hbC5sZW5ndGh9IGFyZ3VtZW50KHMpXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgbGV0IHsgcmVwb1BhdGgsIG9yaWdpblVSTCB9ID0gYXdhaXQgcmVzb2x2ZUtleXNSZXBvKHBvc2l0aW9uYWxbMF0pO1xuXG4gICAgbGV0IHJldm9jYXRpb25zID0gYXdhaXQgcmVhZFJlbW90ZVJldm9jYXRpb25zKHsgc291cmNlVVJMOiBvcmlnaW5VUkwgfSk7XG4gICAgaWYgKCFyZXZvY2F0aW9ucy5sZW5ndGgpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCR7cmV2b2tlUmVwb1VSTChvcmlnaW5VUkwpfSBsaXN0cyBubyByZXZvY2F0aW9ucywgc28gdGhlcmUgaXMgbm90aGluZyB0byB1bmRvLmApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgVU5SRVZPS0VTX0RJUik7XG4gICAgYXdhaXQgZnMubWtkaXIoZGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBsZXQgc3RhbXAgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkucmVwbGFjZSgvWzouXS9nLCBcIi1cIik7XG4gICAgbGV0IHVucmV2b2tlSWQgPSBgJHtzdGFtcH0tdW5yZXZva2VgO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBgJHt1bnJldm9rZUlkfS5qc29uYCksIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgdW5yZXZva2VJZCxcbiAgICAgICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgIC8vIFdoYXQgdGhpcyBmaWxlIHNheXM6IGVhY2ggb2YgdGhlc2Uga2V5cyBpcyBhbGxvd2VkIGZyb20gdGhpcyBhZGRyZXNzLiBQYWlycywgbm90IGtleXMsXG4gICAgICAgIC8vIGFuZCBub3QgYSBibGFua2V0IFwiYWxsb3cgZXZlcnl0aGluZyBhZ2FpblwiIC0gYSBrZXkgZm9yZ2l2ZW4gaGVyZSBmb3Igb25lIGFkZHJlc3MgaXNcbiAgICAgICAgLy8gc3RpbGwgcmV2b2tlZCB0aGUgbW9tZW50IGl0IGlzIHVzZWQgZnJvbSBhbm90aGVyLlxuICAgICAgICBhbGxvd2VkOiByZXZvY2F0aW9ucy5tYXAocmV2b2NhdGlvbiA9PiAoe1xuICAgICAgICAgICAgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24uZmluZ2VycHJpbnQsXG4gICAgICAgICAgICBpcDogcmV2b2NhdGlvbklQKHJldm9jYXRpb24pLFxuICAgICAgICB9KSksXG4gICAgICAgIHJldm9jYXRpb25zOiByZXZvY2F0aW9ucy5tYXAocmV2b2NhdGlvbiA9PiAoe1xuICAgICAgICAgICAgcmV2b2NhdGlvbklkOiByZXZvY2F0aW9uLnJldm9jYXRpb25JZCxcbiAgICAgICAgICAgIGZpbmdlcnByaW50OiByZXZvY2F0aW9uLmZpbmdlcnByaW50LFxuICAgICAgICAgICAgaXA6IHJldm9jYXRpb25JUChyZXZvY2F0aW9uKSxcbiAgICAgICAgICAgIHJldm9rZWRBdDogcmV2b2NhdGlvbi5yZXZva2VkQXQsXG4gICAgICAgICAgICByZXZva2VkQnk6IHJldm9jYXRpb24ucmV2b2tlZEJ5LFxuICAgICAgICAgICAgYXR0ZW1wdDogcmV2b2NhdGlvbi5hdHRlbXB0LFxuICAgICAgICB9KSksXG4gICAgfSwgdW5kZWZpbmVkLCA0KSArIFwiXFxuXCIpO1xuXG4gICAgLy8gTmFtZWQgdGhlIHdheSB0aGUgbm90aWZpY2F0aW9ucyBuYW1lIHRoZW0uIEEgZmluZ2VycHJpbnQgaXMgbm90IGluIGF1dGhvcml6ZWRfa2V5cyBhbmRcbiAgICAvLyBjYW5ub3QgYmUgd29ya2VkIG91dCBieSBleWUsIHNvIG9uIGl0cyBvd24gaXQgdGVsbHMgd2hvZXZlciBpcyByZWFkaW5nIHRoaXMgbm90aGluZyBhYm91dFxuICAgIC8vIHdoaWNoIG9mIHRoZWlyIGtleXMgaXMgYmVpbmcgdGFsa2VkIGFib3V0LlxuICAgIGxldCBrZXlzQnlGaW5nZXJwcmludCA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gICAgZm9yIChsZXQga2V5IG9mIGF3YWl0IHJlYWRSZXBvS2V5cyhyZXBvUGF0aCkuY2F0Y2goKCkgPT4gW10gYXMgc3RyaW5nW10pKSB7XG4gICAgICAgIGtleXNCeUZpbmdlcnByaW50LnNldChrZXlGaW5nZXJwcmludChrZXkpLCBrZXkpO1xuICAgIH1cbiAgICBsZXQgZGVzY3JpYmVLZXkgPSAocmV2b2NhdGlvbjogUmV2b2NhdGlvbikgPT4ge1xuICAgICAgICBsZXQga2V5ID0ga2V5c0J5RmluZ2VycHJpbnQuZ2V0KHJldm9jYXRpb24uZmluZ2VycHJpbnQpIHx8IHJldm9jYXRpb24ua2V5IHx8IFwiXCI7XG4gICAgICAgIHJldHVybiBrZXkgJiYgc3VtbWFyaXplS2V5KGtleSkgfHwgYGEga2V5IG5vIGxvbmdlciBpbiB0aGlzIHJlcG8gKCR7cmV2b2NhdGlvbi5maW5nZXJwcmludH0pYDtcbiAgICB9O1xuXG4gICAgbGV0IGFkZHJlc3Nlc0J5RmluZ2VycHJpbnQgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nW10+KCk7XG4gICAgZm9yIChsZXQgcmV2b2NhdGlvbiBvZiByZXZvY2F0aW9ucykge1xuICAgICAgICBsZXQgaXAgPSByZXZvY2F0aW9uSVAocmV2b2NhdGlvbik7XG4gICAgICAgIGxldCBleGlzdGluZyA9IGFkZHJlc3Nlc0J5RmluZ2VycHJpbnQuZ2V0KHJldm9jYXRpb24uZmluZ2VycHJpbnQpIHx8IFtdO1xuICAgICAgICBpZiAoaXAgJiYgIWV4aXN0aW5nLmluY2x1ZGVzKGlwKSkge1xuICAgICAgICAgICAgZXhpc3RpbmcucHVzaChpcCk7XG4gICAgICAgIH1cbiAgICAgICAgYWRkcmVzc2VzQnlGaW5nZXJwcmludC5zZXQocmV2b2NhdGlvbi5maW5nZXJwcmludCwgZXhpc3RpbmcpO1xuICAgIH1cblxuICAgIC8vIENhbmNlbGxpbmcgdGhlIHJldm9jYXRpb24gb24gaXRzIG93biB3b3VsZCBsZWF2ZSBzc2hkIHJlZnVzaW5nIHRoYXQgYWRkcmVzcyBhbmQgdGhlIG1hY2hpbmVzXG4gICAgLy8gcmV2b2tpbmcgdGhlIGtleSBhbGwgb3ZlciBhZ2Fpbiwgc28gdGhlIGFkZHJlc3NlcyBnbyBpbnRvIHRoZSBrZXkncyBmcm9tPSBoZXJlLiBXZSBhcmUgaW5cbiAgICAvLyB0aGUgcmVwbyB0aGF0IG93bnMgYXV0aG9yaXplZF9rZXlzLCBzbyB0aGVyZSBpcyBub3RoaW5nIHRvIGhhbmQgYmFjayB0byB3aG9ldmVyIHJhbiB0aGlzLlxuICAgIGxldCBhZGRlZCA9IGF3YWl0IGFsbG93QWRkcmVzc2VzSW5SZXBvKHsgcmVwb1BhdGgsIGFkZHJlc3Nlc0J5RmluZ2VycHJpbnQgfSk7XG5cbiAgICBjb25zb2xlLmxvZyhcIlwiKTtcbiAgICBmb3IgKGxldCBbZmluZ2VycHJpbnQsIGFkZHJlc3Nlc10gb2YgYWRkcmVzc2VzQnlGaW5nZXJwcmludCkge1xuICAgICAgICBsZXQgcmV2b2NhdGlvbiA9IHJldm9jYXRpb25zLmZpbmQoZW50cnkgPT4gZW50cnkuZmluZ2VycHJpbnQgPT09IGZpbmdlcnByaW50KSE7XG4gICAgICAgIGNvbnNvbGUubG9nKGBVbmZyZWV6aW5nICR7ZGVzY3JpYmVLZXkocmV2b2NhdGlvbil9YCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBBbGxvd2luZyBJUCAke2FkZHJlc3Nlcy5qb2luKFwiLCBcIikgfHwgXCIobm8gYWRkcmVzcyByZWNvcmRlZClcIn1gKTtcbiAgICAgICAgbGV0IGFkZGVkSGVyZSA9IGFkZGVkLmdldChmaW5nZXJwcmludCkgfHwgW107XG4gICAgICAgIGlmIChhZGRlZEhlcmUubGVuZ3RoKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgICBhZGRlZCB0byB0aGF0IGtleSdzIGZyb209IGxpc3QgaW4gYXV0aG9yaXplZF9rZXlzOiAke2FkZGVkSGVyZS5qb2luKFwiLCBcIil9YCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgICB0aGF0IGtleSdzIGZyb209IGxpc3QgYWxyZWFkeSBhbGxvd2VkICR7YWRkcmVzc2VzLmpvaW4oXCIsIFwiKSA/IFwidGhlbVwiIDogXCJldmVyeXRoaW5nXCJ9YCk7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgZW50cnkgb2YgcmV2b2NhdGlvbnMuZmlsdGVyKGVudHJ5ID0+IGVudHJ5LmZpbmdlcnByaW50ID09PSBmaW5nZXJwcmludCkpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgIGAgIGZyb3plbiBieSAke2VudHJ5LnJldm9rZWRCeSB8fCBcIj9cIn0gJHtlbnRyeS5yZXZva2VkQXQgfHwgXCJhdCBhbiB1bnJlY29yZGVkIHRpbWVcIn1gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUubG9nKFwiXCIpO1xuICAgIH1cbiAgICBjb25zb2xlLmxvZyhgRXZlcnkgb3RoZXIgYWRkcmVzcyBzdGF5cyBmcm96ZW4sIGFuZCBvbmUgb2YgdGhlc2Uga2V5cyB1c2VkIGZyb20gYW55d2hlcmUgZWxzZWApO1xuICAgIGNvbnNvbGUubG9nKGBpcyBmcm96ZW4gYWdhaW4uYCk7XG5cbiAgICAvLyBTaWduZWQgaGVyZSByYXRoZXIgdGhhbiBsZWZ0IGFzIGEgc3RlcCB0byByZW1lbWJlci4gQW4gdW5yZXZva2Ugbm9ib2R5IHNpZ25lZCBkb2VzIG5vdGhpbmcgYXRcbiAgICAvLyBhbGwsIGFuZCB0aGUgcmVwbyB3b3VsZCBzaXQgdGhlcmUgbG9va2luZyBkb25lIHdoaWxlIGV2ZXJ5IG1hY2hpbmUgaWdub3JlZCBpdC5cbiAgICBhd2FpdCBzaWduUmVwbyh7IHJlcG9QYXRoIH0pO1xuXG4gICAgaWYgKCFwdXNoVG9HaXQpIHtcbiAgICAgICAgY29uc29sZS5sb2coYFxcbkNvbW1pdCBhbmQgcHVzaCBpdCwgYW5kIGVhY2ggbWFjaGluZSB3aWxsIHdhaXQgYW4gaG91ciBhZnRlciBzZWVpbmcgaXQgYmVmb3JlYCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGB0aG9zZSBrZXlzIHdvcmsgYWdhaW46YCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBcXGBcXGBcXGBcXG5naXQgYWRkIC1BXFxuZ2l0IGNvbW1pdCAtbSBcIiR7Q09NTUlUX01FU1NBR0V9XCJcXG5naXQgcHVzaFxcblxcYFxcYFxcYGApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGF3YWl0IHJ1blByb21pc2UoYGdpdCBhZGQgLUFgLCB7IGN3ZDogcmVwb1BhdGggfSk7XG4gICAgYXdhaXQgcnVuUHJvbWlzZShgZ2l0IGNvbW1pdCAtbSBcIiR7Q09NTUlUX01FU1NBR0V9XCJgLCB7IGN3ZDogcmVwb1BhdGggfSk7XG4gICAgYXdhaXQgcnVuUHJvbWlzZShgZ2l0IHB1c2hgLCB7IGN3ZDogcmVwb1BhdGggfSk7XG4gICAgY29uc29sZS5sb2coYFxcbkNvbW1pdHRlZCBhbmQgcHVzaGVkLiBFYWNoIG1hY2hpbmUgd2FpdHMgYW4gaG91ciBhZnRlciBzZWVpbmcgaXQgYmVmb3JlIHRob3NlYCk7XG4gICAgY29uc29sZS5sb2coYGtleXMgd29yayBhZ2Fpbi5gKTtcbn1cbiJdfQ==
|
|
263
|
+
/* _JS_SOURCE_HASH = "9b90ba359ba9cd1b6be2a54ba85a896c0ebcf4bac2b38be51f7d4886cdc1820e"; */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { deriveEd25519Key } from "../keys/deriveKey";
|
|
2
2
|
import { formatOpenSSHPrivateKey, parseOpenSSHPrivateKey } from "../keys/sshKeyFile";
|
|
3
|
-
import
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { findKey, keysDir, LEGACY_REPO_KEYS_DIR, REPOS_DIR, sourceName } from "./sources";
|
|
4
5
|
|
|
5
6
|
// A source's revocations live in a repo beside it. Derived rather than configured, so adding a
|
|
6
7
|
// source brings its revoke repo with it and there is nothing extra to pass on the command line.
|
|
@@ -15,7 +16,16 @@ export function revokeRepoURL(sourceURL: string) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function revokeKeyPath(sourceURL: string) {
|
|
18
|
-
return `${
|
|
19
|
+
return path.join(keysDir(), `${sourceName(sourceURL)}${REVOKED_SUFFIX}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function legacyRevokeKeyPath(sourceURL: string) {
|
|
23
|
+
return `${LEGACY_REPO_KEYS_DIR}/${sourceName(sourceURL)}${REVOKED_SUFFIX}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Wherever this source's revoke key already is, or nothing if it has not been derived yet. */
|
|
27
|
+
export async function findRevokeKey(sourceURL: string) {
|
|
28
|
+
return await findKey({ current: revokeKeyPath(sourceURL), legacy: legacyRevokeKeyPath(sourceURL) });
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
export function revokeRepoPath(sourceURL: string) {
|
|
@@ -3,8 +3,9 @@ import os from "os";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { DEFAULT_WEBHOOK_FILE_PATH, parseWebhookFile } from "../notifications/discord";
|
|
5
5
|
import { normalizeKeys, readRepoKeys, summarizeKey } from "./authorizedKeys";
|
|
6
|
-
import { sourceKeyPath, sourceRepoPath } from "./sources";
|
|
6
|
+
import { findSourceKey, KEYS_DIR_NAME, sourceKeyPath, sourceName, sourceRepoPath } from "./sources";
|
|
7
7
|
import { deriveRevokeKey, REVOKE_KEY_LABEL, revokeRepoURL } from "./revokeSource";
|
|
8
|
+
import { legacySourceKeyPath } from "./sources";
|
|
8
9
|
import { revokedKeysInRepo } from "./unrevoke";
|
|
9
10
|
import { expandHome } from "../helpers/paths";
|
|
10
11
|
import { spawnPromise } from "../helpers/spawn";
|
|
@@ -125,6 +126,27 @@ async function fingerprintKeys(keys: string[]) {
|
|
|
125
126
|
return fingerprints;
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
/** Where the daemon on that machine keeps its keys: the home of the user it runs as. Asked of the
|
|
130
|
+
machine itself, because the answer is not the same everywhere and certainly not the same as the
|
|
131
|
+
home of whoever is running this. */
|
|
132
|
+
async function daemonKeysDir(host: string) {
|
|
133
|
+
let result = await runOverSSH({
|
|
134
|
+
host,
|
|
135
|
+
script: `${SUDO_PREAMBLE}\n$SUDO getent passwd root | cut -d: -f6`,
|
|
136
|
+
});
|
|
137
|
+
let home = result.stdout.trim();
|
|
138
|
+
if (!home) {
|
|
139
|
+
throw new Error(`Expected ${describeHost(host)} to report a home directory for root, it reported nothing`);
|
|
140
|
+
}
|
|
141
|
+
return `${home}/${KEYS_DIR_NAME}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The public half of a private key, as ssh-keygen derives it. */
|
|
145
|
+
async function publicKeyOf(privateKeyPath: string) {
|
|
146
|
+
let result = await runLocal({ command: "ssh-keygen", args: ["-y", "-f", privateKeyPath] });
|
|
147
|
+
return `${result.stdout.trim()}\n`;
|
|
148
|
+
}
|
|
149
|
+
|
|
128
150
|
async function cloneRepoForInspection(config: { repoURL: string; keyPath: string }) {
|
|
129
151
|
let { repoURL, keyPath } = config;
|
|
130
152
|
let temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "portsecure-repo-"));
|
|
@@ -184,8 +206,9 @@ async function ensureRevokeReposExist(repoSources: string[]) {
|
|
|
184
206
|
// and the key derived from it is the one credential that repo is guaranteed to accept.
|
|
185
207
|
// Anywhere else, a person is running this and has their own access.
|
|
186
208
|
let ownKey = "";
|
|
187
|
-
|
|
188
|
-
|
|
209
|
+
let localSourceKey = await findSourceKey(repoURL);
|
|
210
|
+
if (localSourceKey) {
|
|
211
|
+
let derived = deriveRevokeKey(await fs.readFile(localSourceKey, "utf8"));
|
|
189
212
|
ownKey = path.join(temporaryDirectory, "key");
|
|
190
213
|
await fs.writeFile(ownKey, derived.privateKeyFile, { mode: 0o600 });
|
|
191
214
|
}
|
|
@@ -426,6 +449,31 @@ async function addSource(config: { host: string; keyPath: string; repoURL: strin
|
|
|
426
449
|
console.log(`${describeHost(host)} already has ${repoURL}, refreshing its key and the daemon.`);
|
|
427
450
|
}
|
|
428
451
|
|
|
452
|
+
// Copied into place before anything else needs it. The checks below look this source's key up
|
|
453
|
+
// where the machine keeps it, not at the path it was handed on the command line, so a key that
|
|
454
|
+
// is still only sitting in the caller's directory reads as no key at all.
|
|
455
|
+
//
|
|
456
|
+
// Into the home of whoever the daemon runs as, which is where it looks. Asked of the host
|
|
457
|
+
// rather than worked out here: this may be running on a machine with no such user, and with a
|
|
458
|
+
// home directory in an entirely different shape.
|
|
459
|
+
let hostKeysDirectory = await daemonKeysDir(host);
|
|
460
|
+
await writeRemoteFile({
|
|
461
|
+
host,
|
|
462
|
+
filePath: `${hostKeysDirectory}/${sourceName(repoURL)}`,
|
|
463
|
+
contents: await fs.readFile(keyPath, "utf8"),
|
|
464
|
+
fileMode: "600",
|
|
465
|
+
directoryMode: "700",
|
|
466
|
+
});
|
|
467
|
+
// The public half too, so the deploy key that a repo was given can be looked up on the machine
|
|
468
|
+
// using it, rather than only in whatever github shows.
|
|
469
|
+
await writeRemoteFile({
|
|
470
|
+
host,
|
|
471
|
+
filePath: `${hostKeysDirectory}/${sourceName(repoURL)}.pub`,
|
|
472
|
+
contents: await publicKeyOf(keyPath),
|
|
473
|
+
fileMode: "644",
|
|
474
|
+
directoryMode: "700",
|
|
475
|
+
});
|
|
476
|
+
|
|
429
477
|
// The merged result is what root ends up with, so our own key has to be somewhere in it. On
|
|
430
478
|
// this machine there is no ssh session to preserve, so there is nothing to check.
|
|
431
479
|
let ourFingerprint = "";
|
|
@@ -467,14 +515,6 @@ async function addSource(config: { host: string; keyPath: string; repoURL: strin
|
|
|
467
515
|
let webhookURL = await requireRemoteWebhook(host);
|
|
468
516
|
console.log(`${describeHost(host)} notifies ${webhookURL}`);
|
|
469
517
|
|
|
470
|
-
await writeRemoteFile({
|
|
471
|
-
host,
|
|
472
|
-
filePath: sourceKeyPath(repoURL),
|
|
473
|
-
contents: await fs.readFile(keyPath, "utf8"),
|
|
474
|
-
fileMode: "600",
|
|
475
|
-
directoryMode: "700",
|
|
476
|
-
});
|
|
477
|
-
|
|
478
518
|
let repoSources = remoteConfig.repoSources.filter(source => source !== repoURL);
|
|
479
519
|
repoSources.push(repoURL);
|
|
480
520
|
await installDaemon({ host, hostLabel: remoteConfig.hostLabel, repoSources });
|
|
@@ -519,7 +559,7 @@ async function removeSource(config: { host: string; repoURL: string }) {
|
|
|
519
559
|
await runOverSSH({
|
|
520
560
|
host,
|
|
521
561
|
script: `${SUDO_PREAMBLE}
|
|
522
|
-
$SUDO rm -f "${sourceKeyPath(repoURL)}"
|
|
562
|
+
$SUDO rm -f "${sourceKeyPath(repoURL)}" "${sourceKeyPath(repoURL)}.pub" "${legacySourceKeyPath(repoURL)}"
|
|
523
563
|
$SUDO rm -rf "${sourceRepoPath(repoURL)}"`,
|
|
524
564
|
});
|
|
525
565
|
await installDaemon({ host, hostLabel: remoteConfig.hostLabel, repoSources });
|
|
@@ -601,7 +641,21 @@ function parseArgs(argv: string[]) {
|
|
|
601
641
|
return { verb, host, rest: positional };
|
|
602
642
|
}
|
|
603
643
|
|
|
644
|
+
/** Everything here is root's: root's authorized_keys, root's keys directory, the sshd config, the
|
|
645
|
+
systemd unit. Run as anyone else it reads a different home and writes files the daemon will
|
|
646
|
+
never see, and it gets there several steps in rather than failing at the start. */
|
|
647
|
+
function requireRoot() {
|
|
648
|
+
let uid = process.getuid?.();
|
|
649
|
+
if (uid !== 0) {
|
|
650
|
+
throw new Error(
|
|
651
|
+
`Expected to be running as root, is uid ${uid ?? "unknown"}.\n`
|
|
652
|
+
+ `Rerun this with sudo.`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
604
657
|
export async function main() {
|
|
658
|
+
requireRoot();
|
|
605
659
|
let { verb, host, rest } = parseArgs(process.argv.slice(2));
|
|
606
660
|
|
|
607
661
|
if (verb === "list") {
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// PORTED CODE: nothing here is duplicated any more, the daemon imports it directly. Kept in one
|
|
6
|
+
// place because the daemon and the deploy have to agree on where a source's key and checkout live.
|
|
7
|
+
|
|
8
|
+
// The user's own folder, so this works the same on a machine that has no /etc. The daemon runs as
|
|
9
|
+
// root, so on a host this is root's home.
|
|
10
|
+
export const KEYS_DIR_NAME = "authorized_keys";
|
|
11
|
+
// Where keys used to go. Still read, so a host set up before this keeps working, but nothing is
|
|
12
|
+
// written here any more.
|
|
13
|
+
export const LEGACY_REPO_KEYS_DIR = "/etc/portsecure/repo-keys";
|
|
6
14
|
export const REPOS_DIR = "/var/lib/portsecure/authorized-keys-repos";
|
|
7
15
|
|
|
16
|
+
export function keysDir() {
|
|
17
|
+
return path.join(os.homedir(), KEYS_DIR_NAME);
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
/** A repo url reduced to something usable as a file name. Derived rather than configured, so the
|
|
9
21
|
daemon and the deploy script always agree on where a source's key and checkout live. */
|
|
10
22
|
export function sourceName(repoURL: string) {
|
|
@@ -12,9 +24,39 @@ export function sourceName(repoURL: string) {
|
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
export function sourceKeyPath(repoURL: string) {
|
|
15
|
-
return
|
|
27
|
+
return path.join(keysDir(), sourceName(repoURL));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function legacySourceKeyPath(repoURL: string) {
|
|
31
|
+
return `${LEGACY_REPO_KEYS_DIR}/${sourceName(repoURL)}`;
|
|
16
32
|
}
|
|
17
33
|
|
|
18
34
|
export function sourceRepoPath(repoURL: string) {
|
|
19
35
|
return `${REPOS_DIR}/${sourceName(repoURL)}`;
|
|
20
36
|
}
|
|
37
|
+
|
|
38
|
+
async function pathExists(filePath: string) {
|
|
39
|
+
try {
|
|
40
|
+
await fs.access(filePath);
|
|
41
|
+
return true;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The key for a source, wherever it happens to be. The user folder is where they go now, and the
|
|
48
|
+
old location is still read so a host that predates the move keeps working. */
|
|
49
|
+
export async function findKey(config: { current: string; legacy: string }) {
|
|
50
|
+
let { current, legacy } = config;
|
|
51
|
+
if (await pathExists(current)) {
|
|
52
|
+
return current;
|
|
53
|
+
}
|
|
54
|
+
if (await pathExists(legacy)) {
|
|
55
|
+
return legacy;
|
|
56
|
+
}
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function findSourceKey(repoURL: string) {
|
|
61
|
+
return await findKey({ current: sourceKeyPath(repoURL), legacy: legacySourceKeyPath(repoURL) });
|
|
62
|
+
}
|