@jantstack/adonis-authz 2.5.0-alpha.1 → 2.5.0-alpha.3

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.
Files changed (103) hide show
  1. package/README.md +515 -22
  2. package/build/commands/authz_relations_reconcile.d.ts +13 -62
  3. package/build/commands/authz_relations_reconcile.d.ts.map +1 -1
  4. package/build/commands/authz_relations_reconcile.js +202 -116
  5. package/build/commands/authz_relations_reconcile.js.map +1 -1
  6. package/build/commands/openfga_provision.d.ts +62 -0
  7. package/build/commands/openfga_provision.d.ts.map +1 -1
  8. package/build/commands/openfga_provision.js +193 -1
  9. package/build/commands/openfga_provision.js.map +1 -1
  10. package/build/index.d.ts +6 -6
  11. package/build/index.d.ts.map +1 -1
  12. package/build/index.js +3 -3
  13. package/build/index.js.map +1 -1
  14. package/build/providers/authz_provider.d.ts.map +1 -1
  15. package/build/providers/authz_provider.js +4 -1
  16. package/build/providers/authz_provider.js.map +1 -1
  17. package/build/src/define_config.d.ts +32 -0
  18. package/build/src/define_config.d.ts.map +1 -1
  19. package/build/src/define_config.js +109 -0
  20. package/build/src/define_config.js.map +1 -1
  21. package/build/src/drivers/database_relations_driver.d.ts +36 -1
  22. package/build/src/drivers/database_relations_driver.d.ts.map +1 -1
  23. package/build/src/drivers/database_relations_driver.js +213 -46
  24. package/build/src/drivers/database_relations_driver.js.map +1 -1
  25. package/build/src/drivers/openfga_driver.d.ts +30 -4
  26. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  27. package/build/src/drivers/openfga_driver.js +151 -45
  28. package/build/src/drivers/openfga_driver.js.map +1 -1
  29. package/build/src/drivers/openfga_facts.d.ts +21 -0
  30. package/build/src/drivers/openfga_facts.d.ts.map +1 -1
  31. package/build/src/drivers/openfga_facts.js +74 -8
  32. package/build/src/drivers/openfga_facts.js.map +1 -1
  33. package/build/src/drivers/openfga_relations_driver.d.ts +140 -10
  34. package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -1
  35. package/build/src/drivers/openfga_relations_driver.js +743 -49
  36. package/build/src/drivers/openfga_relations_driver.js.map +1 -1
  37. package/build/src/errors.d.ts +148 -0
  38. package/build/src/errors.d.ts.map +1 -1
  39. package/build/src/errors.js +166 -0
  40. package/build/src/errors.js.map +1 -1
  41. package/build/src/manager.d.ts.map +1 -1
  42. package/build/src/manager.js +15 -5
  43. package/build/src/manager.js.map +1 -1
  44. package/build/src/openfga.d.ts +1 -1
  45. package/build/src/openfga.d.ts.map +1 -1
  46. package/build/src/openfga.js +1 -1
  47. package/build/src/openfga.js.map +1 -1
  48. package/build/src/relations/check_many.d.ts +1 -1
  49. package/build/src/relations/check_many.d.ts.map +1 -1
  50. package/build/src/relations/check_many.js +6 -4
  51. package/build/src/relations/check_many.js.map +1 -1
  52. package/build/src/relations/define_relations_config.d.ts +187 -1
  53. package/build/src/relations/define_relations_config.d.ts.map +1 -1
  54. package/build/src/relations/define_relations_config.js +395 -7
  55. package/build/src/relations/define_relations_config.js.map +1 -1
  56. package/build/src/relations/manager.d.ts +33 -2
  57. package/build/src/relations/manager.d.ts.map +1 -1
  58. package/build/src/relations/manager.js +123 -4
  59. package/build/src/relations/manager.js.map +1 -1
  60. package/build/src/relations/reconcile.d.ts +295 -13
  61. package/build/src/relations/reconcile.d.ts.map +1 -1
  62. package/build/src/relations/reconcile.js +886 -85
  63. package/build/src/relations/reconcile.js.map +1 -1
  64. package/build/src/relations/tuple_order.d.ts +126 -0
  65. package/build/src/relations/tuple_order.d.ts.map +1 -0
  66. package/build/src/relations/tuple_order.js +598 -0
  67. package/build/src/relations/tuple_order.js.map +1 -0
  68. package/build/src/relations_config_store.d.ts.map +1 -1
  69. package/build/src/relations_config_store.js +29 -7
  70. package/build/src/relations_config_store.js.map +1 -1
  71. package/build/src/relations_reconcile.d.ts +79 -0
  72. package/build/src/relations_reconcile.d.ts.map +1 -0
  73. package/build/src/relations_reconcile.js +85 -0
  74. package/build/src/relations_reconcile.js.map +1 -0
  75. package/build/src/scope_outbox.d.ts +9 -5
  76. package/build/src/scope_outbox.d.ts.map +1 -1
  77. package/build/src/scope_outbox.js +7 -4
  78. package/build/src/scope_outbox.js.map +1 -1
  79. package/build/src/shared/backend_guard.d.ts +8 -0
  80. package/build/src/shared/backend_guard.d.ts.map +1 -1
  81. package/build/src/shared/backend_guard.js +15 -0
  82. package/build/src/shared/backend_guard.js.map +1 -1
  83. package/build/src/testing/main.d.ts +1 -1
  84. package/build/src/testing/main.d.ts.map +1 -1
  85. package/build/src/testing/main.js +1 -1
  86. package/build/src/testing/main.js.map +1 -1
  87. package/build/src/testing/relations_contract.d.ts +18 -0
  88. package/build/src/testing/relations_contract.d.ts.map +1 -1
  89. package/build/src/testing/relations_contract.js +895 -45
  90. package/build/src/testing/relations_contract.js.map +1 -1
  91. package/build/src/testing/relations_reconcile_contract.d.ts +9 -0
  92. package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -1
  93. package/build/src/testing/relations_reconcile_contract.js +207 -0
  94. package/build/src/testing/relations_reconcile_contract.js.map +1 -1
  95. package/build/src/testing/scope_outbox_contract.d.ts +14 -1
  96. package/build/src/testing/scope_outbox_contract.d.ts.map +1 -1
  97. package/build/src/testing/scope_outbox_contract.js +126 -48
  98. package/build/src/testing/scope_outbox_contract.js.map +1 -1
  99. package/build/src/types.d.ts +90 -6
  100. package/build/src/types.d.ts.map +1 -1
  101. package/build/src/types.js +2 -0
  102. package/build/src/types.js.map +1 -1
  103. package/package.json +2 -1
