@jantstack/adonis-authz 2.0.0-alpha.1 → 2.4.0-alpha.2
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 +693 -36
- package/build/commands/authz_catalog_diff.js +1 -1
- package/build/commands/authz_catalog_diff.js.map +1 -1
- package/build/commands/authz_catalog_prune_orphans.d.ts +78 -0
- package/build/commands/authz_catalog_prune_orphans.d.ts.map +1 -0
- package/build/commands/authz_catalog_prune_orphans.js +136 -0
- package/build/commands/authz_catalog_prune_orphans.js.map +1 -0
- package/build/commands/authz_catalog_sync.d.ts +17 -0
- package/build/commands/authz_catalog_sync.d.ts.map +1 -1
- package/build/commands/authz_catalog_sync.js +27 -4
- package/build/commands/authz_catalog_sync.js.map +1 -1
- package/build/commands/authz_freeze.d.ts +44 -0
- package/build/commands/authz_freeze.d.ts.map +1 -0
- package/build/commands/authz_freeze.js +95 -0
- package/build/commands/authz_freeze.js.map +1 -0
- package/build/commands/authz_reconcile.d.ts +102 -0
- package/build/commands/authz_reconcile.d.ts.map +1 -0
- package/build/commands/authz_reconcile.js +294 -0
- package/build/commands/authz_reconcile.js.map +1 -0
- package/build/commands/authz_relations_reconcile.d.ts +97 -0
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
- package/build/commands/authz_relations_reconcile.js +313 -0
- package/build/commands/authz_relations_reconcile.js.map +1 -0
- package/build/commands/authz_scopes_relay.d.ts +47 -0
- package/build/commands/authz_scopes_relay.d.ts.map +1 -0
- package/build/commands/authz_scopes_relay.js +141 -0
- package/build/commands/authz_scopes_relay.js.map +1 -0
- package/build/commands/authz_unfreeze.d.ts +37 -0
- package/build/commands/authz_unfreeze.d.ts.map +1 -0
- package/build/commands/authz_unfreeze.js +92 -0
- package/build/commands/authz_unfreeze.js.map +1 -0
- package/build/commands/main.d.ts +6 -1
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +6 -1
- package/build/commands/main.js.map +1 -1
- package/build/commands/openfga_provision.d.ts +46 -4
- package/build/commands/openfga_provision.d.ts.map +1 -1
- package/build/commands/openfga_provision.js +90 -7
- package/build/commands/openfga_provision.js.map +1 -1
- package/build/configure.d.ts +11 -0
- package/build/configure.d.ts.map +1 -1
- package/build/configure.js +37 -1
- package/build/configure.js.map +1 -1
- package/build/index.d.ts +50 -10
- package/build/index.d.ts.map +1 -1
- package/build/index.js +45 -6
- package/build/index.js.map +1 -1
- package/build/providers/authz_provider.d.ts +26 -2
- package/build/providers/authz_provider.d.ts.map +1 -1
- package/build/providers/authz_provider.js +55 -2
- package/build/providers/authz_provider.js.map +1 -1
- package/build/services/relations.d.ts +14 -0
- package/build/services/relations.d.ts.map +1 -0
- package/build/services/relations.js +17 -0
- package/build/services/relations.js.map +1 -0
- package/build/src/{catalog.d.ts → catalog/catalog.d.ts} +41 -2
- package/build/src/catalog/catalog.d.ts.map +1 -0
- package/build/src/{catalog.js → catalog/catalog.js} +120 -14
- package/build/src/catalog/catalog.js.map +1 -0
- package/build/src/{catalog_cache.d.ts → catalog/catalog_cache.d.ts} +46 -22
- package/build/src/catalog/catalog_cache.d.ts.map +1 -0
- package/build/src/{catalog_cache.js → catalog/catalog_cache.js} +53 -43
- package/build/src/catalog/catalog_cache.js.map +1 -0
- package/build/src/define_config.d.ts +137 -3
- package/build/src/define_config.d.ts.map +1 -1
- package/build/src/define_config.js.map +1 -1
- package/build/src/drivers/database_driver.d.ts +128 -7
- package/build/src/drivers/database_driver.d.ts.map +1 -1
- package/build/src/drivers/database_driver.js +510 -24
- package/build/src/drivers/database_driver.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +113 -0
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/database_relations_driver.js +679 -0
- package/build/src/drivers/database_relations_driver.js.map +1 -0
- package/build/src/drivers/openfga_driver.d.ts +729 -122
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +2083 -476
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_facts.d.ts +384 -0
- package/build/src/drivers/openfga_facts.d.ts.map +1 -0
- package/build/src/drivers/openfga_facts.js +836 -0
- package/build/src/drivers/openfga_facts.js.map +1 -0
- package/build/src/drivers/openfga_relations_driver.d.ts +141 -0
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/openfga_relations_driver.js +590 -0
- package/build/src/drivers/openfga_relations_driver.js.map +1 -0
- package/build/src/errors.d.ts +290 -5
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +296 -7
- package/build/src/errors.js.map +1 -1
- package/build/src/freeze.d.ts +141 -0
- package/build/src/freeze.d.ts.map +1 -0
- package/build/src/freeze.js +217 -0
- package/build/src/freeze.js.map +1 -0
- package/build/src/http/app_access_middleware.d.ts.map +1 -0
- package/build/src/http/app_access_middleware.js.map +1 -0
- package/build/src/http/resource_access_middleware.d.ts +105 -0
- package/build/src/http/resource_access_middleware.d.ts.map +1 -0
- package/build/src/http/resource_access_middleware.js +81 -0
- package/build/src/http/resource_access_middleware.js.map +1 -0
- package/build/src/identity.d.ts +74 -1
- package/build/src/identity.d.ts.map +1 -1
- package/build/src/identity.js +100 -2
- package/build/src/identity.js.map +1 -1
- package/build/src/manager.d.ts +330 -4
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +1285 -188
- package/build/src/manager.js.map +1 -1
- package/build/src/models/authz_assignment.d.ts +7 -7
- package/build/src/models/authz_assignment.d.ts.map +1 -1
- package/build/src/models/authz_deny.d.ts +7 -7
- package/build/src/models/authz_deny.d.ts.map +1 -1
- package/build/src/models/authz_permission.d.ts +7 -7
- package/build/src/models/authz_permission.d.ts.map +1 -1
- package/build/src/models/authz_role.d.ts +7 -7
- package/build/src/models/authz_role.d.ts.map +1 -1
- package/build/src/models/authz_role_permission.d.ts +7 -7
- package/build/src/models/authz_role_permission.d.ts.map +1 -1
- package/build/src/openfga.d.ts +18 -2
- package/build/src/openfga.d.ts.map +1 -1
- package/build/src/openfga.js +15 -1
- package/build/src/openfga.js.map +1 -1
- package/build/src/reconcile.d.ts +37 -0
- package/build/src/reconcile.d.ts.map +1 -0
- package/build/src/reconcile.js +69 -0
- package/build/src/reconcile.js.map +1 -0
- package/build/src/relation_partition_trigger.d.ts +8 -0
- package/build/src/relation_partition_trigger.d.ts.map +1 -0
- package/build/src/relation_partition_trigger.js +93 -0
- package/build/src/relation_partition_trigger.js.map +1 -0
- package/build/src/relations/define_relations_config.d.ts +75 -0
- package/build/src/relations/define_relations_config.d.ts.map +1 -0
- package/build/src/relations/define_relations_config.js +173 -0
- package/build/src/relations/define_relations_config.js.map +1 -0
- package/build/src/relations/manager.d.ts +60 -0
- package/build/src/relations/manager.d.ts.map +1 -0
- package/build/src/relations/manager.js +213 -0
- package/build/src/relations/manager.js.map +1 -0
- package/build/src/relations/reconcile.d.ts +111 -0
- package/build/src/relations/reconcile.d.ts.map +1 -0
- package/build/src/relations/reconcile.js +200 -0
- package/build/src/relations/reconcile.js.map +1 -0
- package/build/src/relations_config_store.d.ts +22 -0
- package/build/src/relations_config_store.d.ts.map +1 -0
- package/build/src/relations_config_store.js +74 -0
- package/build/src/relations_config_store.js.map +1 -0
- package/build/src/scope_outbox.d.ts +69 -0
- package/build/src/scope_outbox.d.ts.map +1 -0
- package/build/src/scope_outbox.js +298 -0
- package/build/src/scope_outbox.js.map +1 -0
- package/build/src/{drivers → shared}/backend_guard.d.ts +42 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -0
- package/build/src/{drivers → shared}/backend_guard.js +78 -1
- package/build/src/shared/backend_guard.js.map +1 -0
- package/build/src/shared/sql_expiry.d.ts.map +1 -0
- package/build/src/shared/sql_expiry.js.map +1 -0
- package/build/src/shared/transaction_guard.d.ts +50 -0
- package/build/src/shared/transaction_guard.d.ts.map +1 -0
- package/build/src/shared/transaction_guard.js +60 -0
- package/build/src/shared/transaction_guard.js.map +1 -0
- package/build/src/sql_descendants.d.ts +47 -1
- package/build/src/sql_descendants.d.ts.map +1 -1
- package/build/src/sql_descendants.js +75 -1
- package/build/src/sql_descendants.js.map +1 -1
- package/build/src/testing/contract.d.ts +129 -4
- package/build/src/testing/contract.d.ts.map +1 -1
- package/build/src/testing/contract.js +912 -177
- package/build/src/testing/contract.js.map +1 -1
- package/build/src/testing/main.d.ts +8 -2
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +4 -1
- package/build/src/testing/main.js.map +1 -1
- package/build/src/testing/migration_contract.d.ts +284 -0
- package/build/src/testing/migration_contract.d.ts.map +1 -0
- package/build/src/testing/migration_contract.js +586 -0
- package/build/src/testing/migration_contract.js.map +1 -0
- package/build/src/testing/relations_contract.d.ts +73 -0
- package/build/src/testing/relations_contract.d.ts.map +1 -0
- package/build/src/testing/relations_contract.js +1069 -0
- package/build/src/testing/relations_contract.js.map +1 -0
- package/build/src/testing/relations_reconcile_contract.d.ts +24 -0
- package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -0
- package/build/src/testing/relations_reconcile_contract.js +220 -0
- package/build/src/testing/relations_reconcile_contract.js.map +1 -0
- package/build/src/traits/authz_scopes.js +1 -1
- package/build/src/traits/authz_scopes.js.map +1 -1
- package/build/src/traits/has_uuid.d.ts +8 -8
- package/build/src/traits/has_uuid.d.ts.map +1 -1
- package/build/src/types.d.ts +1053 -83
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +10 -0
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/authorization.stub +104 -4
- package/build/stubs/migration.stub +136 -0
- package/build/stubs/scopes_outbox_migration.stub +57 -0
- package/package.json +4 -2
- package/build/commands/openfga_import.d.ts +0 -34
- package/build/commands/openfga_import.d.ts.map +0 -1
- package/build/commands/openfga_import.js +0 -97
- package/build/commands/openfga_import.js.map +0 -1
- package/build/src/catalog.d.ts.map +0 -1
- package/build/src/catalog.js.map +0 -1
- package/build/src/catalog_cache.d.ts.map +0 -1
- package/build/src/catalog_cache.js.map +0 -1
- package/build/src/drivers/backend_guard.d.ts.map +0 -1
- package/build/src/drivers/backend_guard.js.map +0 -1
- package/build/src/drivers/sql_expiry.d.ts.map +0 -1
- package/build/src/drivers/sql_expiry.js.map +0 -1
- package/build/src/middleware/app_access_middleware.d.ts.map +0 -1
- package/build/src/middleware/app_access_middleware.js.map +0 -1
- /package/build/src/{middleware → http}/app_access_middleware.d.ts +0 -0
- /package/build/src/{middleware → http}/app_access_middleware.js +0 -0
- /package/build/src/{drivers → shared}/sql_expiry.d.ts +0 -0
- /package/build/src/{drivers → shared}/sql_expiry.js +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RelationsManager` — la fachada de ReBAC (Fase 4, lote 4-2).
|
|
3
|
+
*
|
|
4
|
+
* Es el análogo de `AuthorizationManager` para el puerto `RelationsDriver`:
|
|
5
|
+
* aplica la POLICY antes de tocar el driver y despacha la auditoría después.
|
|
6
|
+
* Lo que fija este lote:
|
|
7
|
+
*
|
|
8
|
+
* - **F-05 (cierre del 🔴 del auditor)**: `relate`/`unrelate` con un
|
|
9
|
+
* `object.type` o una `relation` que NO estén declarados en
|
|
10
|
+
* `defineRelationsConfig` ⇒ 422 ANTES de llamar al driver. Junto con ⚪4
|
|
11
|
+
* (un tipo reservado no se puede declarar) hace IMPOSIBLE que un `relate`
|
|
12
|
+
* componga el id de un `role_binding` real y escale a `roles.authorize`.
|
|
13
|
+
* - **R-13**: `assertWrite` PURO (obligatorio aquí, a diferencia de
|
|
14
|
+
* `roles/`): si rechaza, nada toca el driver.
|
|
15
|
+
* - `actor` en `onRelationWrite` (auditoría del consumidor sin
|
|
16
|
+
* `AsyncLocalStorage`).
|
|
17
|
+
* - `membersOf` como método OPCIONAL DISTINTO de `listSubjects`: si el driver
|
|
18
|
+
* no lo trae (`membersOfNative: false`) ⇒ 500 `E_AUTHZ_UNSUPPORTED`
|
|
19
|
+
* nombrándolo, jamás un `skip`.
|
|
20
|
+
*
|
|
21
|
+
* Pureza (regla 2): solo depende del puerto, los errores, la gramática
|
|
22
|
+
* compartida y `defineRelationsConfig`. Nada de `manager`/`drivers/*`.
|
|
23
|
+
*/
|
|
24
|
+
import { ActorRequiredError, AuthorizationConfigError, UnsupportedOperationError } from '../errors.js';
|
|
25
|
+
import { assertScope, assertSubject, assertRelationId, assertExpiresAt } from '../identity.js';
|
|
26
|
+
import { isClock, systemClock } from '../clock.js';
|
|
27
|
+
import { assertNotFrozenRow } from '../freeze.js';
|
|
28
|
+
import { isRelUserset } from '../types.js';
|
|
29
|
+
import { assertRelationDeclared } from './define_relations_config.js';
|
|
30
|
+
export class RelationsManager {
|
|
31
|
+
#driver;
|
|
32
|
+
#config;
|
|
33
|
+
#options;
|
|
34
|
+
constructor(driver, config, options = {}) {
|
|
35
|
+
if (options.clock !== undefined) {
|
|
36
|
+
if (!isClock(options.clock)) {
|
|
37
|
+
throw new AuthorizationConfigError(`RelationsManager: clock debe ser una función () => Date (llegó ${typeof options.clock})`);
|
|
38
|
+
}
|
|
39
|
+
if (typeof driver.withClock !== 'function') {
|
|
40
|
+
throw new AuthorizationConfigError(`RelationsManager: el driver de relaciones no implementa withClock(now) y la config declara clock: ` +
|
|
41
|
+
`sin él la caducidad de las relaciones se decidiría con otro reloj que el declarado.`);
|
|
42
|
+
}
|
|
43
|
+
driver = driver.withClock(options.clock);
|
|
44
|
+
}
|
|
45
|
+
if (options.requireTransactionalWrites === true && driver.capabilities?.transactionalWrites !== true) {
|
|
46
|
+
throw new AuthorizationConfigError(`requireTransactionalWrites está en true y el driver de relaciones '${options.driverName ?? 'relations'}' ` +
|
|
47
|
+
`declara transactionalWrites: ${driver.capabilities ? String(driver.capabilities.transactionalWrites) : 'nada (sin capabilities)'}: ` +
|
|
48
|
+
`no puede inscribir relate/unrelate/purge* en la transacción del consumidor («los dos o ninguno»), así que ` +
|
|
49
|
+
`el RelationsManager no se construye. Usa un driver que la declare (database) o quita el flag ` +
|
|
50
|
+
`(relations.requireTransactionalWrites, o el del raíz).`);
|
|
51
|
+
}
|
|
52
|
+
this.#driver = driver;
|
|
53
|
+
this.#config = config;
|
|
54
|
+
this.#options = options;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* **Puerta 1 de `{ transaction }`** (L-2, siempre activa): una escritura con
|
|
58
|
+
* `{ transaction }` a un driver que no declara `transactionalWrites: true`
|
|
59
|
+
* es 500 `E_AUTHZ_UNSUPPORTED` nombrando driver y operación, ANTES de la
|
|
60
|
+
* barrera, de F-05 y del driver (cero llamadas). Encolar ≠ escribir: aquí
|
|
61
|
+
* la transacción ESCRIBE la tupla dentro de ella, no hay outbox.
|
|
62
|
+
*/
|
|
63
|
+
#assertTransactional(options, operation) {
|
|
64
|
+
if (options?.transaction === undefined || options.transaction === null)
|
|
65
|
+
return;
|
|
66
|
+
if (this.#driver.capabilities?.transactionalWrites === true)
|
|
67
|
+
return;
|
|
68
|
+
throw UnsupportedOperationError.transactional(`relations.${operation}`, this.#options.driverName ?? 'relations', 'relations');
|
|
69
|
+
}
|
|
70
|
+
/** El driver crudo — salida documentada de las barreras (plataforma y tests). */
|
|
71
|
+
driver() {
|
|
72
|
+
return this.#driver;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* **F-05**: rechaza un tipo/relación no declarado ANTES de tocar el driver.
|
|
76
|
+
* La gramática de identidad no basta: `role_binding`/`assignee` son
|
|
77
|
+
* gramaticalmente válidos y en el store compartido componen el id de un
|
|
78
|
+
* binding real. Es la MISMA función que aplican los dos drivers (L-0,
|
|
79
|
+
* `assertRelationDeclared`): el manager corta primero; el driver es la red
|
|
80
|
+
* para quien entra por `manager.driver()` o por `reconcileRelations`.
|
|
81
|
+
*/
|
|
82
|
+
#assertDeclared(object, relation) {
|
|
83
|
+
assertRelationDeclared(this.#config, object, relation);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* **R-16**: el `object.id` con la gramática estricta de relaciones ANTES del
|
|
87
|
+
* driver ⇒ 422 `E_AUTHZ_INVALID_IDENTITY`. `#assertDeclared` solo valida el
|
|
88
|
+
* TIPO (F-05): un `object.id` con un `|`/`#`/`:`/espacio se colaba y en
|
|
89
|
+
* `openfga` se ESCRIBÍA (invisibilidad de enumeración y pérdida en
|
|
90
|
+
* `reconcile`) o salía como 503 mal clasificado. Se valida aquí (una vez) y
|
|
91
|
+
* en cada driver (defensa en profundidad).
|
|
92
|
+
*/
|
|
93
|
+
#assertObjectId(object) {
|
|
94
|
+
assertRelationId(`el id del objeto '${object?.type}'`, object?.id);
|
|
95
|
+
}
|
|
96
|
+
#assertActor(options) {
|
|
97
|
+
if (this.#options.requireActor && !options?.actor) {
|
|
98
|
+
throw new ActorRequiredError(`Escritura de relación sin 'actor' y la config exige uno (requireActor).`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* **La barrera del freeze, la MISMA que la de roles** (L-1 · J1, juez del
|
|
103
|
+
* panel `{trx}`): hasta L-1 no había una sola referencia al freeze en
|
|
104
|
+
* `relations/`, así que durante un cutover las escrituras de roles recibían
|
|
105
|
+
* 503 y las de relaciones ENTRABAN, y `authz:relations:reconcile`
|
|
106
|
+
* certificaba un estado que podía cambiar debajo. Es la fila `id = 2` de
|
|
107
|
+
* `authz_catalog_version` leída por la conexión del motor (`assertNotFrozenRow`,
|
|
108
|
+
* `src/freeze.ts` — raíz de `src/`, así que la regla 2 de pureza se cumple),
|
|
109
|
+
* delante de las CUATRO escrituras y antes de validar nada: 503
|
|
110
|
+
* `E_AUTHZ_FROZEN` reintentable. Las lecturas no pasan por aquí.
|
|
111
|
+
* `manager.driver()` sigue siendo la salida documentada.
|
|
112
|
+
*/
|
|
113
|
+
#assertNotFrozen(operation) {
|
|
114
|
+
return assertNotFrozenRow(`relations.${operation}`, {
|
|
115
|
+
driver: 'relations',
|
|
116
|
+
nowMs: (this.#options.clock ?? systemClock)().getTime(),
|
|
117
|
+
timeoutMs: this.#options.freezeTimeoutMs,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async relate(subject, relation, object, partition, options) {
|
|
121
|
+
this.#assertTransactional(options, 'relate');
|
|
122
|
+
await this.#assertNotFrozen('relate');
|
|
123
|
+
this.#assertDeclared(object, relation);
|
|
124
|
+
this.#assertObjectId(object);
|
|
125
|
+
this.#assertActor(options);
|
|
126
|
+
assertScope(partition);
|
|
127
|
+
this.#assertSubject(subject, partition);
|
|
128
|
+
// R-15: `expiresAt` en sus tres estados legales (omitido / null / Date
|
|
129
|
+
// válida); cualquier otra cosa es 422 ANTES del driver (invariante 5).
|
|
130
|
+
assertExpiresAt(options?.expiresAt);
|
|
131
|
+
const ref = { operation: 'relate', subject, relation, object, partition };
|
|
132
|
+
if (options && 'expiresAt' in options)
|
|
133
|
+
ref.expiresAt = options.expiresAt;
|
|
134
|
+
this.#options.assertWrite?.(ref);
|
|
135
|
+
await this.#driver.relate(subject, relation, object, partition, options);
|
|
136
|
+
this.#options.onRelationWrite?.({ ...ref, actor: options?.actor });
|
|
137
|
+
}
|
|
138
|
+
async unrelate(subject, relation, object, partition, options) {
|
|
139
|
+
this.#assertTransactional(options, 'unrelate');
|
|
140
|
+
await this.#assertNotFrozen('unrelate');
|
|
141
|
+
this.#assertDeclared(object, relation);
|
|
142
|
+
this.#assertObjectId(object);
|
|
143
|
+
this.#assertActor(options);
|
|
144
|
+
assertScope(partition);
|
|
145
|
+
this.#assertSubject(subject, partition);
|
|
146
|
+
const ref = { operation: 'unrelate', subject, relation, object, partition };
|
|
147
|
+
this.#options.assertWrite?.(ref);
|
|
148
|
+
await this.#driver.unrelate(subject, relation, object, partition, options);
|
|
149
|
+
this.#options.onRelationWrite?.({ ...ref, actor: options?.actor });
|
|
150
|
+
}
|
|
151
|
+
async check(subject, relation, object, partition) {
|
|
152
|
+
assertScope(partition);
|
|
153
|
+
this.#assertObjectId(object);
|
|
154
|
+
return this.#driver.check(subject, relation, object, partition);
|
|
155
|
+
}
|
|
156
|
+
async listObjects(subject, relation, objectType, partition, page) {
|
|
157
|
+
assertScope(partition);
|
|
158
|
+
return this.#driver.listObjects(subject, relation, objectType, partition, page);
|
|
159
|
+
}
|
|
160
|
+
async listSubjects(relation, object, partition, page) {
|
|
161
|
+
assertScope(partition);
|
|
162
|
+
this.#assertObjectId(object);
|
|
163
|
+
return this.#driver.listSubjects(relation, object, partition, page);
|
|
164
|
+
}
|
|
165
|
+
async purgeObject(object, partition, options) {
|
|
166
|
+
this.#assertTransactional(options, 'purgeObject');
|
|
167
|
+
await this.#assertNotFrozen('purgeObject');
|
|
168
|
+
assertScope(partition);
|
|
169
|
+
this.#assertObjectId(object);
|
|
170
|
+
return this.#driver.purgeObject(object, partition, options);
|
|
171
|
+
}
|
|
172
|
+
async purgeSubject(subject, partition, options) {
|
|
173
|
+
this.#assertTransactional(options, 'purgeSubject');
|
|
174
|
+
await this.#assertNotFrozen('purgeSubject');
|
|
175
|
+
assertScope(partition);
|
|
176
|
+
return this.#driver.purgeSubject(subject, partition, options);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Membresía TRANSITIVA de un grupo (a través de usersets anidados) —
|
|
180
|
+
* DISTINTA de `listSubjects`, que da los hechos DIRECTOS. Solo la trae el
|
|
181
|
+
* driver `database` (`membersOfNative: true`); en `openfga` es 500
|
|
182
|
+
* `E_AUTHZ_UNSUPPORTED` nombrándola (la transitiva sería `ListUsers`, que
|
|
183
|
+
* trunca), jamás un `skip`.
|
|
184
|
+
*/
|
|
185
|
+
async membersOf(object, relation, partition, page) {
|
|
186
|
+
assertScope(partition);
|
|
187
|
+
if (!this.#driver.membersOf || !this.#driver.capabilities?.membersOfNative) {
|
|
188
|
+
throw new UnsupportedOperationError('membersOf', 'la membresía transitiva de un grupo', 'relations', `Solo el driver 'database' la resuelve (CTE recursiva); en 'openfga' la transitiva sería ` +
|
|
189
|
+
`'ListUsers', que trunca. Usa 'listSubjects' para los hechos directos.`);
|
|
190
|
+
}
|
|
191
|
+
return this.#driver.membersOf(object, relation, partition, page);
|
|
192
|
+
}
|
|
193
|
+
/** ORIGEN de `authz:reconcile` de relaciones. 500 `E_AUTHZ_UNSUPPORTED` si el driver no lo trae. */
|
|
194
|
+
async enumerateRelations(partition, page) {
|
|
195
|
+
assertScope(partition);
|
|
196
|
+
if (!this.#driver.enumerateRelations || !this.#driver.capabilities?.enumerateRelations) {
|
|
197
|
+
throw new UnsupportedOperationError('enumerateRelations', 'ser origen de authz:reconcile de relaciones', 'relations');
|
|
198
|
+
}
|
|
199
|
+
return this.#driver.enumerateRelations(partition, page);
|
|
200
|
+
}
|
|
201
|
+
#assertSubject(subject, partition) {
|
|
202
|
+
if (isRelUserset(subject)) {
|
|
203
|
+
// El userset (`group:g#member`) también se ancla a la partición; su
|
|
204
|
+
// objeto se valida como cualquier otro objeto declarado (tipo + id, R-16).
|
|
205
|
+
this.#assertDeclared(subject.object, subject.relation);
|
|
206
|
+
this.#assertObjectId(subject.object);
|
|
207
|
+
assertScope(partition);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
assertSubject(subject);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/relations/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Clock } from '../clock.js';
|
|
2
|
+
import type { RelationsDriver, ScopeRef } from '../types.js';
|
|
3
|
+
import type { RelationsConfig } from './define_relations_config.js';
|
|
4
|
+
export interface RelationsReconcileOptions {
|
|
5
|
+
/** El driver del que se LEEN las tuplas (origen). Necesita `enumerateRelations`. */
|
|
6
|
+
from: RelationsDriver;
|
|
7
|
+
/** El driver al que se ESCRIBEN (destino). Necesita `enumerateRelations` para ver lo que sobra. */
|
|
8
|
+
to: RelationsDriver;
|
|
9
|
+
/** La partición a migrar (mono-tenant: `APP_SCOPE`). El reconcile es POR partición. */
|
|
10
|
+
partition: ScopeRef;
|
|
11
|
+
/** Solo reporta lo que HARÍA; cero escrituras (read-only por contrato, como el de roles). */
|
|
12
|
+
dryRun?: boolean;
|
|
13
|
+
/** Borra del destino las tuplas que el origen ya no respalda. Sin él, solo añade. */
|
|
14
|
+
prune?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* **El seguro de borrado masivo** (R-17): con `prune` y un ORIGEN sin una
|
|
17
|
+
* sola tupla utilizable, borrar TODO el destino es 500
|
|
18
|
+
* `E_AUTHZ_MASS_RECONCILE_REFUSED` (la firma de un `--from` equivocado o de un
|
|
19
|
+
* store vacío). `allowMassDelete: true` (`--allow-mass-delete`) es la decisión
|
|
20
|
+
* humana de vaciarlo de verdad; `--dry-run` no lanza, lo marca en `massDelete`.
|
|
21
|
+
* Paridad con el seguro de `authz:reconcile` de roles.
|
|
22
|
+
*/
|
|
23
|
+
allowMassDelete?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* La config del DESTINO. Dos usos: vigilar la deriva del modelo fusionado
|
|
26
|
+
* (un tipo del origen que el destino NO declara se reporta en `modelDrift`)
|
|
27
|
+
* y **embudar F-05 ANTES del driver** (L-0): la tupla cuyo tipo o relación
|
|
28
|
+
* el destino no declara se descarta aquí y se cuenta en
|
|
29
|
+
* `skipped.undeclared`, de modo que `--dry-run` y la pasada real dan los
|
|
30
|
+
* MISMOS números. Sin ella, la embuda el 422 del propio driver del destino
|
|
31
|
+
* (la misma función, `assertRelationDeclared`) y se cuenta igual — pero
|
|
32
|
+
* `--dry-run` no puede anticiparla (la reporta como `written`).
|
|
33
|
+
*/
|
|
34
|
+
toConfig?: RelationsConfig;
|
|
35
|
+
/**
|
|
36
|
+
* El reloj que decide qué tupla del ORIGEN está CADUCADA (R-15): una caducada
|
|
37
|
+
* no se migra —se cuenta en `skipped.expired`— y no respalda nada en el
|
|
38
|
+
* destino. Default `systemClock`; inyectable para el juez.
|
|
39
|
+
*/
|
|
40
|
+
now?: Clock;
|
|
41
|
+
}
|
|
42
|
+
export interface RelationsReconcileReport {
|
|
43
|
+
/** ¿Fue `--dry-run` (sin escrituras)? */
|
|
44
|
+
dryRun: boolean;
|
|
45
|
+
/** Tuplas nuevas escritas en el destino (o que se escribirían, en `--dry-run`). */
|
|
46
|
+
written: number;
|
|
47
|
+
/** Tuplas que sobraban en el destino y se borraron (solo con `prune`; o que se borrarían). */
|
|
48
|
+
deleted: number;
|
|
49
|
+
/**
|
|
50
|
+
* Tuplas que estaban en el destino con OTRA caducidad y se reescribieron con
|
|
51
|
+
* la del origen (R-15; delete+write en ambos drivers, nunca un `Ignore` a
|
|
52
|
+
* ciegas que se quedara la caducidad vieja — la lección S7 de la 3b).
|
|
53
|
+
*/
|
|
54
|
+
updated: number;
|
|
55
|
+
/** Tuplas que ya estaban exactamente igual (misma clave y misma caducidad). */
|
|
56
|
+
unchanged: number;
|
|
57
|
+
/**
|
|
58
|
+
* Tuplas del destino que el origen ya no respalda y que NO se borraron por
|
|
59
|
+
* no pasar `--prune` (deriva recuperable; con `prune` pasan a `deleted`).
|
|
60
|
+
*/
|
|
61
|
+
extra: number;
|
|
62
|
+
/**
|
|
63
|
+
* Deriva del modelo fusionado del destino (`--dry-run`): tipos de relación
|
|
64
|
+
* del origen que la config del destino no declara. Vacío si no aplica.
|
|
65
|
+
*/
|
|
66
|
+
modelDrift: string[];
|
|
67
|
+
/**
|
|
68
|
+
* **El seguro de borrado masivo se disparó** (R-17): `prune` iba a borrar
|
|
69
|
+
* tuplas y el ORIGEN no aportó ni una utilizable. En una pasada real es 500
|
|
70
|
+
* (salvo `allowMassDelete`); en `--dry-run` NO lanza y esto lo marca.
|
|
71
|
+
*/
|
|
72
|
+
massDelete: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Lo que el origen ENUMERÓ y no se migró, con su motivo:
|
|
75
|
+
* - `expired` (R-15) = tuplas caducadas al instante de la pasada. Es la
|
|
76
|
+
* pérdida DECLARADA de la migración (como `expired` en `authz:reconcile`
|
|
77
|
+
* de roles, 3b-8 · B2): se cuenta, no clava el exit 1. Un origen que las
|
|
78
|
+
* filtrara antes de enumerarlas las haría desaparecer sin rastro; por eso
|
|
79
|
+
* tienen que LLEGAR.
|
|
80
|
+
* - `undeclared` (L-0) = tuplas VIVAS cuyo `object.type` o `relation` el
|
|
81
|
+
* DESTINO no declara (F-05): no se escriben —hasta L-0 `modelDrift` las
|
|
82
|
+
* reportaba y `to.relate` las escribía igual, por el driver— y se cuentan
|
|
83
|
+
* aquí. Es una relación viva que no llegó, así que SÍ es deriva
|
|
84
|
+
* (exit ≠ 0 en el comando): declárala en el destino y repite la pasada.
|
|
85
|
+
*/
|
|
86
|
+
skipped: {
|
|
87
|
+
expired: number;
|
|
88
|
+
undeclared: number;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* **La ventana durable bajo la que corrió la pasada que ESCRIBE** (L-1 ·
|
|
92
|
+
* J1; paridad con `report.frozen` de `authz:reconcile` de roles). La pone
|
|
93
|
+
* `runRelationsReconcile` (el comando), no esta función —que no ve al
|
|
94
|
+
* manager de roles—; ausente en `--dry-run` (el verificador no congela) y
|
|
95
|
+
* cuando se llama a `reconcileRelations` a pelo. `lapsed: true` = el lease
|
|
96
|
+
* se perdió a mitad ⇒ la pasada NO se certifica (exit ≠ 0).
|
|
97
|
+
*/
|
|
98
|
+
frozen?: {
|
|
99
|
+
durable: true;
|
|
100
|
+
lapsed: boolean;
|
|
101
|
+
leaseMs: number | null;
|
|
102
|
+
fence: number;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Migra las tuplas de relación de `from` a `to` en `partition`. Idempotente
|
|
107
|
+
* (una segunda pasada no escribe nada) y bidireccional (funciona en cualquier
|
|
108
|
+
* sentido). Nunca silenciosa: devuelve `written`/`deleted`/`unchanged`/`extra`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function reconcileRelations(options: RelationsReconcileOptions): Promise<RelationsReconcileReport>;
|
|
111
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../src/relations/reconcile.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EAA6B,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAkBnE,MAAM,WAAW,yBAAyB;IACxC,oFAAoF;IACpF,IAAI,EAAE,eAAe,CAAA;IACrB,mGAAmG;IACnG,EAAE,EAAE,eAAe,CAAA;IACnB,uFAAuF;IACvF,SAAS,EAAE,QAAQ,CAAA;IACnB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;;;OAIG;IACH,GAAG,CAAC,EAAE,KAAK,CAAA;CACZ;AAED,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAA;IACf,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAA;IACf,8FAA8F;IAC9F,OAAO,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;;;;;;;;OAYG;IACH,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACnF;AA8BD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CA8G9G"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **`authz:reconcile` de relaciones** (Fase 4, lote 4-5) — migra las tuplas de
|
|
3
|
+
* relación de un `RelationsDriver` a otro, idempotente y bidireccional, nunca
|
|
4
|
+
* silenciosa. Es el análogo de `manager.reconcile` de roles, pero para el
|
|
5
|
+
* puerto `RelationsDriver`: solo hay HECHOS de relación (no árbol ni catálogo),
|
|
6
|
+
* así que es más simple —lee el ORIGEN por `enumerateRelations`, lee el DESTINO
|
|
7
|
+
* ENTERO (la única forma de ver lo que SOBRA), y aplica writes y —con
|
|
8
|
+
* `prune`— deletes—.
|
|
9
|
+
*
|
|
10
|
+
* **La lección M1/M3/T4 de la 3b** (el agujero del 3b-4·C1): un contrato que
|
|
11
|
+
* solo cruza `report.skipped` se pasa PERDIENDO datos. El verificador de
|
|
12
|
+
* verdad es el CENSO (`src/testing/relations_reconcile_contract.ts`): busca
|
|
13
|
+
* cada hecho sembrado en el destino UNO A UNO por el camino DIRECTO
|
|
14
|
+
* (`listSubjects`), no le pregunta al origen lleno. Un tramo omisivo o una
|
|
15
|
+
* pérdida de `subject_relation` (un userset migrado como holder) o de
|
|
16
|
+
* partición se ve ROJA ahí, no en los contadores.
|
|
17
|
+
*
|
|
18
|
+
* Pureza (regla 2): este módulo vive en `src/relations/` y solo depende del
|
|
19
|
+
* puerto (`../types.js`), los errores y la config de relaciones. NO importa
|
|
20
|
+
* `manager` ni `drivers/*`: los drivers concretos los inyecta el llamante (la
|
|
21
|
+
* plataforma), como `manager.reconcile` recibe el destino por su clave.
|
|
22
|
+
*/
|
|
23
|
+
import { MassReconcileRefusedError, RelationTypeUnknownError, RelationUnknownError, UnsupportedOperationError, } from '../errors.js';
|
|
24
|
+
import { systemClock } from '../clock.js';
|
|
25
|
+
import { isActiveExpiry, sameInstant } from '../expiry.js';
|
|
26
|
+
import { isRelUserset } from '../types.js';
|
|
27
|
+
/** La clave estable de una tupla de relación (partición + objeto + relación + sujeto). */
|
|
28
|
+
function subjectKey(subject) {
|
|
29
|
+
if (isRelUserset(subject))
|
|
30
|
+
return `${subject.object.type}:${subject.object.id}#${subject.relation}`;
|
|
31
|
+
return `${subject.type}:${subject.uuid}`;
|
|
32
|
+
}
|
|
33
|
+
function scopeKeyOf(scope) {
|
|
34
|
+
return scope.type === 'app' ? 'app' : `${scope.type}|${scope.uuid}`;
|
|
35
|
+
}
|
|
36
|
+
function tupleKey(tuple) {
|
|
37
|
+
return `${scopeKeyOf(tuple.partition)}|${tuple.object.type}:${tuple.object.id}#${tuple.relation}@${subjectKey(tuple.subject)}`;
|
|
38
|
+
}
|
|
39
|
+
/** Enumera TODAS las tuplas de una partición de un driver (paginando el cursor). */
|
|
40
|
+
async function enumerateAll(driver, partition, role) {
|
|
41
|
+
if (!driver.enumerateRelations || !driver.capabilities?.enumerateRelations) {
|
|
42
|
+
throw new UnsupportedOperationError('enumerateRelations', `ser ${role} de authz:reconcile de relaciones`, 'relations', `El driver ${role} no sabe enumerar sus tuplas (capacidad 'enumerateRelations': false). ` +
|
|
43
|
+
`Sin poder leer ${role === 'destino' ? 'el destino no se ve qué sobra' : 'el origen no hay nada que migrar'}.`);
|
|
44
|
+
}
|
|
45
|
+
const out = [];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
let after;
|
|
48
|
+
let pages = 0;
|
|
49
|
+
do {
|
|
50
|
+
const page = await driver.enumerateRelations(partition, after ? { after } : undefined);
|
|
51
|
+
out.push(...page.tuples);
|
|
52
|
+
after = page.cursor || undefined;
|
|
53
|
+
if (after) {
|
|
54
|
+
if (seen.has(after))
|
|
55
|
+
throw new Error(`enumerateRelations (${role}): el cursor '${after}' se repite; no avanza`);
|
|
56
|
+
seen.add(after);
|
|
57
|
+
if (++pages > 100_000)
|
|
58
|
+
throw new Error(`enumerateRelations (${role}): más de 100000 páginas sin agotar`);
|
|
59
|
+
}
|
|
60
|
+
} while (after);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Migra las tuplas de relación de `from` a `to` en `partition`. Idempotente
|
|
65
|
+
* (una segunda pasada no escribe nada) y bidireccional (funciona en cualquier
|
|
66
|
+
* sentido). Nunca silenciosa: devuelve `written`/`deleted`/`unchanged`/`extra`.
|
|
67
|
+
*/
|
|
68
|
+
export async function reconcileRelations(options) {
|
|
69
|
+
const { from, to, partition, dryRun = false, prune = false, allowMassDelete = false, toConfig } = options;
|
|
70
|
+
const now = (options.now ?? systemClock)();
|
|
71
|
+
const enumerated = await enumerateAll(from, partition, 'origen');
|
|
72
|
+
const destTuples = await enumerateAll(to, partition, 'destino');
|
|
73
|
+
// R-15: la caducada LLEGA del origen (el puerto lo exige) y aquí se DESCARTA
|
|
74
|
+
// contándola: no se migra ni respalda nada en el destino. Decidido con UN
|
|
75
|
+
// `now` para toda la pasada.
|
|
76
|
+
// L-0: la tupla cuyo tipo o relación el DESTINO no declara (F-05) se
|
|
77
|
+
// descarta AQUÍ cuando hay `toConfig` —antes del driver, así `--dry-run` y la
|
|
78
|
+
// pasada real dan los mismos números— y tampoco respalda nada en el destino
|
|
79
|
+
// (una que ya estuviera allí es `extra`, y `--prune` la barre). El tipo se
|
|
80
|
+
// reporta además en `modelDrift` (deriva del modelo fusionado).
|
|
81
|
+
const skipped = { expired: 0, undeclared: 0 };
|
|
82
|
+
const modelDrift = [];
|
|
83
|
+
const sourceTuples = [];
|
|
84
|
+
for (const tuple of enumerated) {
|
|
85
|
+
if (!isActiveExpiry(tuple.expiresAt ?? null, now)) {
|
|
86
|
+
skipped.expired += 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (toConfig && !declaredIn(toConfig, tuple.object.type)) {
|
|
90
|
+
skipped.undeclared += 1;
|
|
91
|
+
if (!modelDrift.includes(tuple.object.type))
|
|
92
|
+
modelDrift.push(tuple.object.type);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (toConfig && !toConfig.isDeclared(tuple.object.type, tuple.relation)) {
|
|
96
|
+
skipped.undeclared += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
sourceTuples.push(tuple);
|
|
100
|
+
}
|
|
101
|
+
const sourceByKey = new Map();
|
|
102
|
+
for (const tuple of sourceTuples)
|
|
103
|
+
sourceByKey.set(tupleKey(tuple), tuple);
|
|
104
|
+
const destByKey = new Map();
|
|
105
|
+
for (const tuple of destTuples)
|
|
106
|
+
destByKey.set(tupleKey(tuple), tuple);
|
|
107
|
+
const toWrite = [];
|
|
108
|
+
const toUpdate = [];
|
|
109
|
+
let unchanged = 0;
|
|
110
|
+
for (const [key, tuple] of sourceByKey) {
|
|
111
|
+
const present = destByKey.get(key);
|
|
112
|
+
if (!present)
|
|
113
|
+
toWrite.push(tuple);
|
|
114
|
+
// Misma clave, OTRA caducidad ⇒ se reescribe con la del origen (R-15).
|
|
115
|
+
else if (!sameInstant(present.expiresAt ?? null, tuple.expiresAt ?? null))
|
|
116
|
+
toUpdate.push(tuple);
|
|
117
|
+
else
|
|
118
|
+
unchanged += 1;
|
|
119
|
+
}
|
|
120
|
+
const toDelete = destTuples.filter((tuple) => !sourceByKey.has(tupleKey(tuple)));
|
|
121
|
+
// **El seguro de borrado masivo** (R-17, paridad con `authz:reconcile` de
|
|
122
|
+
// roles / AA2). `prune` + un ORIGEN sin una sola tupla utilizable + un destino
|
|
123
|
+
// con tuplas que borrar es la firma de un `--from` equivocado o de un store
|
|
124
|
+
// vacío: vaciar el destino entero se rechaza con 500 antes de tocar nada,
|
|
125
|
+
// salvo `allowMassDelete`. En `--dry-run` no lanza, lo marca. «Utilizable» =
|
|
126
|
+
// una tupla VIVA del origen que cabe en el destino (las que `skipped`
|
|
127
|
+
// recoge no migran, así que no cuentan como respaldo).
|
|
128
|
+
const usableSource = sourceTuples.length;
|
|
129
|
+
const massDelete = prune && toDelete.length > 0 && usableSource === 0;
|
|
130
|
+
if (massDelete && !dryRun && !allowMassDelete) {
|
|
131
|
+
throw new MassReconcileRefusedError(`authz:relations:reconcile --prune borraría ${toDelete.length} tupla(s) de relación del destino y el ` +
|
|
132
|
+
`ORIGEN no ha aportado NI UNA utilizable (${enumerated.length} leídas` +
|
|
133
|
+
`${toConfig ? ', todas de tipos que el destino no declara' : ''}` +
|
|
134
|
+
`${skipped.expired ? `, ${skipped.expired} caducada(s)` : ''}). Eso es la firma de un --from ` +
|
|
135
|
+
`equivocado o de un store vacío, no de un destino que sobra: esta pasada dejaría la partición sin ` +
|
|
136
|
+
`una sola relación. Comprueba el --from y el store; si de verdad quieres vaciarlo, --allow-mass-delete.`);
|
|
137
|
+
}
|
|
138
|
+
let written = toWrite.length;
|
|
139
|
+
let updated = toUpdate.length;
|
|
140
|
+
if (!dryRun) {
|
|
141
|
+
// La caducidad viaja EXPLÍCITA (`Date` la fija, `null` la quita): un
|
|
142
|
+
// `relate` sin opciones PRESERVARÍA la del destino (invariante 10).
|
|
143
|
+
// L-0: el DRIVER del destino aplica F-05 (`assertRelationDeclared`), así que
|
|
144
|
+
// sin `toConfig` —o con una que no coincida con la del driver— es SU 422 el
|
|
145
|
+
// que embuda la tupla no declarada: se cuenta en `skipped.undeclared` y se
|
|
146
|
+
// descuenta de `written`/`updated`. Nunca un throw a mitad de pasada por
|
|
147
|
+
// una tupla que el reporte puede contar, y nunca un silencio.
|
|
148
|
+
for (const tuple of toWrite) {
|
|
149
|
+
if (await relateOrSkip(to, tuple))
|
|
150
|
+
continue;
|
|
151
|
+
skipped.undeclared += 1;
|
|
152
|
+
written -= 1;
|
|
153
|
+
}
|
|
154
|
+
for (const tuple of toUpdate) {
|
|
155
|
+
if (await relateOrSkip(to, tuple))
|
|
156
|
+
continue;
|
|
157
|
+
skipped.undeclared += 1;
|
|
158
|
+
updated -= 1;
|
|
159
|
+
}
|
|
160
|
+
if (prune) {
|
|
161
|
+
for (const tuple of toDelete) {
|
|
162
|
+
await to.unrelate(tuple.subject, tuple.relation, tuple.object, tuple.partition);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
dryRun,
|
|
168
|
+
written,
|
|
169
|
+
updated,
|
|
170
|
+
deleted: prune ? toDelete.length : 0,
|
|
171
|
+
unchanged,
|
|
172
|
+
extra: prune ? 0 : toDelete.length,
|
|
173
|
+
modelDrift,
|
|
174
|
+
massDelete,
|
|
175
|
+
skipped,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/** ¿El tipo de objeto cabe en el modelo fusionado del destino? (`group` es built-in: `hasType` lo incluye.) */
|
|
179
|
+
function declaredIn(config, objectType) {
|
|
180
|
+
return config.hasType(objectType);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Escribe la tupla en el destino; `false` si el DRIVER la rechazó por F-05
|
|
184
|
+
* (tipo o relación no declarados: 422 `E_AUTHZ_RELATION_TYPE_UNKNOWN` /
|
|
185
|
+
* `E_AUTHZ_RELATION_UNKNOWN`). Cualquier otro error se propaga tal cual.
|
|
186
|
+
*/
|
|
187
|
+
async function relateOrSkip(to, tuple) {
|
|
188
|
+
try {
|
|
189
|
+
await to.relate(tuple.subject, tuple.relation, tuple.object, tuple.partition, {
|
|
190
|
+
expiresAt: tuple.expiresAt ?? null,
|
|
191
|
+
});
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (error instanceof RelationTypeUnknownError || error instanceof RelationUnknownError)
|
|
196
|
+
return false;
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../src/relations/reconcile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RelationsConfig, RelationsConfigSpec } from './relations/define_relations_config.js';
|
|
2
|
+
export interface RelationsConfigStoreOptions {
|
|
3
|
+
connection?: string;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* **Guarda (persiste) la config de relaciones** bajo el gate de versión: la
|
|
8
|
+
* valida (⚪4/F-05 de config), la serializa a JSON y hace UPSERT de la fila
|
|
9
|
+
* `id=1` dentro de `withAuthzCatalogWrite` —que sube la versión compartida como
|
|
10
|
+
* última sentencia (invariante 14): un sync en otro proceso la ve en la
|
|
11
|
+
* siguiente pregunta—. Devuelve la config congelada.
|
|
12
|
+
*/
|
|
13
|
+
export declare function saveRelationsConfig(spec: RelationsConfigSpec, options?: RelationsConfigStoreOptions): Promise<RelationsConfig>;
|
|
14
|
+
/** Lee el spec persistido (o `null` si no hay relaciones declaradas — opt-in). */
|
|
15
|
+
export declare function readRelationsConfigSpec(options?: RelationsConfigStoreOptions): Promise<RelationsConfigSpec | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Lee la config persistida ya VALIDADA (o `null`). Es lo que hace 🟡3: un
|
|
18
|
+
* proceso SIN `defineRelationsConfig` en memoria recupera aquí los tipos de
|
|
19
|
+
* relación de la base, así que el modelo que republique los lleva.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readRelationsConfig(options?: RelationsConfigStoreOptions): Promise<RelationsConfig | null>;
|
|
22
|
+
//# sourceMappingURL=relations_config_store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations_config_store.d.ts","sourceRoot":"","sources":["../../src/relations_config_store.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAMlG,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAMD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,mBAAmB,EACzB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,eAAe,CAAC,CAc1B;AAED,kFAAkF;AAClF,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAMrC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAGjC"}
|