@intentic/keyring 1.344.0 → 1.346.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -45
- package/dist/address.d.ts +0 -1
- package/dist/address.d.ts.map +1 -1
- package/dist/address.js +1 -1
- package/dist/address.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +49 -48
- package/dist/app.js.map +1 -1
- package/dist/askpass/answer.d.ts +12 -0
- package/dist/askpass/answer.d.ts.map +1 -0
- package/dist/askpass/answer.js +204 -0
- package/dist/askpass/answer.js.map +1 -0
- package/dist/askpass/askpass.d.ts +6 -0
- package/dist/askpass/askpass.d.ts.map +1 -1
- package/dist/askpass/askpass.js +28 -0
- package/dist/askpass/askpass.js.map +1 -1
- package/dist/audit.d.ts +5 -2
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +15 -2
- package/dist/audit.js.map +1 -1
- package/dist/consent/consent-posix.d.ts +7 -3
- package/dist/consent/consent-posix.d.ts.map +1 -1
- package/dist/consent/consent-posix.js +82 -12
- package/dist/consent/consent-posix.js.map +1 -1
- package/dist/consent/consent-windows.d.ts +3 -1
- package/dist/consent/consent-windows.d.ts.map +1 -1
- package/dist/consent/consent-windows.js +174 -62
- package/dist/consent/consent-windows.js.map +1 -1
- package/dist/consent/consent.d.ts +41 -2
- package/dist/consent/consent.d.ts.map +1 -1
- package/dist/consent/consent.js +7 -5
- package/dist/consent/consent.js.map +1 -1
- package/dist/core.d.ts +3 -30
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +189 -62
- package/dist/core.js.map +1 -1
- package/dist/git-credential/git-credential.d.ts +18 -0
- package/dist/git-credential/git-credential.d.ts.map +1 -0
- package/dist/git-credential/git-credential.js +68 -0
- package/dist/git-credential/git-credential.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/native-host/framing.d.ts +10 -0
- package/dist/native-host/framing.d.ts.map +1 -0
- package/dist/native-host/framing.js +33 -0
- package/dist/native-host/framing.js.map +1 -0
- package/dist/native-host/native-host.d.ts +11 -0
- package/dist/native-host/native-host.d.ts.map +1 -0
- package/dist/native-host/native-host.js +130 -0
- package/dist/native-host/native-host.js.map +1 -0
- package/dist/passwords/access.d.ts +73 -0
- package/dist/passwords/access.d.ts.map +1 -0
- package/dist/passwords/access.js +3 -0
- package/dist/passwords/access.js.map +1 -0
- package/dist/passwords/app-ops.d.ts +18 -0
- package/dist/passwords/app-ops.d.ts.map +1 -0
- package/dist/passwords/app-ops.js +261 -0
- package/dist/passwords/app-ops.js.map +1 -0
- package/dist/passwords/capture.d.ts +35 -0
- package/dist/passwords/capture.d.ts.map +1 -0
- package/dist/passwords/capture.js +306 -0
- package/dist/passwords/capture.js.map +1 -0
- package/dist/passwords/entries.d.ts +20 -0
- package/dist/passwords/entries.d.ts.map +1 -0
- package/dist/passwords/entries.js +57 -0
- package/dist/passwords/entries.js.map +1 -0
- package/dist/passwords/fills.d.ts +10 -0
- package/dist/passwords/fills.d.ts.map +1 -0
- package/dist/passwords/fills.js +87 -0
- package/dist/passwords/fills.js.map +1 -0
- package/dist/passwords/needs.d.ts +11 -0
- package/dist/passwords/needs.d.ts.map +1 -0
- package/dist/passwords/needs.js +232 -0
- package/dist/passwords/needs.js.map +1 -0
- package/dist/passwords/phone.d.ts +19 -0
- package/dist/passwords/phone.d.ts.map +1 -0
- package/dist/passwords/phone.js +125 -0
- package/dist/passwords/phone.js.map +1 -0
- package/dist/protocol.d.ts +1169 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +86 -1
- package/dist/protocol.js.map +1 -1
- package/dist/service/channel.d.ts +10 -3
- package/dist/service/channel.d.ts.map +1 -1
- package/dist/service/channel.js +31 -13
- package/dist/service/channel.js.map +1 -1
- package/dist/service/door.d.ts +45 -4
- package/dist/service/door.d.ts.map +1 -1
- package/dist/service/door.js +31 -2
- package/dist/service/door.js.map +1 -1
- package/dist/service/helper.d.ts +18 -1
- package/dist/service/helper.d.ts.map +1 -1
- package/dist/service/helper.js +68 -28
- package/dist/service/helper.js.map +1 -1
- package/dist/service/linux/install-linux.d.ts +12 -0
- package/dist/service/linux/install-linux.d.ts.map +1 -0
- package/dist/service/linux/install-linux.js +133 -0
- package/dist/service/linux/install-linux.js.map +1 -0
- package/dist/service/linux/paths-linux.d.ts +21 -0
- package/dist/service/linux/paths-linux.d.ts.map +1 -0
- package/dist/service/linux/paths-linux.js +31 -0
- package/dist/service/linux/paths-linux.js.map +1 -0
- package/dist/service/linux/polkit-linux.d.ts +29 -0
- package/dist/service/linux/polkit-linux.d.ts.map +1 -0
- package/dist/service/linux/polkit-linux.js +89 -0
- package/dist/service/linux/polkit-linux.js.map +1 -0
- package/dist/service/linux/service-run-linux.d.ts +4 -0
- package/dist/service/linux/service-run-linux.d.ts.map +1 -0
- package/dist/service/linux/service-run-linux.js +52 -0
- package/dist/service/linux/service-run-linux.js.map +1 -0
- package/dist/service/linux/session-linux.d.ts +4 -0
- package/dist/service/linux/session-linux.d.ts.map +1 -0
- package/dist/service/linux/session-linux.js +13 -0
- package/dist/service/linux/session-linux.js.map +1 -0
- package/dist/service/linux/unit-linux.d.ts +16 -0
- package/dist/service/linux/unit-linux.d.ts.map +1 -0
- package/dist/service/linux/unit-linux.js +67 -0
- package/dist/service/linux/unit-linux.js.map +1 -0
- package/dist/service/platform.d.ts +7 -0
- package/dist/service/platform.d.ts.map +1 -0
- package/dist/service/platform.js +62 -0
- package/dist/service/platform.js.map +1 -0
- package/dist/service/recovery.d.ts +3 -0
- package/dist/service/recovery.d.ts.map +1 -0
- package/dist/service/recovery.js +22 -0
- package/dist/service/recovery.js.map +1 -0
- package/dist/service/remote.d.ts +4 -1
- package/dist/service/remote.d.ts.map +1 -1
- package/dist/service/remote.js +41 -6
- package/dist/service/remote.js.map +1 -1
- package/dist/service/service-key.d.ts +9 -0
- package/dist/service/service-key.d.ts.map +1 -0
- package/dist/service/service-key.js +55 -0
- package/dist/service/service-key.js.map +1 -0
- package/dist/service/service-run.d.ts +20 -2
- package/dist/service/service-run.d.ts.map +1 -1
- package/dist/service/service-run.js +52 -46
- package/dist/service/service-run.js.map +1 -1
- package/dist/service/user.d.ts +8 -0
- package/dist/service/user.d.ts.map +1 -0
- package/dist/service/user.js +39 -0
- package/dist/service/user.js.map +1 -0
- package/dist/service/{acl.d.ts → windows/acl-windows.d.ts} +3 -3
- package/dist/service/windows/acl-windows.d.ts.map +1 -0
- package/dist/service/{acl.js → windows/acl-windows.js} +3 -3
- package/dist/service/windows/acl-windows.js.map +1 -0
- package/dist/service/{install.d.ts → windows/install-windows.d.ts} +4 -2
- package/dist/service/windows/install-windows.d.ts.map +1 -0
- package/dist/service/{install.js → windows/install-windows.js} +13 -12
- package/dist/service/windows/install-windows.js.map +1 -0
- package/dist/service/windows/leftovers-windows.d.ts +8 -0
- package/dist/service/windows/leftovers-windows.d.ts.map +1 -0
- package/dist/service/windows/leftovers-windows.js +44 -0
- package/dist/service/windows/leftovers-windows.js.map +1 -0
- package/dist/service/{paths.d.ts → windows/paths-windows.d.ts} +4 -5
- package/dist/service/windows/paths-windows.d.ts.map +1 -0
- package/dist/service/{paths.js → windows/paths-windows.js} +5 -5
- package/dist/service/windows/paths-windows.js.map +1 -0
- package/dist/service/windows/service-run-windows.d.ts +4 -0
- package/dist/service/windows/service-run-windows.d.ts.map +1 -0
- package/dist/service/windows/service-run-windows.js +46 -0
- package/dist/service/windows/service-run-windows.js.map +1 -0
- package/dist/service/windows/session-windows.d.ts +4 -0
- package/dist/service/windows/session-windows.d.ts.map +1 -0
- package/dist/service/windows/session-windows.js +13 -0
- package/dist/service/windows/session-windows.js.map +1 -0
- package/dist/service/windows/sid-windows.d.ts +3 -0
- package/dist/service/windows/sid-windows.d.ts.map +1 -0
- package/dist/service/{sid.js → windows/sid-windows.js} +2 -2
- package/dist/service/windows/sid-windows.js.map +1 -0
- package/dist/service/{task.d.ts → windows/task-windows.d.ts} +4 -3
- package/dist/service/windows/task-windows.d.ts.map +1 -0
- package/dist/service/{task.js → windows/task-windows.js} +19 -2
- package/dist/service/windows/task-windows.js.map +1 -0
- package/dist/service/wire.d.ts +32 -0
- package/dist/service/wire.d.ts.map +1 -1
- package/dist/service/wire.js +15 -0
- package/dist/service/wire.js.map +1 -1
- package/dist/typing/clipboard.d.ts +13 -0
- package/dist/typing/clipboard.d.ts.map +1 -0
- package/dist/typing/clipboard.js +56 -0
- package/dist/typing/clipboard.js.map +1 -0
- package/dist/typing/index.d.ts.map +1 -1
- package/dist/typing/index.js +2 -1
- package/dist/typing/index.js.map +1 -1
- package/dist/typing/typist.d.ts +3 -1
- package/dist/typing/typist.d.ts.map +1 -1
- package/dist/typing/typist.js +2 -1
- package/dist/typing/typist.js.map +1 -1
- package/dist/vault.d.ts +785 -0
- package/dist/vault.d.ts.map +1 -1
- package/dist/vault.js +24 -2
- package/dist/vault.js.map +1 -1
- package/dist/watch/apps.d.ts +10 -0
- package/dist/watch/apps.d.ts.map +1 -0
- package/dist/watch/apps.js +95 -0
- package/dist/watch/apps.js.map +1 -0
- package/dist/watch/chip.d.ts +52 -0
- package/dist/watch/chip.d.ts.map +1 -0
- package/dist/watch/chip.js +76 -0
- package/dist/watch/chip.js.map +1 -0
- package/dist/watch/lines-child.d.ts +9 -0
- package/dist/watch/lines-child.d.ts.map +1 -0
- package/dist/watch/lines-child.js +34 -0
- package/dist/watch/lines-child.js.map +1 -0
- package/dist/watch/watch-linux.d.ts +6 -0
- package/dist/watch/watch-linux.d.ts.map +1 -0
- package/dist/watch/watch-linux.js +52 -0
- package/dist/watch/watch-linux.js.map +1 -0
- package/dist/watch/watch-windows.d.ts +6 -0
- package/dist/watch/watch-windows.d.ts.map +1 -0
- package/dist/watch/watch-windows.js +110 -0
- package/dist/watch/watch-windows.js.map +1 -0
- package/package.json +5 -5
- package/dist/serve.d.ts +0 -17
- package/dist/serve.d.ts.map +0 -1
- package/dist/serve.js +0 -65
- package/dist/serve.js.map +0 -1
- package/dist/service/acl.d.ts.map +0 -1
- package/dist/service/acl.js.map +0 -1
- package/dist/service/install.d.ts.map +0 -1
- package/dist/service/install.js.map +0 -1
- package/dist/service/paths.d.ts.map +0 -1
- package/dist/service/paths.js.map +0 -1
- package/dist/service/sid.d.ts +0 -3
- package/dist/service/sid.d.ts.map +0 -1
- package/dist/service/sid.js.map +0 -1
- package/dist/service/task.d.ts.map +0 -1
- package/dist/service/task.js.map +0 -1
package/dist/core.js
CHANGED
|
@@ -1,26 +1,43 @@
|
|
|
1
1
|
import { serialLock } from "@intentic/base/async";
|
|
2
2
|
import { KeyringSandboxKeysSchema, } from "@intentic/sandbox-contract/schemas/keyring";
|
|
3
3
|
import { keyFingerprint, newSealKeyPair, seal, unseal } from "@intentic/sandbox-contract/keyring-seal";
|
|
4
|
-
import {
|
|
4
|
+
import { NOT_SET_UP, refuse } from "./passwords/access.js";
|
|
5
|
+
import { AppOps } from "./passwords/app-ops.js";
|
|
6
|
+
import { Askpass } from "./askpass/answer.js";
|
|
7
|
+
import { Captures, seenIn } from "./passwords/capture.js";
|
|
8
|
+
import { DEFAULT_SETTINGS, entryInfoOf, healthOf, liveEntries, usedEntry, withEntry } from "./passwords/entries.js";
|
|
9
|
+
import { Fills } from "./passwords/fills.js";
|
|
5
10
|
import { pinOf, samePin } from "./keys.js";
|
|
6
|
-
import {
|
|
11
|
+
import { Needs } from "./passwords/needs.js";
|
|
12
|
+
import { Phones } from "./passwords/phone.js";
|
|
13
|
+
import { acceptGrant, applyPatch, hostOf, isUnlocked, MINUTE_MS, modeNeedsPerson, NonceBook, releaseTargetOf, sandboxKey, shareWith, typeTargetOf, typingDecision, wideningOf, } from "./policy.js";
|
|
7
14
|
import { emptyOpenPart, helloChallenge, helloWrapOf, newVaultKey, openDocument, PENDING_INBOX_MAX, PENDING_SYNC_MAX, SCRYPT_COST, sealDocument, StoredEntrySchema, unwrapHello, unwrapPassphrase, VALUE_MAX_BYTES, wrapWithHello, wrapWithPassphrase, } from "./vault.js";
|
|
8
|
-
const RELAYED_OPS = ["shares", "release", "type", "inbox", "copies", "sync-state", "sync-pair", "sync-outbox", "sync-deliver"];
|
|
15
|
+
const RELAYED_OPS = ["shares", "release", "type", "inbox", "copies", "sync-state", "sync-pair", "sync-outbox", "sync-deliver", "capture", "need", "phone-pair", "phone-fill"];
|
|
16
|
+
const APP_OPS = [
|
|
17
|
+
"capture-batch",
|
|
18
|
+
"undo-capture",
|
|
19
|
+
"seen",
|
|
20
|
+
"reveal",
|
|
21
|
+
"copy",
|
|
22
|
+
"history",
|
|
23
|
+
"restore",
|
|
24
|
+
"settings",
|
|
25
|
+
"set-settings",
|
|
26
|
+
"activity",
|
|
27
|
+
"fill-window",
|
|
28
|
+
"fill-value",
|
|
29
|
+
"candidates",
|
|
30
|
+
"needs",
|
|
31
|
+
"answer-need",
|
|
32
|
+
];
|
|
9
33
|
const RELAYED = new Set(RELAYED_OPS);
|
|
34
|
+
const APP = new Set(APP_OPS);
|
|
10
35
|
const isRelayed = (request) => RELAYED.has(request.op);
|
|
11
|
-
const
|
|
36
|
+
const isApp = (request) => APP.has(request.op);
|
|
12
37
|
const fromConsent = (answer) => refuse(answer.code, answer.reason);
|
|
13
|
-
const NOT_SET_UP = "The keyring on this computer is not set up yet: `intentic-machine keyring setup` sets it up.";
|
|
14
38
|
const SETUP_UNLOCK_MINUTES = 15;
|
|
15
39
|
const FOCUS_SETTLE_MS = 400;
|
|
16
40
|
const SyncEntrySchema = StoredEntrySchema.omit({ shares: true, revision: true });
|
|
17
|
-
const entryInfoOf = (entry) => {
|
|
18
|
-
const info = { name: entry.name, ...entry.rules, source: entry.source, shares: entry.shares, createdAt: entry.createdAt, updatedAt: entry.updatedAt };
|
|
19
|
-
if (entry.backend !== undefined) {
|
|
20
|
-
info.backend = entry.backend;
|
|
21
|
-
}
|
|
22
|
-
return info;
|
|
23
|
-
};
|
|
24
41
|
const syncEntryOf = (entry) => {
|
|
25
42
|
const sent = { name: entry.name, rules: entry.rules, source: entry.source, createdAt: entry.createdAt, updatedAt: entry.updatedAt };
|
|
26
43
|
if (entry.backend !== undefined) {
|
|
@@ -51,18 +68,42 @@ const rulesWords = (rules) => {
|
|
|
51
68
|
const field = rules.kind === "password" && !rules.anyField ? "password fields only" : "any field";
|
|
52
69
|
return `${programs}; ${field}`;
|
|
53
70
|
};
|
|
54
|
-
const withEntry = (doc, entry) => ({ ...doc, entries: { ...doc.entries, [entry.name]: entry } });
|
|
55
71
|
export class Keyring {
|
|
56
72
|
deps;
|
|
57
73
|
#file;
|
|
58
74
|
#keys;
|
|
59
75
|
#unlocked;
|
|
60
76
|
#nonces = new NonceBook();
|
|
61
|
-
#remembered = new Map();
|
|
62
77
|
#lock = serialLock();
|
|
63
78
|
#loaded = false;
|
|
79
|
+
#access;
|
|
80
|
+
#captures;
|
|
81
|
+
#appOps;
|
|
82
|
+
#fills;
|
|
83
|
+
#needs;
|
|
84
|
+
#phones;
|
|
85
|
+
#askpass;
|
|
64
86
|
constructor(deps) {
|
|
65
87
|
this.deps = deps;
|
|
88
|
+
this.#access = {
|
|
89
|
+
deps,
|
|
90
|
+
file: () => this.#file,
|
|
91
|
+
keys: async () => await this.#load(),
|
|
92
|
+
held: () => this.#held(),
|
|
93
|
+
open: async (file, request) => await this.#open(file, request),
|
|
94
|
+
commit: async (opened, pending) => await this.#commit(opened, pending),
|
|
95
|
+
writeOpen: async (open) => await this.#writeOpen(open),
|
|
96
|
+
record: async (record) => await this.#record(record),
|
|
97
|
+
valueOf: async (doc, entry) => await this.#valueOf(doc, entry),
|
|
98
|
+
fetchKeys: async (sandbox) => await this.#fetchKeys(sandbox),
|
|
99
|
+
typeInto: async (place, seen, value, asked) => await this.#typeInto(place, seen, value, asked),
|
|
100
|
+
};
|
|
101
|
+
this.#captures = new Captures(this.#access);
|
|
102
|
+
this.#appOps = new AppOps(this.#access);
|
|
103
|
+
this.#fills = new Fills(this.#access, this.#captures);
|
|
104
|
+
this.#needs = new Needs(this.#access);
|
|
105
|
+
this.#phones = new Phones(this.#access);
|
|
106
|
+
this.#askpass = new Askpass(this.#access, this.#captures);
|
|
66
107
|
}
|
|
67
108
|
async #load() {
|
|
68
109
|
if (!this.#loaded || this.#keys === undefined) {
|
|
@@ -81,7 +122,7 @@ export class Keyring {
|
|
|
81
122
|
#forget() {
|
|
82
123
|
this.#unlocked?.key.fill(0);
|
|
83
124
|
this.#unlocked = undefined;
|
|
84
|
-
this.#
|
|
125
|
+
this.#askpass.forget();
|
|
85
126
|
this.deps.backends.forget();
|
|
86
127
|
}
|
|
87
128
|
#opener(file) {
|
|
@@ -96,23 +137,42 @@ export class Keyring {
|
|
|
96
137
|
if (held !== undefined && !request.person) {
|
|
97
138
|
return { doc: held.doc, key: held.key, asked: false, how: undefined };
|
|
98
139
|
}
|
|
99
|
-
const
|
|
140
|
+
const picked = await this.#choose(request);
|
|
141
|
+
if (!picked.ok) {
|
|
142
|
+
return picked;
|
|
143
|
+
}
|
|
144
|
+
const chosen = picked.choice;
|
|
145
|
+
const label = request.choices?.find((choice) => choice.id === chosen)?.label;
|
|
146
|
+
const message = label === undefined ? request.message : `${request.message} You chose: ${label}.`;
|
|
147
|
+
const answer = await this.deps.consent.ask({ title: request.title, message, unlock: held === undefined, opener: this.#opener(file) });
|
|
100
148
|
if (!answer.ok) {
|
|
101
149
|
return fromConsent(answer);
|
|
102
150
|
}
|
|
103
151
|
if (held !== undefined) {
|
|
104
|
-
return { doc: held.doc, key: held.key, asked: true, how: answer.how };
|
|
152
|
+
return { doc: held.doc, key: held.key, asked: true, how: answer.how, choice: chosen };
|
|
105
153
|
}
|
|
106
154
|
if (answer.vaultKey === undefined) {
|
|
107
|
-
return refuse("locked", "
|
|
155
|
+
return refuse("locked", "Passwords could not be opened with that answer: unlock it with its recovery key.");
|
|
108
156
|
}
|
|
109
157
|
try {
|
|
110
|
-
return { doc: openDocument(answer.vaultKey, file.sealed), key: answer.vaultKey, asked: true, how: answer.how };
|
|
158
|
+
return { doc: openDocument(answer.vaultKey, file.sealed), key: answer.vaultKey, asked: true, how: answer.how, choice: chosen };
|
|
111
159
|
}
|
|
112
160
|
catch (error) {
|
|
113
161
|
return refuse("unavailable", error instanceof Error ? error.message : "The vault could not be opened.");
|
|
114
162
|
}
|
|
115
163
|
}
|
|
164
|
+
async #choose(request) {
|
|
165
|
+
const choices = request.choices;
|
|
166
|
+
const choose = this.deps.consent.choose;
|
|
167
|
+
if (choices === undefined || choices.length === 0 || choose === undefined) {
|
|
168
|
+
return { ok: true, choice: choices?.[0]?.id };
|
|
169
|
+
}
|
|
170
|
+
const picked = await choose({ title: request.title, message: request.message, choices });
|
|
171
|
+
if (picked.kind === "cancelled") {
|
|
172
|
+
return refuse("declined", "The person at the computer said no.");
|
|
173
|
+
}
|
|
174
|
+
return { ok: true, choice: picked.kind === "chosen" ? picked.id : choices[0]?.id };
|
|
175
|
+
}
|
|
116
176
|
#copiesOf(doc, entries) {
|
|
117
177
|
const copies = new Map();
|
|
118
178
|
for (const entry of entries) {
|
|
@@ -142,6 +202,7 @@ export class Keyring {
|
|
|
142
202
|
pendingInbox: pending?.pendingInbox ?? file.open.pendingInbox,
|
|
143
203
|
pendingSync: pending?.pendingSync ?? file.open.pendingSync,
|
|
144
204
|
};
|
|
205
|
+
this.#openExtras(open, doc, file.open, pending);
|
|
145
206
|
const next = { ...file, sealed: sealDocument(opened.key, doc), open };
|
|
146
207
|
await this.deps.vault.write(next);
|
|
147
208
|
this.#file = next;
|
|
@@ -152,6 +213,27 @@ export class Keyring {
|
|
|
152
213
|
this.#unlocked = { ...this.#unlocked, doc };
|
|
153
214
|
}
|
|
154
215
|
}
|
|
216
|
+
#openExtras(open, doc, before, pending) {
|
|
217
|
+
if (doc.settings !== undefined) {
|
|
218
|
+
open.settings = doc.settings;
|
|
219
|
+
}
|
|
220
|
+
const phones = Object.entries(doc.phones ?? {}).map(([device, pin]) => ({ device, label: pin.keys.label, pairedAt: pin.pairedAt }));
|
|
221
|
+
if (phones.length > 0) {
|
|
222
|
+
open.phones = phones;
|
|
223
|
+
}
|
|
224
|
+
const captures = pending?.pendingCaptures ?? before.pendingCaptures;
|
|
225
|
+
if (captures !== undefined && captures.length > 0) {
|
|
226
|
+
open.pendingCaptures = captures;
|
|
227
|
+
}
|
|
228
|
+
const needs = pending?.pendingNeeds ?? before.pendingNeeds;
|
|
229
|
+
if (needs !== undefined && needs.length > 0) {
|
|
230
|
+
open.pendingNeeds = needs;
|
|
231
|
+
}
|
|
232
|
+
const seen = pending?.pendingSeen ?? before.pendingSeen;
|
|
233
|
+
if (seen !== undefined && seen.length > 0) {
|
|
234
|
+
open.pendingSeen = seen;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
155
237
|
async #writeOpen(open) {
|
|
156
238
|
const file = this.#file;
|
|
157
239
|
if (file !== undefined) {
|
|
@@ -188,7 +270,10 @@ export class Keyring {
|
|
|
188
270
|
}
|
|
189
271
|
async handle(request) {
|
|
190
272
|
await this.#load();
|
|
191
|
-
|
|
273
|
+
if (isRelayed(request)) {
|
|
274
|
+
return await this.#relayed(request);
|
|
275
|
+
}
|
|
276
|
+
return isApp(request) ? await this.#app(request) : await this.#local(request);
|
|
192
277
|
}
|
|
193
278
|
async #local(request) {
|
|
194
279
|
switch (request.op) {
|
|
@@ -217,11 +302,49 @@ export class Keyring {
|
|
|
217
302
|
case "backend":
|
|
218
303
|
return await this.#lock(async () => await this.backend(request.config));
|
|
219
304
|
case "askpass":
|
|
220
|
-
return await this.askpass(request.prompt, request.parent);
|
|
305
|
+
return await this.#askpass.askpass(request.prompt, request.parent);
|
|
306
|
+
case "git-credential":
|
|
307
|
+
return await this.#lock(async () => await this.#askpass.gitCredential(request.action, request.url, request.username));
|
|
221
308
|
case "consent-upgrade":
|
|
222
309
|
return await this.consentUpgrade(request.version);
|
|
223
310
|
}
|
|
224
311
|
}
|
|
312
|
+
async #app(request) {
|
|
313
|
+
switch (request.op) {
|
|
314
|
+
case "capture-batch":
|
|
315
|
+
return await this.#lock(async () => await this.#captures.batch(request.items, request.from));
|
|
316
|
+
case "undo-capture":
|
|
317
|
+
return await this.#lock(async () => await this.#captures.undo(request.name, request.at));
|
|
318
|
+
case "seen":
|
|
319
|
+
return await this.#lock(async () => await this.#captures.seen(request.names));
|
|
320
|
+
case "reveal":
|
|
321
|
+
return await this.#appOps.reveal(request.name, request.version);
|
|
322
|
+
case "copy":
|
|
323
|
+
return await this.#appOps.copy(request.name, request.field, request.clearAfterSeconds);
|
|
324
|
+
case "history":
|
|
325
|
+
return await this.#appOps.history(request.name);
|
|
326
|
+
case "restore":
|
|
327
|
+
return await this.#lock(async () => await this.#appOps.restore(request.name, request.version));
|
|
328
|
+
case "settings": {
|
|
329
|
+
const phones = this.#file?.open.phones;
|
|
330
|
+
return phones === undefined ? { ok: true, settings: this.#appOps.settings() } : { ok: true, settings: this.#appOps.settings(), phones };
|
|
331
|
+
}
|
|
332
|
+
case "set-settings":
|
|
333
|
+
return await this.#lock(async () => await this.#appOps.setSettings(request.patch));
|
|
334
|
+
case "activity":
|
|
335
|
+
return await this.#appOps.activity(request.name, request.limit);
|
|
336
|
+
case "fill-window":
|
|
337
|
+
return await this.#lock(async () => await this.#fills.fillWindow(request.name));
|
|
338
|
+
case "fill-value":
|
|
339
|
+
return await this.#lock(async () => await this.#appOps.fillValue(request.origin, request.name));
|
|
340
|
+
case "candidates":
|
|
341
|
+
return this.#appOps.candidates(request.origin);
|
|
342
|
+
case "needs":
|
|
343
|
+
return this.#needs.needs();
|
|
344
|
+
case "answer-need":
|
|
345
|
+
return await this.#lock(async () => await this.#needs.answerNeed(request.id, request.answer, request.name, request.sealed, request.at));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
225
348
|
async #relayed(request) {
|
|
226
349
|
switch (request.op) {
|
|
227
350
|
case "shares":
|
|
@@ -242,6 +365,14 @@ export class Keyring {
|
|
|
242
365
|
return await this.#lock(async () => await this.syncOutbox());
|
|
243
366
|
case "sync-deliver":
|
|
244
367
|
return await this.#lock(async () => await this.syncDeliver(request.deliver.items));
|
|
368
|
+
case "capture":
|
|
369
|
+
return await this.#lock(async () => await this.#captures.capture(request.item, request.sandbox));
|
|
370
|
+
case "need":
|
|
371
|
+
return await this.#needs.need(request.sandbox, request.need);
|
|
372
|
+
case "phone-pair":
|
|
373
|
+
return await this.#lock(async () => await this.#phones.pair(request.sandbox, request.phone));
|
|
374
|
+
case "phone-fill":
|
|
375
|
+
return await this.#lock(async () => await this.#phones.fill(request.sandbox, request.fill));
|
|
245
376
|
}
|
|
246
377
|
}
|
|
247
378
|
async state() {
|
|
@@ -255,7 +386,11 @@ export class Keyring {
|
|
|
255
386
|
locked: held === undefined,
|
|
256
387
|
unlock: helloReady ? ["hello", "passphrase"] : ["passphrase"],
|
|
257
388
|
pendingInbox: file?.open.pendingInbox.length ?? 0,
|
|
389
|
+
pendingCaptures: file?.open.pendingCaptures?.length ?? 0,
|
|
258
390
|
};
|
|
391
|
+
if (file !== undefined) {
|
|
392
|
+
state.remember = (held === undefined ? file.open.settings : held.doc.settings)?.remember ?? DEFAULT_SETTINGS.remember;
|
|
393
|
+
}
|
|
259
394
|
if (this.#unlocked !== undefined && held !== undefined) {
|
|
260
395
|
state.unlockedUntil = this.#unlocked.until;
|
|
261
396
|
}
|
|
@@ -296,10 +431,12 @@ export class Keyring {
|
|
|
296
431
|
return refuse("locked", NOT_SET_UP);
|
|
297
432
|
}
|
|
298
433
|
const pending = file.open.pendingInbox;
|
|
434
|
+
const captured = file.open.pendingCaptures?.length ?? 0;
|
|
299
435
|
const waiting = pending.length === 0 ? "" : ` Waiting to be taken in: ${pending.map((item) => `${item.item.name} from ${hostOf(item.item.sandbox)}`).join(", ")}.`;
|
|
436
|
+
const saving = captured === 0 ? "" : ` And save ${String(captured)} new ${captured === 1 ? "password" : "passwords"}.`;
|
|
300
437
|
const opened = await this.#open(file, {
|
|
301
438
|
title: "Unlock the Intentic keyring",
|
|
302
|
-
message: `For ${String(minutes)} minutes, entries shared "while you're here" go to their sandboxes without asking.${waiting}`,
|
|
439
|
+
message: `For ${String(minutes)} minutes, entries shared "while you're here" go to their sandboxes without asking.${waiting}${saving}`,
|
|
303
440
|
person: true,
|
|
304
441
|
});
|
|
305
442
|
if ("ok" in opened) {
|
|
@@ -314,7 +451,9 @@ export class Keyring {
|
|
|
314
451
|
}
|
|
315
452
|
async #takeInPending(opened) {
|
|
316
453
|
const file = this.#file;
|
|
317
|
-
|
|
454
|
+
const captures = file?.open.pendingCaptures ?? [];
|
|
455
|
+
const seen = file?.open.pendingSeen ?? [];
|
|
456
|
+
if (file === undefined || (file.open.pendingInbox.length === 0 && file.open.pendingSync.length === 0 && captures.length === 0 && seen.length === 0)) {
|
|
318
457
|
return;
|
|
319
458
|
}
|
|
320
459
|
let doc = opened.doc;
|
|
@@ -328,12 +467,25 @@ export class Keyring {
|
|
|
328
467
|
}
|
|
329
468
|
}
|
|
330
469
|
doc = this.#mergeSync(doc, file.open.pendingSync).doc;
|
|
331
|
-
|
|
470
|
+
doc = seenIn(this.#captures.takeIn(doc, captures), new Set(seen));
|
|
471
|
+
await this.#commit({ doc, key: opened.key }, { pendingInbox: [], pendingSync: [], pendingCaptures: [], pendingSeen: [] });
|
|
332
472
|
}
|
|
333
473
|
list() {
|
|
334
474
|
const held = this.#held();
|
|
335
475
|
const open = this.#file?.open;
|
|
336
|
-
|
|
476
|
+
if (held === undefined) {
|
|
477
|
+
return { ok: true, entries: open?.entries ?? [], backends: open?.backends ?? [] };
|
|
478
|
+
}
|
|
479
|
+
const live = liveEntries(held.doc);
|
|
480
|
+
const health = healthOf(live);
|
|
481
|
+
const entries = live.map((entry) => {
|
|
482
|
+
const info = entryInfoOf(entry);
|
|
483
|
+
const verdict = health.get(entry.name);
|
|
484
|
+
if (verdict !== undefined) {
|
|
485
|
+
info.health = verdict;
|
|
486
|
+
}
|
|
487
|
+
return info;
|
|
488
|
+
});
|
|
337
489
|
return { ok: true, entries, backends: open?.backends ?? [] };
|
|
338
490
|
}
|
|
339
491
|
#sentValue(doc, name, sealed) {
|
|
@@ -575,6 +727,7 @@ export class Keyring {
|
|
|
575
727
|
const sealed = target.target.kind === "relay"
|
|
576
728
|
? seal(target.target.sealTo, value.value, { kind: "type", sandbox: grant.sandbox, secret: grant.secret, nonce: grant.nonce })
|
|
577
729
|
: seal(pin.seal, value.value, { kind: "release", sandbox: grant.sandbox, secret: grant.secret, nonce: grant.nonce });
|
|
730
|
+
await this.#markUsed(granted.opened, granted.entry);
|
|
578
731
|
await this.#record({
|
|
579
732
|
op: "release",
|
|
580
733
|
outcome: "granted",
|
|
@@ -657,7 +810,17 @@ export class Keyring {
|
|
|
657
810
|
return refuse("guard", onFile.verdict === "refuse" ? onFile.reason : "The entry on file differs from what this use was judged against: the sandbox asks again.");
|
|
658
811
|
}
|
|
659
812
|
const value = await this.#valueOf(granted.opened.doc, granted.entry);
|
|
660
|
-
|
|
813
|
+
if (!value.ok) {
|
|
814
|
+
return value;
|
|
815
|
+
}
|
|
816
|
+
const typed = await this.#typeInto(place, seen, value.value, granted.opened.asked);
|
|
817
|
+
if (typed.ok) {
|
|
818
|
+
await this.#markUsed(granted.opened, granted.entry);
|
|
819
|
+
}
|
|
820
|
+
return typed;
|
|
821
|
+
}
|
|
822
|
+
async #markUsed(opened, entry) {
|
|
823
|
+
await this.#commit({ doc: withEntry(opened.doc, usedEntry(entry, this.deps.now())), key: opened.key });
|
|
661
824
|
}
|
|
662
825
|
async #typeInto(place, seen, value, asked) {
|
|
663
826
|
let now = seen;
|
|
@@ -831,42 +994,6 @@ export class Keyring {
|
|
|
831
994
|
}
|
|
832
995
|
return { doc: { ...doc, entries }, accepted };
|
|
833
996
|
}
|
|
834
|
-
async askpass(prompt, parent) {
|
|
835
|
-
const file = this.#file;
|
|
836
|
-
if (file === undefined) {
|
|
837
|
-
return refuse("locked", NOT_SET_UP);
|
|
838
|
-
}
|
|
839
|
-
const question = parseAskpassPrompt(prompt);
|
|
840
|
-
const shown = file.open.entries.find((entry) => askpassMatch(question, entry.askpass) !== undefined);
|
|
841
|
-
const matcher = shown === undefined ? undefined : askpassMatch(question, shown.askpass);
|
|
842
|
-
if (shown === undefined || matcher === undefined) {
|
|
843
|
-
return refuse("unknown", `No keyring entry answers "${question.prompt}": \`intentic-machine keyring edit NAME --askpass ${question.matchers[0] ?? "prompt:…"}\` adds one.`);
|
|
844
|
-
}
|
|
845
|
-
if (question.wants === "username") {
|
|
846
|
-
return shown.username === undefined ? refuse("unknown", `${shown.name} has no user name to answer with.`) : { ok: true, value: shown.username };
|
|
847
|
-
}
|
|
848
|
-
const now = this.deps.now();
|
|
849
|
-
const remembered = this.#held() !== undefined && rememberedFresh(this.#remembered.get(matcher), shown.askpassRememberMinutes, now);
|
|
850
|
-
const asker = parent ?? "A program";
|
|
851
|
-
const opened = await this.#open(file, { title: `Answer ${asker}'s prompt?`, message: `${asker} asks: "${question.prompt}". The keyring answers with ${shown.name}.`, person: !remembered });
|
|
852
|
-
if ("ok" in opened) {
|
|
853
|
-
await this.#record({ op: "askpass", outcome: "refused", code: opened.code, secret: shown.name, target: matcher });
|
|
854
|
-
return opened;
|
|
855
|
-
}
|
|
856
|
-
const entry = opened.doc.entries[shown.name];
|
|
857
|
-
if (entry === undefined || entry.deleted === true || askpassMatch(question, entry.rules.askpass) === undefined) {
|
|
858
|
-
return refuse("unknown", `No keyring entry answers "${question.prompt}".`);
|
|
859
|
-
}
|
|
860
|
-
const value = await this.#valueOf(opened.doc, entry);
|
|
861
|
-
if (!value.ok) {
|
|
862
|
-
return value;
|
|
863
|
-
}
|
|
864
|
-
if (entry.rules.askpassRememberMinutes > 0) {
|
|
865
|
-
this.#remembered.set(matcher, now);
|
|
866
|
-
}
|
|
867
|
-
await this.#record({ op: "askpass", outcome: "granted", secret: entry.name, target: matcher, asked: opened.asked, program: parent });
|
|
868
|
-
return { ok: true, value: value.value };
|
|
869
|
-
}
|
|
870
997
|
async consentUpgrade(version) {
|
|
871
998
|
const file = this.#file;
|
|
872
999
|
const answer = await this.deps.consent.ask({
|