@dotrino/identity 0.55.0 → 0.57.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/node.js +2 -0
- package/vault/acta.js +13 -6
- package/vault/core.js +23 -0
package/package.json
CHANGED
package/src/node.js
CHANGED
|
@@ -179,6 +179,8 @@ export class Identity {
|
|
|
179
179
|
*/
|
|
180
180
|
/** Abre un sobre sellado A ESTE aparato (envoltura + sobre). Ver `core.js`. */
|
|
181
181
|
openSealedValue (wrap, envelope) { return this._h('openSealedValue', { wrap, envelope }) }
|
|
182
|
+
/** Re-envuelve para otro miembro una llave que este aparato ya puede abrir. */
|
|
183
|
+
rewrapFor (wrap, encPub) { return this._h('rewrapFor', { wrap, encPub }) }
|
|
182
184
|
setSealKeyProvider (provider) { return this._h('setSealKeyProvider', { provider }) }
|
|
183
185
|
/** Estrena la llave de sellado en un acta nueva, sin tocar a ningún miembro. */
|
|
184
186
|
rotateSealKey () { return this._h('rotateSealKey') }
|
package/vault/acta.js
CHANGED
|
@@ -68,6 +68,12 @@ export const PAIRED_CAPS = Object.freeze(['sign', 'store', 'read'])
|
|
|
68
68
|
/** Capacidades de un SERVICIO (con CN): solo su propio cajón de secretos. */
|
|
69
69
|
export const SERVICE_CAPS = Object.freeze(['secrets'])
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Qué capacidades puede llevar un miembro según tenga cajón (CN) o no. Con CN: su cajón
|
|
73
|
+
* y, si se le conceden, las de aparato. Sin CN: solo las de aparato (no hay cajón que abrir).
|
|
74
|
+
*/
|
|
75
|
+
export const allowedCaps = (cn) => (cn ? [...SERVICE_CAPS, ...DEVICE_CAPS] : [...DEVICE_CAPS])
|
|
76
|
+
|
|
71
77
|
/** Un CN válido: minúsculas, números y guiones (igual que el namespace de secretos). */
|
|
72
78
|
export const isValidCn = (cn) => typeof cn === 'string' && /^[a-z0-9-]{1,32}$/.test(cn)
|
|
73
79
|
|
|
@@ -178,7 +184,6 @@ export function checkShape (acta) {
|
|
|
178
184
|
// mezcle las dos cosas es lo que hace que el límite sea real y no una costumbre.
|
|
179
185
|
if (m.cn != null) {
|
|
180
186
|
if (!isValidCn(m.cn)) return 'cn-invalido'
|
|
181
|
-
if (m.caps.some((c) => !SERVICE_CAPS.includes(c))) return 'servicio-con-capacidades-de-dispositivo'
|
|
182
187
|
} else if (m.caps.includes('secrets')) {
|
|
183
188
|
return 'secretos-sin-cn'
|
|
184
189
|
}
|
|
@@ -306,8 +311,10 @@ export async function applyChanges (acta, changes, { by, now = Date.now(), sealP
|
|
|
306
311
|
encPub: m.encPub || null,
|
|
307
312
|
label: String(m.label || '').slice(0, 60),
|
|
308
313
|
cn,
|
|
309
|
-
//
|
|
310
|
-
|
|
314
|
+
// PERMISOS, no tipos (2026-08-22): el CN dice qué cajón abre; no recorta lo demás.
|
|
315
|
+
// Un miembro con CN puede llevar además las capacidades de aparato (un bot que
|
|
316
|
+
// firma como aparato del acta Y lee su cajón). Sin CN, `secrets` no significa nada.
|
|
317
|
+
caps: cleanCaps(m.caps).filter((c) => allowedCaps(cn).includes(c)),
|
|
311
318
|
addedAt: now,
|
|
312
319
|
cert: m.cert || null,
|
|
313
320
|
// Puente con la identidad que este miembro traía de antes (ver makeContinuity).
|
|
@@ -331,9 +338,9 @@ export async function applyChanges (acta, changes, { by, now = Date.now(), sealP
|
|
|
331
338
|
case 'caps': {
|
|
332
339
|
const m = find(ch.pub)
|
|
333
340
|
if (!m) throw new Error('caps: that member is not in the record')
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
m.caps = cleanCaps(ch.caps).filter((c) => (m.cn
|
|
341
|
+
// El CN no cambia por aquí (quitar el cajón es sacar al miembro y volver a admitirlo,
|
|
342
|
+
// que es un gesto visible); las capacidades sí, dentro de lo que su CN permite.
|
|
343
|
+
m.caps = cleanCaps(ch.caps).filter((c) => allowedCaps(m.cn).includes(c))
|
|
337
344
|
// CONCEDER LIMPIA LA RENUNCIA. `effectiveCaps` resta lo renunciado, así que sin
|
|
338
345
|
// esto una renuncia sellada era IRREVERSIBLE: el master le devolvía el permiso en
|
|
339
346
|
// `caps` y la resta seguía dejándolo fuera, para siempre. La interfaz promete
|
package/vault/core.js
CHANGED
|
@@ -1667,6 +1667,29 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
|
|
|
1667
1667
|
return Content.decryptWithCek({ cek, envelope })
|
|
1668
1668
|
},
|
|
1669
1669
|
|
|
1670
|
+
/**
|
|
1671
|
+
* RE-ENVUELVE para otro miembro una llave que este aparato ya puede abrir.
|
|
1672
|
+
*
|
|
1673
|
+
* Es lo que permite completar a un aparato que entró tarde **sin la frase del
|
|
1674
|
+
* perfil y sin que la bóveda tenga que abrir nada**: quien administra ya tiene su
|
|
1675
|
+
* envoltura de ese cajón —por eso puede pulsar «Ver»—, así que abre la llave con la
|
|
1676
|
+
* suya, que no sale de este dispositivo, y la vuelve a envolver para la pública del
|
|
1677
|
+
* recién llegado.
|
|
1678
|
+
*
|
|
1679
|
+
* No regala nada: quien hace esto ya podía leer ese cajón, y a quien se lo entrega
|
|
1680
|
+
* es a alguien que el ACTA —firmada por la maestra— ya reconoce como miembro. La
|
|
1681
|
+
* autorización es el acta; esto solo es el sobre.
|
|
1682
|
+
*
|
|
1683
|
+
* @param {{ wrap: any, encPub: string }} o `wrap`: mi envoltura · `encPub`: la
|
|
1684
|
+
* pública de cifrado del miembro al que hay que envolvérsela.
|
|
1685
|
+
* @returns {Promise<any>} la envoltura nueva, para que la bóveda la guarde.
|
|
1686
|
+
*/
|
|
1687
|
+
async rewrapFor ({ wrap, encPub } = {}) {
|
|
1688
|
+
if (!wrap || !encPub) throw new Error('rewrapFor: missing the wrap or the recipient key')
|
|
1689
|
+
const cek = await Content.openWrap({ wrap, myEncPrivateKey: encKeypair.privateKey })
|
|
1690
|
+
return Content.wrapForMember({ cek, memberEncPub: encPub })
|
|
1691
|
+
},
|
|
1692
|
+
|
|
1670
1693
|
/** Abre un sobre de contenido con el llavero del perfil (todas las generaciones). */
|
|
1671
1694
|
async openContent ({ envelope } = {}) {
|
|
1672
1695
|
return Content.decryptWithKeyring({
|