@dotrino/vaultd 0.38.0 → 0.46.2
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 +4 -1
- package/lib/src/admin.js +4 -0
- package/lib/src/atrest.js +0 -0
- package/lib/src/enroll.js +12 -3
- package/lib/src/protocol.js +10 -0
- package/lib/src/service.js +288 -52
- package/package.json +6 -6
- package/src/ctl.js +273 -17
- package/src/daemon.js +161 -14
- package/src/manager.js +6 -0
- package/src/profiles.js +82 -9
- package/src/sealKey.js +80 -0
- package/src/sealer.js +170 -0
- package/src/secretsStore.js +777 -91
- package/src/store.js +3 -1
- package/src/tui/app.js +119 -9
- package/src/tui/i18n.js +31 -6
- package/src/vault.js +647 -30
- package/src/vaultControl.js +50 -14
package/src/vaultControl.js
CHANGED
|
@@ -224,8 +224,12 @@ export const renameProfile = (profile, name) => profileOp('rename', { profile, n
|
|
|
224
224
|
export const removeProfile = (profile) => profileOp('rm', { profile })
|
|
225
225
|
export const unlockProfile = (profile, password) => profileOp('unlock', { profile, password })
|
|
226
226
|
export const lockProfile = (profile) => profileOp('lock', { profile })
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
// `current`: la contraseña que YA tenía el perfil. Hace falta para abrir la copia
|
|
228
|
+
// maestra de los secretos y volver a cerrarla con la nueva — sin ese paso, cambiar la
|
|
229
|
+
// contraseña dejaría las variables privadas ilegibles.
|
|
230
|
+
export const setProfilePassword = (profile, password, current) => profileOp('password-set', { profile, password, ...(current ? { current } : {}) })
|
|
231
|
+
// Quitarla también la pide: se re-sella a la llave de la máquina antes de que se vaya.
|
|
232
|
+
export const removeProfilePassword = (profile, password) => profileOp('password-rm', { profile, password })
|
|
229
233
|
|
|
230
234
|
// ---------------------------------------------------------------------------
|
|
231
235
|
// Volcado de dispositivos + secretos de un perfil
|
|
@@ -431,6 +435,9 @@ export async function listSecrets (profile) {
|
|
|
431
435
|
* callado, y un «guardado» que no guardó nada es la peor forma de fallar en esto.
|
|
432
436
|
*/
|
|
433
437
|
async function secretOp (req, profile, check) {
|
|
438
|
+
// `req` puede llevar la CONTRASEÑA del perfil (las operaciones que sellan la
|
|
439
|
+
// necesitan para abrir la copia maestra). Vive un instante en un archivo 0600 del
|
|
440
|
+
// dir del vault y el daemon lo borra al consumirlo.
|
|
434
441
|
requireAlive() // el VALOR es secreto: no escribirlo si el daemon está caído
|
|
435
442
|
rm(F.secretsList)
|
|
436
443
|
await writeReq(F.secretReq, req, profile)
|
|
@@ -445,6 +452,35 @@ async function secretOp (req, profile, check) {
|
|
|
445
452
|
return out
|
|
446
453
|
}
|
|
447
454
|
|
|
455
|
+
/**
|
|
456
|
+
* VER el valor de una privada. Es lo único que pide la contraseña desde v5 (§8.3), y en
|
|
457
|
+
* esta máquina no hay otra manera: la llave de este aparato es una llave que vive en
|
|
458
|
+
* este mismo disco, así que si abriera sin frase, una copia del disco abriría todo.
|
|
459
|
+
*
|
|
460
|
+
* El valor viaja por el volcado —igual que el de las públicas— y quien lo lee borra el
|
|
461
|
+
* archivo en el acto; no se queda esperando a que alguien copie la carpeta.
|
|
462
|
+
*/
|
|
463
|
+
export async function revealSecret (owner, key, profile, password) {
|
|
464
|
+
requireAlive()
|
|
465
|
+
rm(F.secretsList)
|
|
466
|
+
await writeReq(F.secretReq, { op: 'reveal', owner, key, ...(password ? { password } : {}) }, profile)
|
|
467
|
+
await writeReq(F.dumpReq, {}, profile)
|
|
468
|
+
signalOrCleanup('SIGUSR2', [F.secretReq, F.dumpReq])
|
|
469
|
+
const d = await waitFor(F.secretsList)
|
|
470
|
+
rm(F.secretsList)
|
|
471
|
+
if (!d) throw coded('the daemon did not reply', 'NO_REPLY')
|
|
472
|
+
assertOpen(d)
|
|
473
|
+
if (d.revealed?.owner !== owner || d.revealed?.key !== key) {
|
|
474
|
+
throw coded('the daemon did not return the value (check the service logs)', 'NOT_REVEALED')
|
|
475
|
+
}
|
|
476
|
+
return d.revealed.value
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** SALDA lo que quedó a deber (heredar lo viejo, rotar de verdad). Pide la contraseña. */
|
|
480
|
+
export function settleSecrets (profile, password) {
|
|
481
|
+
return secretOp({ op: 'settle', ...(password ? { password } : {}) }, profile, () => {})
|
|
482
|
+
}
|
|
483
|
+
|
|
448
484
|
const keysOf = (out, pub) => (out.dev.find((x) => x.pub === pub)?.keys) || []
|
|
449
485
|
/** ¿Está esa clave en la lista? Las listas traen `{key, public}`, nunca el valor. */
|
|
450
486
|
const has = (list, key) => (list || []).some((x) => x.key === key)
|
|
@@ -455,8 +491,8 @@ const visibilityOf = (list, key) => !!(list || []).find((x) => x.key === key)?.p
|
|
|
455
491
|
* `isPublic` opcional: sin decir nada conserva la visibilidad que ya tenía (y una nueva
|
|
456
492
|
* nace privada, o sea que su valor no sale de la máquina de la bóveda).
|
|
457
493
|
*/
|
|
458
|
-
export function setSecret (ns, key, value, profile, isPublic) {
|
|
459
|
-
return secretOp({ op: 'set', ns, key, value, ...(isPublic === undefined ? {} : { public: !!isPublic }) }, profile, (out) => {
|
|
494
|
+
export function setSecret (ns, key, value, profile, isPublic, password) {
|
|
495
|
+
return secretOp({ op: 'set', ns, key, value, ...(password ? { password } : {}), ...(isPublic === undefined ? {} : { public: !!isPublic }) }, profile, (out) => {
|
|
460
496
|
if (!has(out.ns[ns], key)) throw coded('the daemon did not apply the change (check the service logs)', 'NOT_APPLIED')
|
|
461
497
|
})
|
|
462
498
|
}
|
|
@@ -472,14 +508,14 @@ export function deleteSecret (ns, key, profile) {
|
|
|
472
508
|
* Cambia SOLO quién puede ver el valor: `public` deja que la consola remota lo vea,
|
|
473
509
|
* `private` lo encierra en esta máquina. No toca el valor (ni hace falta conocerlo).
|
|
474
510
|
*/
|
|
475
|
-
export function setSecretVisibility (ns, key, isPublic, profile) {
|
|
476
|
-
return secretOp({ op: 'vis', ns, key, public: !!isPublic }, profile, (out) => {
|
|
511
|
+
export function setSecretVisibility (ns, key, isPublic, profile, password) {
|
|
512
|
+
return secretOp({ op: 'vis', ns, key, public: !!isPublic, ...(password ? { password } : {}) }, profile, (out) => {
|
|
477
513
|
if (visibilityOf(out.ns[ns], key) !== !!isPublic) throw coded('the daemon did not apply the change (check the service logs)', 'NOT_APPLIED')
|
|
478
514
|
})
|
|
479
515
|
}
|
|
480
516
|
|
|
481
|
-
export function setDeviceSecretVisibility (pub, key, isPublic, profile) {
|
|
482
|
-
return secretOp({ op: 'dev-vis', pub, key, public: !!isPublic }, profile, (out) => {
|
|
517
|
+
export function setDeviceSecretVisibility (pub, key, isPublic, profile, password) {
|
|
518
|
+
return secretOp({ op: 'dev-vis', pub, key, public: !!isPublic, ...(password ? { password } : {}) }, profile, (out) => {
|
|
483
519
|
if (visibilityOf(keysOf(out, pub), key) !== !!isPublic) throw coded('the daemon did not apply the change (check the service logs)', 'NOT_APPLIED')
|
|
484
520
|
})
|
|
485
521
|
}
|
|
@@ -495,16 +531,16 @@ export function setDeviceSecretVisibility (pub, key, isPublic, profile) {
|
|
|
495
531
|
*
|
|
496
532
|
* @param {Array<{op:'set'|'rm', key:string, value?:string, public?:boolean}>} items
|
|
497
533
|
*/
|
|
498
|
-
export function applySecrets (ns, items, profile) {
|
|
499
|
-
return secretOp({ op: 'batch', ns, items }, profile, (out) => {
|
|
534
|
+
export function applySecrets (ns, items, profile, password) {
|
|
535
|
+
return secretOp({ op: 'batch', ns, items, ...(password ? { password } : {}) }, profile, (out) => {
|
|
500
536
|
const missing = items.find((it) => (it.op === 'rm' ? has(out.ns[ns], it.key) : !has(out.ns[ns], it.key)))
|
|
501
537
|
if (missing) throw coded('the daemon did not apply the change (check the service logs)', 'NOT_APPLIED')
|
|
502
538
|
})
|
|
503
539
|
}
|
|
504
540
|
|
|
505
541
|
/** Lo mismo para el cajón de UN aparato. */
|
|
506
|
-
export function applyDeviceSecrets (pub, items, profile) {
|
|
507
|
-
return secretOp({ op: 'batch', pub, items }, profile, (out) => {
|
|
542
|
+
export function applyDeviceSecrets (pub, items, profile, password) {
|
|
543
|
+
return secretOp({ op: 'batch', pub, items, ...(password ? { password } : {}) }, profile, (out) => {
|
|
508
544
|
const missing = items.find((it) => (it.op === 'rm' ? has(keysOf(out, pub), it.key) : !has(keysOf(out, pub), it.key)))
|
|
509
545
|
if (missing) throw coded('the daemon did not apply the change (check the service logs)', 'NOT_APPLIED')
|
|
510
546
|
})
|
|
@@ -523,8 +559,8 @@ export async function deleteScope (ns, profile) {
|
|
|
523
559
|
* misma que trae el acta). Solo la lee ese aparato, y le gana a la del scope con el
|
|
524
560
|
* mismo nombre.
|
|
525
561
|
*/
|
|
526
|
-
export function setDeviceSecret (pub, key, value, profile, isPublic) {
|
|
527
|
-
return secretOp({ op: 'dev-set', pub, key, value, ...(isPublic === undefined ? {} : { public: !!isPublic }) }, profile, (out) => {
|
|
562
|
+
export function setDeviceSecret (pub, key, value, profile, isPublic, password) {
|
|
563
|
+
return secretOp({ op: 'dev-set', pub, key, value, ...(password ? { password } : {}), ...(isPublic === undefined ? {} : { public: !!isPublic }) }, profile, (out) => {
|
|
528
564
|
if (!has(keysOf(out, pub), key)) throw coded('the daemon did not apply the change (check the service logs)', 'NOT_APPLIED')
|
|
529
565
|
})
|
|
530
566
|
}
|