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.
Files changed (54) hide show
  1. package/bin/addmachine.js +9 -0
  2. package/bin/machineclient.js +9 -0
  3. package/bin/machineserver.js +10 -0
  4. package/index.d.ts +15 -0
  5. package/misc/dist/ownIPs.ts.cache +43 -0
  6. package/misc/dist/strings.ts.cache +2 -2
  7. package/misc/https/dist/certs.ts.cache +2 -2
  8. package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
  9. package/misc/ownIPs.d.ts +11 -0
  10. package/misc/ownIPs.ts +34 -0
  11. package/package.json +9 -3
  12. package/security/authorizedKeys/authorizedKeys.ts +34 -0
  13. package/security/authorizedKeys/daemon/authLog.ts +12 -5
  14. package/security/authorizedKeys/daemon/changes.ts +16 -1
  15. package/security/authorizedKeys/daemon/daemon.ts +50 -40
  16. package/security/authorizedKeys/daemon/dist/changes.ts.cache +40 -0
  17. package/security/authorizedKeys/daemon/dist/git.ts.cache +121 -0
  18. package/security/authorizedKeys/daemon/dist/notify.ts.cache +39 -0
  19. package/security/authorizedKeys/daemon/dist/paths.ts.cache +29 -0
  20. package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +114 -0
  21. package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
  22. package/security/authorizedKeys/daemon/dist/sessions.ts.cache +147 -0
  23. package/security/authorizedKeys/daemon/dist/state.ts.cache +74 -0
  24. package/security/authorizedKeys/daemon/dist/trust.ts.cache +295 -0
  25. package/security/authorizedKeys/daemon/git.ts +3 -3
  26. package/security/authorizedKeys/daemon/notify.ts +8 -2
  27. package/security/authorizedKeys/daemon/repoFiles.ts +118 -0
  28. package/security/authorizedKeys/daemon/revocation.ts +0 -0
  29. package/security/authorizedKeys/daemon/rootKeys.ts +77 -10
  30. package/security/authorizedKeys/daemon/state.ts +25 -7
  31. package/security/authorizedKeys/daemon/trust.ts +37 -16
  32. package/security/authorizedKeys/daemon/userKeys.ts +5 -4
  33. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +37 -3
  34. package/security/authorizedKeys/dist/revokeSource.ts.cache +17 -4
  35. package/security/authorizedKeys/dist/sources.ts.cache +52 -8
  36. package/security/authorizedKeys/dist/unrevoke.ts.cache +113 -17
  37. package/security/authorizedKeys/revokeSource.ts +12 -2
  38. package/security/authorizedKeys/secureSSH.ts +66 -12
  39. package/security/authorizedKeys/sources.ts +47 -5
  40. package/security/authorizedKeys/unrevoke.ts +176 -16
  41. package/security/helpers/dist/remoteSSH.ts.cache +19 -9
  42. package/security/machines/addMachine.ts +83 -0
  43. package/security/machines/dist/addMachine.ts.cache +88 -0
  44. package/security/machines/dist/identity.ts.cache +176 -0
  45. package/security/machines/dist/machines.ts.cache +322 -0
  46. package/security/machines/dist/trustClient.ts.cache +48 -0
  47. package/security/machines/dist/trustState.ts.cache +57 -0
  48. package/security/machines/identity.ts +199 -0
  49. package/security/machines/machines.ts +355 -0
  50. package/security/machines/trustClient.ts +51 -0
  51. package/security/machines/trustServer.ts +85 -0
  52. package/security/machines/trustState.ts +53 -0
  53. package/security/notifications/discord.ts +14 -5
  54. package/security/notifications/dist/discord.ts.cache +17 -8
@@ -1,11 +1,25 @@
1
1
  "use strict";
