@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.
- package/README.md +515 -22
- package/build/commands/authz_relations_reconcile.d.ts +13 -62
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -1
- package/build/commands/authz_relations_reconcile.js +202 -116
- package/build/commands/authz_relations_reconcile.js.map +1 -1
- package/build/commands/openfga_provision.d.ts +62 -0
- package/build/commands/openfga_provision.d.ts.map +1 -1
- package/build/commands/openfga_provision.js +193 -1
- package/build/commands/openfga_provision.js.map +1 -1
- package/build/index.d.ts +6 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/providers/authz_provider.d.ts.map +1 -1
- package/build/providers/authz_provider.js +4 -1
- package/build/providers/authz_provider.js.map +1 -1
- package/build/src/define_config.d.ts +32 -0
- package/build/src/define_config.d.ts.map +1 -1
- package/build/src/define_config.js +109 -0
- package/build/src/define_config.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +36 -1
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -1
- package/build/src/drivers/database_relations_driver.js +213 -46
- package/build/src/drivers/database_relations_driver.js.map +1 -1
- package/build/src/drivers/openfga_driver.d.ts +30 -4
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +151 -45
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_facts.d.ts +21 -0
- package/build/src/drivers/openfga_facts.d.ts.map +1 -1
- package/build/src/drivers/openfga_facts.js +74 -8
- package/build/src/drivers/openfga_facts.js.map +1 -1
- package/build/src/drivers/openfga_relations_driver.d.ts +140 -10
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_relations_driver.js +743 -49
- package/build/src/drivers/openfga_relations_driver.js.map +1 -1
- package/build/src/errors.d.ts +148 -0
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +166 -0
- package/build/src/errors.js.map +1 -1
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +15 -5
- package/build/src/manager.js.map +1 -1
- package/build/src/openfga.d.ts +1 -1
- package/build/src/openfga.d.ts.map +1 -1
- package/build/src/openfga.js +1 -1
- package/build/src/openfga.js.map +1 -1
- package/build/src/relations/check_many.d.ts +1 -1
- package/build/src/relations/check_many.d.ts.map +1 -1
- package/build/src/relations/check_many.js +6 -4
- package/build/src/relations/check_many.js.map +1 -1
- package/build/src/relations/define_relations_config.d.ts +187 -1
- package/build/src/relations/define_relations_config.d.ts.map +1 -1
- package/build/src/relations/define_relations_config.js +395 -7
- package/build/src/relations/define_relations_config.js.map +1 -1
- package/build/src/relations/manager.d.ts +33 -2
- package/build/src/relations/manager.d.ts.map +1 -1
- package/build/src/relations/manager.js +123 -4
- package/build/src/relations/manager.js.map +1 -1
- package/build/src/relations/reconcile.d.ts +295 -13
- package/build/src/relations/reconcile.d.ts.map +1 -1
- package/build/src/relations/reconcile.js +886 -85
- package/build/src/relations/reconcile.js.map +1 -1
- package/build/src/relations/tuple_order.d.ts +126 -0
- package/build/src/relations/tuple_order.d.ts.map +1 -0
- package/build/src/relations/tuple_order.js +598 -0
- package/build/src/relations/tuple_order.js.map +1 -0
- package/build/src/relations_config_store.d.ts.map +1 -1
- package/build/src/relations_config_store.js +29 -7
- package/build/src/relations_config_store.js.map +1 -1
- package/build/src/relations_reconcile.d.ts +79 -0
- package/build/src/relations_reconcile.d.ts.map +1 -0
- package/build/src/relations_reconcile.js +85 -0
- package/build/src/relations_reconcile.js.map +1 -0
- package/build/src/scope_outbox.d.ts +9 -5
- package/build/src/scope_outbox.d.ts.map +1 -1
- package/build/src/scope_outbox.js +7 -4
- package/build/src/scope_outbox.js.map +1 -1
- package/build/src/shared/backend_guard.d.ts +8 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -1
- package/build/src/shared/backend_guard.js +15 -0
- package/build/src/shared/backend_guard.js.map +1 -1
- package/build/src/testing/main.d.ts +1 -1
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +1 -1
- package/build/src/testing/main.js.map +1 -1
- package/build/src/testing/relations_contract.d.ts +18 -0
- package/build/src/testing/relations_contract.d.ts.map +1 -1
- package/build/src/testing/relations_contract.js +895 -45
- package/build/src/testing/relations_contract.js.map +1 -1
- package/build/src/testing/relations_reconcile_contract.d.ts +9 -0
- package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -1
- package/build/src/testing/relations_reconcile_contract.js +207 -0
- package/build/src/testing/relations_reconcile_contract.js.map +1 -1
- package/build/src/testing/scope_outbox_contract.d.ts +14 -1
- package/build/src/testing/scope_outbox_contract.d.ts.map +1 -1
- package/build/src/testing/scope_outbox_contract.js +126 -48
- package/build/src/testing/scope_outbox_contract.js.map +1 -1
- package/build/src/types.d.ts +90 -6
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +2 -0
- package/build/src/types.js.map +1 -1
- 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
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
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":"AAOA,OAAO,KAAK,EAAE,oBAAoB,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAA;
|
|
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
|
-
:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
1626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
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
|
-
|
|
1691
|
-
throw
|
|
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
|
|
2264
|
-
*
|
|
2265
|
-
*
|
|
2266
|
-
*
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|