@@ -299,6 +299,13 @@ export declare class OpenFgaAuthorizationDriver implements AuthorizationDriver {
299
299
  };
300
300
  private logger;
301
301
  private timeoutMs;
302
+ /**
303
+ * El modelo FIJADO por el despliegue (`options.modelId`), o `undefined` si el
304
+ * driver pregunta siempre al último. Lo lee `modelPermissions` (plan 2.5 ·
305
+ * lote B · B8): el verificador de la pasada tiene que mirar el MISMO modelo
306
+ * con el que este driver escribe y responde, no el último del store.
307
+ */
308
+ private readonly modelId;
302
309
  private consistency;
303
310
  /** Reloj de pared del driver (J1): el ÚNICO `now` de checks, filtros y re-grant. */
304
311
  private now;
@@ -863,10 +870,17 @@ export declare class OpenFgaAuthorizationDriver implements AuthorizationDriver {
863
870
  */
864
871
  reconcile(source: ReconcileSource, options?: ReconcileOptions): Promise<ReconcileReport>;
865
872
  /**
866
- * Las relaciones `permits_<P>` que DECLARA el modelo publicado del store, o
867
- * `null` si no se puede saber (un store sin modelo). No es una barrera de
868
- * seguridad: es la diferencia entre contar un permiso que este store no
869
- * puede llevar y morirse con un 400 a mitad de la migración.
873
+ * Las relaciones `permits_<P>` que DECLARA el modelo con el que ESTE driver
874
+ * escribe y responde —el FIJADO (`options.modelId`) si lo hay; el último
875
+ * del store solo si no fija ninguno—, o `null` si no se puede saber (un
876
+ * store sin modelo). No es una barrera de seguridad: es la diferencia entre
877
+ * contar un permiso que este store no puede llevar y morirse con un 400 a
878
+ * mitad de la migración. **Plan 2.5 · lote B · B8 (auditor R2-10)**: hasta
879
+ * `2.5.0-alpha.1` leía SIEMPRE el último modelo, y con el id fijado en los
880
+ * pods la pasada certificaba contra un modelo que nadie usa — un permiso
881
+ * publicado después del fijado se escribía bajo el fijado (400 ⇒ 503 a
882
+ * mitad), y uno retirado del último se CONTABA y se dejaba sin proyectar en
883
+ * el modelo que sí sirve (fail-closed sobre un permiso vivo).
870
884
  */
871
885
  private modelPermissions;
872
886
  /**
@@ -1059,6 +1073,18 @@ export declare class OpenFgaAuthorizationDriver implements AuthorizationDriver {
1059
1073
  * extendido con relaciones derivadas sobre `role_binding` no se enumeraría
1060
1074
  * por aquí.
1061
1075
  */
1076
+ /**
1077
+ * **El gemelo de roles de M8** (cierre 1 del lote B · 🟡 4): «toda enumeración
1078
+ * que se puede cortar devuelve `truncated` o lanza». Las TRES lecturas por
1079
+ * `Read` de este driver (`readAllTuples`, `readEveryTuple`, `enumerateFacts`)
1080
+ * pasan por aquí: un 200 sin `tuples` (un proxy, una caché, un cuerpo vacío)
1081
+ * NO es una página vacía — hasta el cierre un `?? []` sobre `response.tuples` la vendía
1082
+ * como tal, y `purgeScope` «demostraba» cero sobre ella sin haber borrado
1083
+ * nada (invariante 11 abierto). Es 503 `E_AUTHZ_BACKEND_UNAVAILABLE`, como
1084
+ * `#readAll` del driver de relaciones. Un `grep` del spec vigila que ningún
1085
+ * default `?? []` sobre `tuples` vuelva a este fichero.
1086
+ */
1087
+ private tuplesOrThrow;
1062
1088
  private readAllTuples;
1063
1089
  }
