Parte II — Tronco y raíces

05 · La rueda: identidad y permisos

Ubuntu: "soy porque somos". En Baobáxia, quién eres vale en toda la red; lo que puedes hacer vale dentro de la rueda que te reconoce.

La rueda de los roles: USER, PAJÉ, OXALÁ

Archivos para leer

  1. services/auth.py — contraseñas (PBKDF2), tokens JWT, verificación de un token de otra mucua, freno de intentos.
  2. api/v3/auth.py y api/v3/deps.py — el login y cómo el token se convierte en el moc que recibe toda ruta.
  3. infra/identity.py — claves Ed25519 (firma) y X25519 (cifrado).
  4. services/permissions.py — can(): toda decisión de acceso pasa por aquí.
  5. services/acesso_galaxia.py, services/grupos.py, services/acl.py.
  6. infra/envelope.py — cifrado de extremo a extremo.
  7. services/challenge.py, services/recovery.py, infra/seed_phrase.py.

Dos identidades, una persona

  • Contraseña (password_hash PBKDF2-SHA256, 200 mil rondas) en el saber de la mocambola.
  • Claves: un par Ed25519 (firma autoría y tokens) + un par X25519 (abre saberes cifrados). La privada queda en ~/.baobaxia/keys/<uuid>.json, cifrada con la contraseña (scrypt + AES-GCM), modo 600 — nunca viaja. La pública va en el saber.bbx.
  • BBX_AUTH_MODE = senha (contraseña) | chave (clave) | ambos (ambos, por defecto).

Login y token

sequenceDiagram
    autonumber
    actor P as Persona
    participant A as POST /auth/login
    participant F as freno (services/auth.py)
    participant S as SaberService
    participant K as identity
    P->>A: username (o nombre@uuid8 de la mucua) + contraseña
    A->>F: tentativas_bloqueadas?
    alt demasiados fallos en la ventana
        A-->>P: 429
    end
    A->>S: find_mocambola (prefiere la de la mucua local)
    A->>A: verify_password (PBKDF2, compare_digest)
    A->>K: cache_session_key — desbloquea las privadas en memoria (8 h)
    A->>A: _issue_for: rol efectivo + galaxias administradas
    A->>K: firma un JWT EdDSA con la clave de la MUCUA (o HS256)
    A-->>P: access_token (7 días) + perfil sin password_hash

Con login por clave: POST /auth/desafio devuelve un nonce de un solo uso; POST /auth/login-chave envía la firma (y, si es una clave secundaria, el certificado de delegación firmado por la principal).

Token firmado por otra mucua

flowchart TD
    T[token EdDSA, iss = bbx:mucua:X] --> L{¿X es la mucua local?}
    L -- sí --> K[verifica con la clave que está EN EL DISCO<br/>nunca con la pubkey de un saber]
    L -- no --> P[verifica con la pubkey del saber mucua X]
    P --> Q{¿la mocambola sub es conocida aquí?}
    Q -- "sí, y es de otra mucua" --> N[rechaza: X solo responde<br/>por sus propias mocambolas]
    Q -- "sí, y es de X" --> R[rol, galaxias y mucua<br/>recalculados con NUESTROS datos]
    Q -- no --> U[entra como USER, sin alcance]

La firma prueba quién es; nunca qué puede hacer aquí. (Este diseño es de la versión 3.3 — ver el módulo 11.)

can(): la decisión

flowchart TD
    A[can mocambola, acción, recurso] --> S{¿actor de sistema?}
    S -- sí --> OK[permite]
    S -- no --> R{¿acción = READ?}
    R -- sí --> RA{¿borrador?}
    RA -- sí --> RB[solo autor, PAJÉ de la mucua,<br/>OXALÁ de la galaxia]
    RA -- no --> G{¿galaxia restringida<br/>y fuera de la lista?}
    G -- sí --> NO[niega]
    G -- no --> PR{¿protecao acl/cifrado?}
    PR -- sí --> PL[lectores, autor;<br/>acl: + PAJÉ de la mucua]
    PR -- no --> V[visibility: public → todos<br/>mucua → misma mucua u OXALÁ<br/>private → dueño o PAJÉ]
    R -- no --> W{¿anónimo?}
    W -- sí --> NO
    W -- no --> RO{¿galaxia de solo lectura?}
    RO -- sí --> NO
    RO -- no --> E[editores del saber UPDATE → gestor local →<br/>dueño → PAJÉ de la mucua → OXALÁ de la galaxia]

El descriptor del recurso se arma en un solo lugar (SaberService._descritor / _resource_of). Cuando cada ruta armaba el suyo, algunas olvidaban protecao y filtraban datos — lee el docstring de testes/test_vazamentos_de_leitura.py.

Cifrado de extremo a extremo

La calabaza sellada

Con protecao: cifrado, cada saber recibe una clave de contenido (ChaCha20-Poly1305). description, body, location y los anexos (.bbxenc) se cifran; title y tags quedan en claro para la búsqueda. La clave del saber se envuelve para cada lector (X25519 ECDH + HKDF + AES-GCM). El rol no abre la calabaza: solo quien tiene un envoltorio con su propio nombre.

Recuperación

Tres caminos independientes (services/recovery.py): un guardián autoriza la rotación de la identidad (nunca ve contraseñas ni contenido), una frase semilla armada con respuestas personales + entropía, o la clave guardada en la mucua natal.

Prácticas

  1. En test_token_federado_recalcula_papel.py, lee mucua_de_fora_nao_responde_por_mocambola_daqui. Explica el ataque con tus palabras.
  2. Crea un saber con protecao: acl y leitores: [alguien]. Intenta leerlo como anónimo por el listado, por el detalle, por la colección, por la descarga del anexo. (test_vazamentos_de_leitura.py hace eso.)
  3. En permissions.py, ¿cuál es la diferencia entre acl y cifrado para un PAJÉ? ¿Por qué esa diferencia es honesta?

Para conversar

  • El comentario de GalaxiaData.acesso (natures/schemas.py) llama a la galaxia cerrada "cortesía de software". ¿Qué quiere decir, y cuándo basta?
  • ¿Quién debería ser guardián de la identidad de alguien en tu comunidad?

vedi questo capitolo come sapere