@dotrino/identity 0.57.0 → 0.59.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/index.js +13 -0
- package/vault/acta.js +9 -3
- package/vault/core.js +21 -1
- package/vault/remote.js +15 -0
- package/vault/vendor/vault/protocol.js +2 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -404,6 +404,19 @@ export class Identity {
|
|
|
404
404
|
return this._call('vaultAdmin', { op, ...(args || {}) }, 20000)
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
+
/**
|
|
408
|
+
* PEDIDOS DE APROBACIÓN de la bóveda (cajones con `approval`): `op` = `approvals` ·
|
|
409
|
+
* `approve` · `deny` (estos dos con `{ id }`). Requiere `vault:approve` en el cert.
|
|
410
|
+
*/
|
|
411
|
+
async vaultApprovals (op, args) {
|
|
412
|
+
return this._call('vaultApprovals', { op, ...(args || {}) }, 20000)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** ¿El cert de este dispositivo le permite aprobar pedidos de la bóveda? */
|
|
416
|
+
async canApproveVault () {
|
|
417
|
+
return this._call('canApproveVault', {}, 20000)
|
|
418
|
+
}
|
|
419
|
+
|
|
407
420
|
/** ¿El cert de este dispositivo le permite administrar el perfil a distancia? */
|
|
408
421
|
async canAdminVault () {
|
|
409
422
|
return this._call('canAdminVault', {}, 20000)
|
package/vault/acta.js
CHANGED
|
@@ -53,10 +53,15 @@ const ACTA_LEIBLES = Object.freeze([1, 2])
|
|
|
53
53
|
* el rol de master, que no se delega. Así un dispositivo con `admin` robado hace daño
|
|
54
54
|
* acotado y **reversible** (se le revoca), en vez de poder dejarte fuera de tu cuenta.
|
|
55
55
|
*/
|
|
56
|
-
export const CAPS = Object.freeze(['sign', 'store', 'read', 'secrets', 'admin'])
|
|
56
|
+
export const CAPS = Object.freeze(['sign', 'store', 'read', 'secrets', 'admin', 'approve'])
|
|
57
57
|
|
|
58
58
|
/** Capacidades de un DISPOSITIVO (sin CN): acceso a todo lo del usuario. */
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* `approve` es el aparato que APRUEBA: cuando un cajón exige aprobación por uso, el
|
|
61
|
+
* vault le avisa y solo su firma libera los secretos (normalmente el teléfono). Como
|
|
62
|
+
* `admin`, no viaja en un QR: se concede a mano (`dotrino-vault caps <ID> +approve`).
|
|
63
|
+
*/
|
|
64
|
+
export const DEVICE_CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve'])
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* Lo que recibe un dispositivo recién emparejado. `admin` **no está**: no se
|
|
@@ -87,12 +92,13 @@ export function capScope (cap, cn = null) {
|
|
|
87
92
|
if (cap === 'store') return 'vault:store'
|
|
88
93
|
if (cap === 'read') return 'vault:read'
|
|
89
94
|
if (cap === 'admin') return 'vault:admin'
|
|
95
|
+
if (cap === 'approve') return 'vault:approve'
|
|
90
96
|
if (cap === 'secrets') return isValidCn(cn) ? 'vault:secrets:' + cn : null
|
|
91
97
|
return null
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
/** Compat: el mapa directo, para las capacidades de dispositivo. */
|
|
95
|
-
export const CAP_SCOPE = Object.freeze({ sign: 'vault:sign', store: 'vault:store', read: 'vault:read', admin: 'vault:admin' })
|
|
101
|
+
export const CAP_SCOPE = Object.freeze({ sign: 'vault:sign', store: 'vault:store', read: 'vault:read', admin: 'vault:admin', approve: 'vault:approve' })
|
|
96
102
|
|
|
97
103
|
const enc = (s) => new TextEncoder().encode(s)
|
|
98
104
|
const hex = (buf) => [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, '0')).join('')
|
package/vault/core.js
CHANGED
|
@@ -22,7 +22,7 @@ import { signDelegationWith, MAX_DELEGATION_MS, DEFAULT_DELEGATION_MS } from './
|
|
|
22
22
|
import * as Acta from './acta.js'
|
|
23
23
|
import * as Content from './content.js'
|
|
24
24
|
import { pubkeyId as pubkeyIdOf } from './capabilities.js'
|
|
25
|
-
import { enrollDevice as remoteEnroll, requestSign as remoteSign, requestStore as remoteStore, requestDevices as remoteDevices, requestRenew as remoteRenew, requestAdmin as remoteAdmin, requestRenounce as remoteRenounce, checkMembership as remoteCheck } from './remote.js'
|
|
25
|
+
import { enrollDevice as remoteEnroll, requestSign as remoteSign, requestStore as remoteStore, requestDevices as remoteDevices, requestRenew as remoteRenew, requestAdmin as remoteAdmin, requestApproval as remoteApproval, requestRenounce as remoteRenounce, checkMembership as remoteCheck } from './remote.js'
|
|
26
26
|
|
|
27
27
|
export const KEY_STORAGE = 'dotrino.identity.keypair'
|
|
28
28
|
export const ENC_KEY_STORAGE = 'dotrino.identity.enc-keypair'
|
|
@@ -1956,6 +1956,26 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
|
|
|
1956
1956
|
catch (e) { return handleVaultError(e) }
|
|
1957
1957
|
},
|
|
1958
1958
|
|
|
1959
|
+
/**
|
|
1960
|
+
* PEDIDOS DE APROBACIÓN: lo que le toca al teléfono cuando un cajón de la bóveda exige
|
|
1961
|
+
* el visto bueno por uso. `op`: `approvals` (listar) · `approve` · `deny` (con `id`).
|
|
1962
|
+
* Requiere `vault:approve` en el cert, que se concede a mano (`caps <ID> +aprueba`).
|
|
1963
|
+
*/
|
|
1964
|
+
async vaultApprovals ({ op, id } = {}) {
|
|
1965
|
+
const v = loadVaultCert(); const device = loadVaultDevice()
|
|
1966
|
+
if (!v?.cert || !device) throw new Error('this device is not paired with a vault')
|
|
1967
|
+
maybeRenewVaultCert()
|
|
1968
|
+
try { return await remoteApproval({ master: v.master, proxy: v.proxy, device, cert: v.cert, op, id, onRevoked: wipeVaultLink }) }
|
|
1969
|
+
catch (e) { return handleVaultError(e) }
|
|
1970
|
+
},
|
|
1971
|
+
|
|
1972
|
+
/** ¿Puede ESTE dispositivo aprobar pedidos? Mismo criterio que `canAdminVault`. */
|
|
1973
|
+
async canApproveVault () {
|
|
1974
|
+
if (certDesfasadoDelActa()) { try { await renovarCert() } catch (_) {} }
|
|
1975
|
+
const v = loadVaultCert()
|
|
1976
|
+
return !!v?.cert && (v.cert.scope || []).includes('vault:approve')
|
|
1977
|
+
},
|
|
1978
|
+
|
|
1959
1979
|
/**
|
|
1960
1980
|
* ¿Puede ESTE dispositivo administrar el perfil a distancia? Sale del scope del
|
|
1961
1981
|
* cert que le dio la bóveda, no de una preferencia: la interfaz pregunta para
|
package/vault/remote.js
CHANGED
|
@@ -395,6 +395,21 @@ export async function requestAdmin ({ master, proxy, device, cert, op, onRevoked
|
|
|
395
395
|
return res.result
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
/**
|
|
399
|
+
* PEDIDOS DE APROBACIÓN (cajones con `approval`): `approvals` (listar) · `approve` ·
|
|
400
|
+
* `deny`. Van por `vault.secrets`, firmados por un aparato cuyo cert lleve
|
|
401
|
+
* `vault:approve` — que, como `admin`, no se recibe al emparejar: se concede a mano.
|
|
402
|
+
* La bóveda contesta un cuerpo firmado por la maestra (`{ op, items | ok }`).
|
|
403
|
+
*/
|
|
404
|
+
export async function requestApproval ({ master, proxy, device, cert, op, id, onRevoked } = {}) {
|
|
405
|
+
const res = await vaultRpc({
|
|
406
|
+
master, proxy, device, cert, onRevoked,
|
|
407
|
+
sendType: MSG.SECRETS, okType: MSG.SECRETS_RESULT,
|
|
408
|
+
data: id ? { op, id } : { op }
|
|
409
|
+
})
|
|
410
|
+
return res.body
|
|
411
|
+
}
|
|
412
|
+
|
|
398
413
|
/**
|
|
399
414
|
* ¿Es AUTÉNTICO este `vault.admin.event` (entró o salió alguien del perfil)? Solo si va
|
|
400
415
|
* firmado por la maestra PINEADA. Un aviso sin firma no se muestra: si no, cualquiera
|
|
@@ -87,7 +87,8 @@ export const SCOPE = Object.freeze({
|
|
|
87
87
|
// Consola remota (docs/consola-remota.md): admitir y expulsar miembros a distancia.
|
|
88
88
|
// NO incluye cambiar permisos, traspasar el mando ni conceder `admin`: eso es el rol
|
|
89
89
|
// de master y sigue siendo local. No se empareja — se concede desde el PC.
|
|
90
|
-
ADMIN: 'vault:admin'
|
|
90
|
+
ADMIN: 'vault:admin',
|
|
91
|
+
APPROVE: 'vault:approve' // aprobar pedidos de secretos (cajones con `approval`); se concede a mano, como admin
|
|
91
92
|
})
|
|
92
93
|
|
|
93
94
|
/**
|