1064
1090
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"openfga_driver.d.ts","sourceRoot":"","sources":["../../../src/drivers/openfga_driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openfga_driver.d.ts","sourceRoot":"","sources":["../../../src/drivers/openfga_driver.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import db from '@adonisjs/lucid/services/db';
2
2
  import { OpenFgaClient, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes, ConsistencyPreference, } from '@openfga/sdk';
3
- import { AuthorizationBackendError, AuthorizationBackendTimeoutError, AuthorizationConfigError, AuthorizationInternalError, InvalidIdentityError, MassReconcileRefusedError, ReconcileTooLargeError, PurgeIncompleteError, ScopeCycleError, ScopeDriftUnguardedError, ScopeTreeDriftError, UnknownPermissionError, UnknownRoleError, UnsupportedOperationError, WriteConflictError, } from '../errors.js';
3
+ import { AuthorizationBackendError, AuthorizationBackendTimeoutError, AuthorizationConfigError, AuthorizationInternalError, InvalidIdentityError, MassReconcileRefusedError, ReconcileTooLargeError, PurgeIncompleteError, ScopeCycleError, ScopeDriftUnguardedError, ScopeTreeDriftError, UnknownPermissionError, UnknownRoleError, UnsupportedOperationError, WriteConflictError, markPartialWrite, } from '../errors.js';
4
4
  import { APP_SCOPE_TYPE } from '../types.js';
5
5
  import { AUTHZ_TABLES_ORIGIN, RECONCILE_MAX_DETAILS, emptyReconcilePhases as emptyPhases, reconcileBatchSize, reconcileMaxTuples, sumReconcilePhases as sumPhases, } from '../reconcile.js';
6
6
  import { assertRoleAssignableAt, declaredRoleAt, fromDbScopeUuid, hasRoleTargets, resolveRoleQuery, rolesToRevoke, visibleRoleFor, } from './database_driver.js';
7
7
  import { assertCatalogUuid, assertIdentity, assertScope, chainKeysFrom, isCatalogUuid, isValidScope, normalizeRoleQuery, scopeFromKey, scopeKey, } from '../identity.js';
8
8
  import { resolveGrantExpiry, sameInstant, toExpiryDate } from '../expiry.js';
9
- import { assertKnownScope, canonicalScope, canonicalScopeTargets, guardSql, isAuthzError, isTimeoutLike, resolveChain, rootOnlyResolver, withDeadline, } from '../shared/backend_guard.js';
9
+ import { assertKnownScope, canonicalScope, canonicalScopeTargets, guardSql, isAuthzError, isServerWriteLimit, isTimeoutLike, resolveChain, rootOnlyResolver, withDeadline, } from '../shared/backend_guard.js';
10
10
  import { CatalogCache, GLOBAL_OWNER_KEY, assertCatalogOptions, isRoleVisibleWith, withAuthzCatalogWrite } from '../catalog/catalog_cache.js';
11
11
  import { FACTS_ASSIGNEE_RELATION, FACTS_BINDING_RELATION, FACTS_BINDING_TYPE, FACTS_DENIED_PREFIX, FACTS_GROUP_TYPE, FACTS_MAX_RESOLVE_DEPTH, FACTS_PARENT_RELATION, FACTS_PERMITS_PREFIX, FACTS_ROLE_RELATION, FACTS_ROLE_TYPE, FACTS_ROOTED_RELATION, FACTS_SCOPE_TYPE, assertFactsModelPublishable, assertHolderTypes, factsBindingObject, factsBindingTuples, factsCatalogTuples, factsDenyTuple, factsParentTuple, factsRelationsOf, factsRootTuples, factsScopeBindingTuple, factsScopeObject, factsTupleId, openFgaFactsModel, } from './openfga_facts.js';
12
12
  import { readCatalogProjectionSnapshot } from '../catalog/catalog.js';
@@ -239,6 +239,22 @@ function withPreserveRecipe(error) {
239
239
  `escribe a ciegas: si la intención es "permanente", pasa { expiresAt: null }; si es temporal, una Date.`;
240
240
  return error;
241
241
  }
242
+ /**
243
+ * **El gemelo de roles de N3** (lote D · D2; cierre 2 de D1 · ⚪ N3 lo cerró en relaciones): un servidor arrancado con
244
+ * `--max-tuples-per-write` < 100 rechaza con un 400 `exceeded_entity_limit` los `Write` que este driver parte en
245
+ * `PURGE_BATCH_SIZE` (100) — `projectCatalog` (la proyección del catálogo), `reconcile` (`applyReconcileWrites`) y
246
+ * `purgeScope`. Hasta D2 salía como 503 `E_AUTHZ_BACKEND_UNAVAILABLE`, que parece transitorio: repetido, vuelve a
247
+ * fallar igual para siempre. Es configuración del DESPLIEGUE ⇒ 500 `E_AUTHZ_CONFIG` con la letra del requisito, en el
248
+ * PUNTO DE PASO ÚNICO de todas las llamadas del cliente (`guardBackendErrors`).
249
+ */
250
+ function serverWriteLimitError(operation, cause) {
251
+ const sdk = cause?.apiErrorMessage;
252
+ return new AuthorizationConfigError(`OpenFgaAuthorizationDriver: ${operation}: el servidor OpenFGA rechazó un Write por su tope de operaciones por Write ` +
253
+ `(exceeded_entity_limit${typeof sdk === 'string' ? `: «${sdk}»` : ''}). Este driver parte sus escrituras (proyección ` +
254
+ `del catálogo, reconcile, purgas) en lotes de ${PURGE_BATCH_SIZE} y exige --max-tuples-per-write ≥ ${PURGE_BATCH_SIZE} en ` +
255
+ `el servidor (OPENFGA_MAX_TUPLES_PER_WRITE; el default es ${PURGE_BATCH_SIZE}). No es transitorio: repetida, la operación ` +
256
+ `vuelve a fallar igual. Si ya se habían aplicado Write anteriores de la misma operación, quedó A MEDIAS.`, { cause });
257
+ }
242
258
  /**
243
259
  * Devuelve el cliente con TODOS sus métodos envueltos: un fallo de red o un
244
260
  * 5xx sale como `AuthorizationBackendError` (503) y no como el `FgaError` del
@@ -264,7 +280,9 @@ function guardBackendErrors(client, timeoutMs) {
264
280
  const operation = String(prop);
265
281
  const fail = (cause) => isTimeoutLike(cause)
266
282
  ? new AuthorizationBackendTimeoutError('openfga', operation, timeoutMs, cause)
267
- : new AuthorizationBackendError('openfga', operation, cause);
283
+ : isServerWriteLimit(cause)
284
+ ? serverWriteLimitError(operation, cause)
285
+ : new AuthorizationBackendError('openfga', operation, cause);
268
286
  try {
269
287
  const result = value.apply(target, args);
270
288
  if (!(result instanceof Promise))
@@ -417,6 +435,13 @@ export class OpenFgaAuthorizationDriver {
417
435
  diagnostics = { unparseableBindings: 0 };
418
436
  logger;
419
437
  timeoutMs;
438
+ /**
439
+ * El modelo FIJADO por el despliegue (`options.modelId`), o `undefined` si el
440
+ * driver pregunta siempre al último. Lo lee `modelPermissions` (plan 2.5 ·
441
+ * lote B · B8): el verificador de la pasada tiene que mirar el MISMO modelo
442
+ * con el que este driver escribe y responde, no el último del store.
443
+ */
444
+ modelId;
420
445
  consistency;
421
446
  /** Reloj de pared del driver (J1): el ÚNICO `now` de checks, filtros y re-grant. */
422
447
  now;
@@ -455,6 +480,7 @@ export class OpenFgaAuthorizationDriver {
455
480
  assertScopeDriftGuarded(options);
456
481
  this.logger = options.logger ?? console;
457
482
  this.timeoutMs = timeoutMs;
483
+ this.modelId = options.modelId;
458
484
  this.consistency =
459
485
  options.consistency === 'minimize_latency'
460
486
  ? ConsistencyPreference.MinimizeLatency
@@ -849,7 +875,16 @@ export class OpenFgaAuthorizationDriver {
849
875
  await this.client.deleteTuples([key], {
850
876
  conflict: { onMissingDeletes: ClientWriteRequestOnMissingDeletes.Ignore },
851
877
  });
852
- await this.client.writeTuples([...structure, tuple], IGNORE_DUPLICATE_WRITES);
878
+ // Cierre 3 de D2 · ⚪ C2-5 (gemelo de roles del N1 (b) de relaciones): el delete YA entró — si el write falla, el
879
+ // `assignee` quedó BORRADO (el grant se perdió: fail-closed) y la escritura es PARCIAL, no «no escrita»: el error va
880
+ // marcado (`markPartialWrite`) y el manager notifica `indeterminate: true` (invariante 13). Hasta el cierre 3 era un
881
+ // 503 sin marca y sin evento, con el grant ya revocado.
882
+ try {
883
+ await this.client.writeTuples([...structure, tuple], IGNORE_DUPLICATE_WRITES);
884
+ }
885
+ catch (error) {
886
+ throw error !== null && typeof error === 'object' ? markPartialWrite(error) : error;
887
+ }
853
888
  }
854
889
  /**
855
890
  * Estado actual de una asignación, con TRES resultados posibles y no dos.
@@ -863,7 +898,10 @@ export class OpenFgaAuthorizationDriver {
863
898
  async readAssignment(key) {
864
899
  try {
865
900
  const response = await this.client.read(key, { consistency: this.consistency });
866
- const tuple = response.tuples?.[0];
901
+ // ⚪ N5 (cierre 2 del lote B): un 200 sin `tuples` no es «ausente», es
902
+ // «no se sabe» (503 de `tuplesOrThrow` como causa): sin objetivo explícito
903
+ // `grant` no escribe a ciegas sobre una caducidad que no ha podido ver.
904
+ const tuple = this.tuplesOrThrow(response, 'readAssignment')[0];
867
905
  if (!tuple)
868
906
  return { kind: 'absent' };
869
907
  const validUntil = tuple.key?.condition?.context?.valid_until;
@@ -1622,12 +1660,32 @@ export class OpenFgaAuthorizationDriver {
1622
1660
  const keys = await this.readAllTuples({ object }, { includeExpired: true });
1623
1661
  return object === scopeObject ? keys.filter((k) => k.relation !== FACTS_PARENT_RELATION) : keys;
1624
1662
  };
1625
- /** Borra en lotes ≤ 100 (límite del `Write`), sin quejarse de lo que ya no está. */
1626
- const deleteKeys = async (keys) => {
1663
+ /**
1664
+ * Cierre 4 de D2 · ⚪ C3-4 (gemelo de `#partialIfDeleted` de relaciones): cuántos `Write` de borrado YA entraron. La
1665
+ * purga son varios `Write` (estructura, hechos por objeto, denies; lotes de 100): un fallo después del primero llega
1666
+ * con parte ya borrada y se marca `markPartialWrite` (el manager notifica `indeterminate: true`, invariante 13). Un
1667
+ * lote que falla no borró nada (FGA aplica cada `Write` entero).
1668
+ */
1669
+ const progress = { deleted: 0, unconfirmed: false };
1670
+ /**
1671
+ * Borra en lotes ≤ 100 (límite del `Write`), sin quejarse de lo que ya no está. Cierre 5 de D2 · ⚪ D4-5: el contador
1672
+ * suma lo que el lote BORRA DE VERDAD (`effective`, por defecto todo: las fases 2-4 borran lo que acaban de LEER), no
1673
+ * lo pedido — la fase 1 borra la estructura a CIEGAS con `Ignore` y hasta el cierre 5 contaba claves que no existían
1674
+ * (un fallo posterior salía `indeterminate: true` sin haber borrado nada).
1675
+ */
1676
+ const deleteKeys = async (keys, effective = () => true) => {
1627
1677
  for (let i = 0; i < keys.length; i += PURGE_BATCH_SIZE) {
1628
- await this.client.deleteTuples(keys.slice(i, i + PURGE_BATCH_SIZE), {
1678
+ const batch = keys.slice(i, i + PURGE_BATCH_SIZE);
1679
+ await this.client.deleteTuples(batch, {
1629
1680
  conflict: { onMissingDeletes: ClientWriteRequestOnMissingDeletes.Ignore },
1630
1681
  });
1682
+ progress.deleted += batch.filter(effective).length;
1683
+ // Cierre 6 de D2 · ⚪ D5-4: una clave pedida a ciegas que la lectura previa NO vio puede haberla escrito un
1684
+ // `grant` concurrente entre esa lectura y este borrado: entonces este lote SÍ borró algo que no se contó. Hasta
1685
+ // que la fase 2 termine (borra —y cuenta— el `assignee` de ese grant) el efecto es INCIERTO, y un fallo en ese
1686
+ // tramo sale marcado parcial: preferir sobre-notificar a sub-notificar.
1687
+ if (batch.some((k) => !effective(k)))
1688
+ progress.unconfirmed = true;
1631
1689
  }
1632
1690
  };
1633
1691
  /** Las dos aristas de (c2): estructura, no hechos (`#role` y `#binding`). */
@@ -1660,36 +1718,55 @@ export class OpenFgaAuthorizationDriver {
1660
1718
  // el residuo es justo lo que la demostración de cero reporta. La arista
1661
1719
  // `parent` sigue sin tocarse: la borra `scopes.detached` DESPUÉS de que
1662
1720
  // esta purga demuestre cero (S6, cruce 9).
1663
- await deleteKeys(roles.flatMap((r) => factsBindingTuples(key, r.uuid)));
1664
- for (const object of objects) {
1665
- if (object === scopeObject)
1666
- continue;
1667
- await deleteKeys((await purgeable(object)).filter((k) => !isStructure(k)));
1668
- }
1669
- // **Las aristas `#binding` de roles que el catálogo YA NO declara en este
1670
- // nivel también se purgan** (3b-8 · C2). El barrido no es atómico con el
1671
- // catálogo: un rol cuya fila se borró (o cambió de `scope_type`) entre el
1672
- // commit y esta purga dejaba una arista que el paso 1 no ve —solo mira
1673
- // `authz_roles WHERE scope_type = <este>`— y que la demostración de cero
1674
- // SÍ cuenta ⇒ `E_AUTHZ_PURGE_INCOMPLETE` en TODOS los intentos y
1675
- // `detached` bloqueado para siempre. Borrarla nunca concede (es la arista
1676
- // de visibilidad, y su rol ya no existe en este nivel), y la arista de un
1677
- // rol DEL catálogo que un `grant` concurrente reescribió no se toca
1678
- // (`known` la protege): esa sigue saliendo como residuo, que es lo
1679
- // correcto — se reintenta.
1680
- const known = new Set(roles.map((r) => `role_binding:${key}|${r.uuid}`));
1681
- await deleteKeys((await purgeable(scopeObject)).filter((k) => k.relation === FACTS_BINDING_RELATION && !known.has(k.user)));
1682
- await deleteKeys((await purgeable(scopeObject)).filter((k) => k.relation.startsWith(FACTS_DENIED_PREFIX)));
1683
- // Demostrar cero: lo que no se puede demostrar, se reporta.
1684
- const residue = [];
1685
- for (const object of objects) {
1686
- const left = await purgeable(object);
1687
- if (left.length)
1688
- residue.push(`${object} (${left.length})`);
1721
+ // Los pasos (estructura → hechos → denies → demostrar cero), en una función LOCAL (no un método `#`: el driver
1722
+ // puede llegar envuelto en un Proxy y un campo privado exige el receptor real), para marcar el corte a medias.
1723
+ const steps = async () => {
1724
+ // ⚪ D4-5 (cierre 5 de D2): qué estructura EXISTE antes del borrado a ciegas — UNA lectura del scope (sus aristas
1725
+ // `#binding`). Solo la arista `scope#binding` hace visible (y por tanto observable) un rol aquí: un `role_binding#role`
1726
+ // sin su `#binding` (lo que deja el barrido de roles locales) no cambia ninguna respuesta al borrarse, así que no
1727
+ // cuenta como escritura hecha. Ventana declarada: una arista que un `grant` concurrente escribe ENTRE esta lectura y
1728
+ // el borrado se borra sin contarse; la fase 2 borra después su `assignee`, que sí cuenta, y HASTA que la fase 2
1729
+ // termina el error va marcado parcial igual (`unconfirmed`, ⚪ D5-4 del cierre 6: sobre-notificar, nunca sub-).
1730
+ const visible = new Set((await purgeable(scopeObject)).filter((k) => k.relation === FACTS_BINDING_RELATION).map((k) => k.user));
1731
+ await deleteKeys(roles.flatMap((r) => factsBindingTuples(key, r.uuid)), (k) => visible.has(k.relation === FACTS_BINDING_RELATION ? k.user : k.object));
1732
+ for (const object of objects) {
1733
+ if (object === scopeObject)
1734
+ continue;
1735
+ await deleteKeys((await purgeable(object)).filter((k) => !isStructure(k)));
1736
+ }
1737
+ // ⚪ D5-4: la fase 2 terminó — el `assignee` de un grant que se coló antes de la fase 1 ya se borró y se contó.
1738
+ progress.unconfirmed = false;
1739
+ // **Las aristas `#binding` de roles que el catálogo YA NO declara en este
1740
+ // nivel también se purgan** (3b-8 · C2). El barrido no es atómico con el
1741
+ // catálogo: un rol cuya fila se borró (o cambió de `scope_type`) entre el
1742
+ // commit y esta purga dejaba una arista que el paso 1 no ve —solo mira
1743
+ // `authz_roles WHERE scope_type = <este>`— y que la demostración de cero
1744
+ // SÍ cuenta ⇒ `E_AUTHZ_PURGE_INCOMPLETE` en TODOS los intentos y
1745
+ // `detached` bloqueado para siempre. Borrarla nunca concede (es la arista
1746
+ // de visibilidad, y su rol ya no existe en este nivel), y la arista de un
1747
+ // rol DEL catálogo que un `grant` concurrente reescribió no se toca
1748
+ // (`known` la protege): esa sigue saliendo como residuo, que es lo
1749
+ // correcto — se reintenta.
1750
+ const known = new Set(roles.map((r) => `role_binding:${key}|${r.uuid}`));
1751
+ await deleteKeys((await purgeable(scopeObject)).filter((k) => k.relation === FACTS_BINDING_RELATION && !known.has(k.user)));
1752
+ await deleteKeys((await purgeable(scopeObject)).filter((k) => k.relation.startsWith(FACTS_DENIED_PREFIX)));
1753
+ // Demostrar cero: lo que no se puede demostrar, se reporta.
1754
+ const residue = [];
1755
+ for (const object of objects) {
1756
+ const left = await purgeable(object);
1757
+ if (left.length)
1758
+ residue.push(`${object} (${left.length})`);
1759
+ }
1760
+ if (residue.length) {
1761
+ throw new PurgeIncompleteError(`purgeScope ${key}: quedan tuplas tras el borrado — ${residue.join('; ')}. ` +
1762
+ `No confirmes el borrado del scope; reintenta la purga.`);
1763
+ }
1764
+ };
1765
+ try {
1766
+ await steps();
1689
1767
  }
1690
- if (residue.length) {
1691
- throw new PurgeIncompleteError(`purgeScope ${key}: quedan tuplas tras el borrado — ${residue.join('; ')}. ` +
1692
- `No confirmes el borrado del scope; reintenta la purga.`);
1768
+ catch (error) {
1769
+ throw (progress.deleted > 0 || progress.unconfirmed) && error !== null && typeof error === 'object' ? markPartialWrite(error) : error;
1693
1770
  }
1694
1771
  }
1695
1772
  /**
@@ -2260,13 +2337,24 @@ export class OpenFgaAuthorizationDriver {
2260
2337
  };
2261
2338
  }
2262
2339
  /**
2263
- * Las relaciones `permits_<P>` que DECLARA el modelo publicado del store, o
2264
- * `null` si no se puede saber (un store sin modelo). No es una barrera de
2265
- * seguridad: es la diferencia entre contar un permiso que este store no
2266
- * puede llevar y morirse con un 400 a mitad de la migración.
2340
+ * Las relaciones `permits_<P>` que DECLARA el modelo con el que ESTE driver
2341
+ * escribe y responde —el FIJADO (`options.modelId`) si lo hay; el último
2342
+ * del store solo si no fija ninguno—, o `null` si no se puede saber (un
2343
+ * store sin modelo). No es una barrera de seguridad: es la diferencia entre
2344
+ * contar un permiso que este store no puede llevar y morirse con un 400 a
2345
+ * mitad de la migración. **Plan 2.5 · lote B · B8 (auditor R2-10)**: hasta
2346
+ * `2.5.0-alpha.1` leía SIEMPRE el último modelo, y con el id fijado en los
2347
+ * pods la pasada certificaba contra un modelo que nadie usa — un permiso
2348
+ * publicado después del fijado se escribía bajo el fijado (400 ⇒ 503 a
2349
+ * mitad), y uno retirado del último se CONTABA y se dejaba sin proyectar en
2350
+ * el modelo que sí sirve (fail-closed sobre un permiso vivo).
2267
2351
  */
2268
2352
  async modelPermissions() {
2269
- const response = await this.client.readLatestAuthorizationModel();
2353
+ // `readAuthorizationModel()` sin argumentos usa el `authorizationModelId`
2354
+ // con el que se construyó el cliente (el mismo de cada `Write`/`Check`).
2355
+ const response = this.modelId
2356
+ ? await this.client.readAuthorizationModel()
2357
+ : await this.client.readLatestAuthorizationModel();
2270
2358
  const model = response?.authorization_model;
2271
2359
  if (!model?.type_definitions)
2272
2360
  return null;
@@ -2722,7 +2810,7 @@ export class OpenFgaAuthorizationDriver {
2722
2810
  // página del driver: pedir 1 000 es un 400 del servidor, y el contrato del
2723
2811
  // puerto es «como mucho `limit`», no «exactamente `limit`».
2724
2812
  const response = await this.client.read({}, { pageSize: Math.min(limit, READ_PAGE_SIZE), continuationToken: page.after, consistency: this.consistency });
2725
- for (const tuple of response.tuples ?? []) {
2813
+ for (const tuple of this.tuplesOrThrow(response, 'enumerateFacts')) {
2726
2814
  const key = tuple?.key;
2727
2815
  if (!key?.user || !key?.relation || !key?.object) {
2728
2816
  skipped.push({ kind: 'tuple', reason: 'unparseable-tuple', detail: JSON.stringify(key ?? null) });
@@ -2833,7 +2921,7 @@ export class OpenFgaAuthorizationDriver {
2833
2921
  do {
2834
2922
  const response = await this.client.read({}, { pageSize: READ_PAGE_SIZE, continuationToken, consistency: this.consistency });
2835
2923
  pages += 1;
2836
- for (const tuple of response.tuples ?? []) {
2924
+ for (const tuple of this.tuplesOrThrow(response, 'reconcile.readEveryTuple')) {
2837
2925
  const key = tuple?.key;
2838
2926
  if (!key?.user || !key?.relation || !key?.object) {
2839
2927
  this.diagnostics.unparseableBindings += 1;
@@ -2898,6 +2986,24 @@ export class OpenFgaAuthorizationDriver {
2898
2986
  * extendido con relaciones derivadas sobre `role_binding` no se enumeraría
2899
2987
  * por aquí.
2900
2988
  */
2989
+ /**
2990
+ * **El gemelo de roles de M8** (cierre 1 del lote B · 🟡 4): «toda enumeración
2991
+ * que se puede cortar devuelve `truncated` o lanza». Las TRES lecturas por
2992
+ * `Read` de este driver (`readAllTuples`, `readEveryTuple`, `enumerateFacts`)
2993
+ * pasan por aquí: un 200 sin `tuples` (un proxy, una caché, un cuerpo vacío)
2994
+ * NO es una página vacía — hasta el cierre un `?? []` sobre `response.tuples` la vendía
2995
+ * como tal, y `purgeScope` «demostraba» cero sobre ella sin haber borrado
2996
+ * nada (invariante 11 abierto). Es 503 `E_AUTHZ_BACKEND_UNAVAILABLE`, como
2997
+ * `#readAll` del driver de relaciones. Un `grep` del spec vigila que ningún
2998
+ * default `?? []` sobre `tuples` vuelva a este fichero.
2999
+ */
3000
+ tuplesOrThrow(response, what) {
3001
+ const tuples = response?.tuples;
3002
+ if (!Array.isArray(tuples)) {
3003
+ throw new AuthorizationBackendError('openfga', what, new Error(`la respuesta del Read no trae 'tuples'; no se toma por vacía (sería una enumeración completa muda)`));
3004
+ }
3005
+ return tuples;
3006
+ }
2901
3007
  async readAllTuples(filter, options = {}) {
2902
3008
  // El instante con el que se filtra: el de la operación si lo trae (K9), o
2903
3009
  // el de esta lectura.
@@ -2913,7 +3019,7 @@ export class OpenFgaAuthorizationDriver {
2913
3019
  consistency: this.consistency,
2914
3020
  });
2915
3021
  pages += 1;
2916
- for (const tuple of response.tuples ?? []) {
3022
+ for (const tuple of this.tuplesOrThrow(response, `Read ${JSON.stringify(filter)}`)) {
2917
3023
  const k = tuple?.key;
2918
3024
  if (!k?.user || !k?.relation || !k?.object) {
2919
3025
  // Una tupla que el motor no puede leer es un hecho que las