2
- // PORTED CODE: security/authorizedKeys/daemon/portsecureDaemon.js contains a plain JS port of everything here, so it can
3
- // resolve the same locations with no dependencies. Both sides must derive identical paths from a
4
- // repo url - if you change one, make the matching change in the other.
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.REPO_KEYS_DIR = void 0;
7
- exports.REPO_KEYS_DIR = "/etc/portsecure/repo-keys";
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 `${exports.REPO_KEYS_DIR}/${sourceName(repoURL)}`;
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
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNvdXJjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHlIQUF5SDtBQUN6SCxpR0FBaUc7QUFDakcsdUVBQXVFOzs7QUFFMUQsUUFBQSxhQUFhLEdBQUcsMkJBQTJCLENBQUM7QUFDNUMsUUFBQSxTQUFTLEdBQUcsMkNBQTJDLENBQUM7QUFFckU7MkZBQzJGO0FBQzNGLFNBQWdCLFVBQVUsQ0FBQyxPQUFlO0lBQ3RDLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3hGLENBQUM7QUFGRCxnQ0FFQztBQUVELFNBQWdCLGFBQWEsQ0FBQyxPQUFlO0lBQ3pDLE9BQU8sR0FBRyxxQkFBYSxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0FBQ3JELENBQUM7QUFGRCxzQ0FFQztBQUVELFNBQWdCLGNBQWMsQ0FBQyxPQUFlO0lBQzFDLE9BQU8sR0FBRyxpQkFBUyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0FBQ2pELENBQUM7QUFGRCx3Q0FFQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFBPUlRFRCBDT0RFOiBzZWN1cml0eS9hdXRob3JpemVkS2V5cy9kYWVtb24vcG9ydHNlY3VyZURhZW1vbi5qcyBjb250YWlucyBhIHBsYWluIEpTIHBvcnQgb2YgZXZlcnl0aGluZyBoZXJlLCBzbyBpdCBjYW5cbi8vIHJlc29sdmUgdGhlIHNhbWUgbG9jYXRpb25zIHdpdGggbm8gZGVwZW5kZW5jaWVzLiBCb3RoIHNpZGVzIG11c3QgZGVyaXZlIGlkZW50aWNhbCBwYXRocyBmcm9tIGFcbi8vIHJlcG8gdXJsIC0gaWYgeW91IGNoYW5nZSBvbmUsIG1ha2UgdGhlIG1hdGNoaW5nIGNoYW5nZSBpbiB0aGUgb3RoZXIuXG5cbmV4cG9ydCBjb25zdCBSRVBPX0tFWVNfRElSID0gXCIvZXRjL3BvcnRzZWN1cmUvcmVwby1rZXlzXCI7XG5leHBvcnQgY29uc3QgUkVQT1NfRElSID0gXCIvdmFyL2xpYi9wb3J0c2VjdXJlL2F1dGhvcml6ZWQta2V5cy1yZXBvc1wiO1xuXG4vKiogQSByZXBvIHVybCByZWR1Y2VkIHRvIHNvbWV0aGluZyB1c2FibGUgYXMgYSBmaWxlIG5hbWUuIERlcml2ZWQgcmF0aGVyIHRoYW4gY29uZmlndXJlZCwgc28gdGhlXG4gICAgZGFlbW9uIGFuZCB0aGUgZGVwbG95IHNjcmlwdCBhbHdheXMgYWdyZWUgb24gd2hlcmUgYSBzb3VyY2UncyBrZXkgYW5kIGNoZWNrb3V0IGxpdmUuICovXG5leHBvcnQgZnVuY3Rpb24gc291cmNlTmFtZShyZXBvVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcmVwb1VSTC5yZXBsYWNlKC9bXkEtWmEtejAtOV0rL2csIFwiLVwiKS5yZXBsYWNlKC9eLSt8LSskL2csIFwiXCIpLnRvTG93ZXJDYXNlKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzb3VyY2VLZXlQYXRoKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPX0tFWVNfRElSfS8ke3NvdXJjZU5hbWUocmVwb1VSTCl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNvdXJjZVJlcG9QYXRoKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPU19ESVJ9LyR7c291cmNlTmFtZShyZXBvVVJMKX1gO1xufVxuIl19
24
- /* _JS_SOURCE_HASH = "d4a51163d16ac57a8fa5966ca3b09eaebcbef4ca2200c706a7e125220c7db7b8"; */
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
- /** Every revocation the revoke repo lists. Cloned read only into a temp directory, with whatever
30
- credentials this machine already has - the derived deploy key is for servers, and a person
31
- running this has their own access to the repo. */
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 clone = await (0, spawn_1.spawnPromise)({
37
- command: "git",
38
- args: ["clone", "--depth", "1", (0, revokeSource_1.revokeRepoURL)(sourceURL), repoPath],
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
- // Named one by one rather than as a blanket "allow everything again", so this file only
136
- // ever undoes the revocations that existed when it was written.
137
- revocationIds: revocations.map(revocation => revocation.revocationId),
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
- console.log(`Wrote ${UNREVOKES_DIR}/${unrevokeId}.json covering ${revocations.length} revocation(s):`);
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
- console.log(` ${revocation.fingerprint} revoked by ${revocation.revokedBy || "?"} from ${((_a = revocation.attempt) === null || _a === void 0 ? void 0 : _a.ip) || "?"}`);
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 = "0fcca51775122c0bd892f4c8b9872f06ef62cdb278ee8668702a7ce402c07867"; */
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 { REPO_KEYS_DIR, REPOS_DIR, sourceName } from "./sources";
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 `${REPO_KEYS_DIR}/${sourceName(sourceURL)}${REVOKED_SUFFIX}`;
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
- if (await pathExists(sourceKeyPath(repoURL))) {
188
- let derived = deriveRevokeKey(await fs.readFile(sourceKeyPath(repoURL), "utf8"));
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
- // PORTED CODE: security/authorizedKeys/daemon/portsecureDaemon.js contains a plain JS port of everything here, so it can
2
- // resolve the same locations with no dependencies. Both sides must derive identical paths from a
3
- // repo url - if you change one, make the matching change in the other.
1
+ import fs from "fs/promises";
2
+ import os from "os";
3
+ import path from "path";
4
4
 
5
- export const REPO_KEYS_DIR = "/etc/portsecure/repo-keys";
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 `${REPO_KEYS_DIR}/${sourceName(repoURL)}`;
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
+ }