@dotrino/vault 0.18.0 → 0.19.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/package.json +1 -1
- package/src/admin.js +8 -0
- package/src/enroll.js +24 -1
- package/src/index.js +4 -2
- package/src/protocol.js +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vault",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Usa ESTE dispositivo (navegador) como bóveda/CA del ecosistema Dotrino: atiende enrolamientos por el proxy y firma certificados de delegación a tus máquinas. Incluye el cliente de SERVICIO (Node): un proyecto se enrola una vez y jala sus credenciales del vault en vez del .env (`import '@dotrino/vault/config'`).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/admin.js
CHANGED
|
@@ -127,7 +127,15 @@ export function createAdminDesk ({
|
|
|
127
127
|
return { ok: true, result: { ok: true } }
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// Quitar un dispositivo: por `sub` se le retiran TODOS sus certificados. Con
|
|
131
|
+
// `certNonce` a secas solo cae ese papel, y el aparato puede tener otro vigente.
|
|
130
132
|
if (data.op === 'revoke') {
|
|
133
|
+
if (data.sub) {
|
|
134
|
+
const r = await desk.revokeDevice(String(data.sub))
|
|
135
|
+
audit('admin.revoke-device', { by, certs: r?.nonces?.length ?? null })
|
|
136
|
+
await notify('revoked', { by })
|
|
137
|
+
return { ok: true, result: r || { ok: true } }
|
|
138
|
+
}
|
|
131
139
|
const r = await desk.revoke(String(data.certNonce || ''))
|
|
132
140
|
audit('admin.revoke', { by, certNonce: data.certNonce })
|
|
133
141
|
await notify('revoked', { certNonce: data.certNonce, by })
|
package/src/enroll.js
CHANGED
|
@@ -441,9 +441,32 @@ export function createEnrollDesk ({
|
|
|
441
441
|
return res
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
/**
|
|
445
|
+
* QUITA EL DISPOSITIVO entero: retira TODOS sus certificados vigentes y le manda una
|
|
446
|
+
* sola orden de autoborrado. `revoke(nonce)` retira un papel, y un aparato puede tener
|
|
447
|
+
* varios (una renovación dejaba vivo el anterior): quitarle uno no lo echaba, y podía
|
|
448
|
+
* quedarse dentro justo con el que llevaba `vault:admin`.
|
|
449
|
+
*/
|
|
450
|
+
async function revokeDevice (sub) {
|
|
451
|
+
if (!sub) throw new Error('sub (device pubkey) required')
|
|
452
|
+
const { issued } = await identity.listDelegations()
|
|
453
|
+
const mine = (issued || []).filter((d) => d.sub === sub)
|
|
454
|
+
audit('revoke-device', { certs: mine.length })
|
|
455
|
+
// Si el núcleo no trae `revokeDevice` (bóveda vieja), se cae a retirarlos uno a uno.
|
|
456
|
+
const res = identity.revokeDevice
|
|
457
|
+
? await identity.revokeDevice(sub)
|
|
458
|
+
: { ok: true, nonces: await (async () => {
|
|
459
|
+
const done = []
|
|
460
|
+
for (const d of mine) { await identity.revokeDelegation(d.nonce); done.push(d.nonce) }
|
|
461
|
+
return done
|
|
462
|
+
})() }
|
|
463
|
+
await emitRevoke(sub, mine[0]?.nonce || null)
|
|
464
|
+
return res
|
|
465
|
+
}
|
|
466
|
+
|
|
444
467
|
return {
|
|
445
468
|
startPairing, stopPairing, handleEnroll, handleActaSealed, handleHello, approve, reject,
|
|
446
|
-
listPending, findPending, emitRevoke, revoke,
|
|
469
|
+
listPending, findPending, emitRevoke, revoke, revokeDevice,
|
|
447
470
|
get pendingCount () { return pending.size }
|
|
448
471
|
}
|
|
449
472
|
}
|
package/src/index.js
CHANGED
|
@@ -155,14 +155,16 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
|
|
|
155
155
|
if (typeof d.ts !== 'number' || Math.abs(Date.now() - d.ts) > FRESH_WINDOW_MS) return
|
|
156
156
|
const chk = await verifyChain({ data: d, signature: p.signature, cert: p.cert, trustedIssuer: iss })
|
|
157
157
|
if (!chk.ok) return send(from, { type: MSG.ERROR, error: 'no autorizado: ' + chk.reason })
|
|
158
|
-
const { issued, revoked } = await identity.listDelegations()
|
|
158
|
+
const { issued, revoked, revokedCerts } = await identity.listDelegations()
|
|
159
159
|
const devices = await Promise.all((issued || []).map(async (x) => ({
|
|
160
160
|
deviceId: x.sub ? await deviceIdOf(x.sub) : null, sub: x.sub || null,
|
|
161
161
|
label: x.label || '', scope: x.scope, exp: x.exp, nonce: x.nonce
|
|
162
162
|
})))
|
|
163
163
|
send(from, { type: MSG.DEVICES_RESULT, devices, revoked: (revoked || []).map((r) => r.nonce || r) })
|
|
164
164
|
// ¿el que consulta es una máquina revocada que reapareció? → re-emite el REVOKED firmado.
|
|
165
|
-
|
|
165
|
+
// Se busca en `revokedCerts`: desde que `issued` solo trae lo vigente, los retirados
|
|
166
|
+
// ya no están ahí y este aviso dejaba de dispararse (se caía en silencio).
|
|
167
|
+
const mine = (revokedCerts || issued || []).find((x) => x.sub === chk.device && x.revokedAt)
|
|
166
168
|
if (mine) desk.emitRevoke(chk.device, mine.nonce)
|
|
167
169
|
}
|
|
168
170
|
|
package/src/protocol.js
CHANGED
|
@@ -42,6 +42,8 @@ export const MSG = Object.freeze({
|
|
|
42
42
|
DEVICES: 'vault.devices', // dispositivo → vault: { data:{publickey,ts}, signature, cert }
|
|
43
43
|
DEVICES_RESULT: 'vault.devices.result', // vault → dispositivo: { devices, revoked }
|
|
44
44
|
RENEW: 'vault.renew', // dispositivo → vault: { data:{op,publickey,ts}, signature, cert }
|
|
45
|
+
RENOUNCE: 'vault.renounce', // dispositivo → vault: { record } (RENUNCIA firmada por el propio miembro)
|
|
46
|
+
RENOUNCE_RESULT: 'vault.renounce.result', // vault → dispositivo: { ok, seq }
|
|
45
47
|
RENEWED: 'vault.renewed', // vault → dispositivo: { cert } (cert fresco, misma sub-clave/scope)
|
|
46
48
|
SECRETS: 'vault.secrets', // servicio → vault: { data:{op,ns,ek,publickey,ts}, signature, cert }
|
|
47
49
|
SECRETS_RESULT: 'vault.secrets.result', // vault → servicio: { body:{op,ns,enc,ts}, signature } (enc SELLADO a ek; body firmado por la maestra)
|