@jantstack/adonis-authz 2.0.0-alpha.1 → 2.4.0-alpha.10
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 +934 -40
- package/build/commands/authz_catalog_diff.d.ts.map +1 -1
- package/build/commands/authz_catalog_diff.js +1 -2
- 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 +135 -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 +94 -0
- package/build/commands/authz_freeze.js.map +1 -0
- package/build/commands/authz_reconcile.d.ts +111 -0
- package/build/commands/authz_reconcile.d.ts.map +1 -0
- package/build/commands/authz_reconcile.js +417 -0
- package/build/commands/authz_reconcile.js.map +1 -0
- package/build/commands/authz_relations_reconcile.d.ts +144 -0
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
- package/build/commands/authz_relations_reconcile.js +438 -0
- package/build/commands/authz_relations_reconcile.js.map +1 -0
- package/build/commands/authz_scopes_relay.d.ts +97 -0
- package/build/commands/authz_scopes_relay.d.ts.map +1 -0
- package/build/commands/authz_scopes_relay.js +344 -0
- package/build/commands/authz_scopes_relay.js.map +1 -0
- package/build/commands/authz_scopes_requeue.d.ts +80 -0
- package/build/commands/authz_scopes_requeue.d.ts.map +1 -0
- package/build/commands/authz_scopes_requeue.js +230 -0
- package/build/commands/authz_scopes_requeue.js.map +1 -0
- package/build/commands/authz_unfreeze.d.ts +45 -0
- package/build/commands/authz_unfreeze.d.ts.map +1 -0
- package/build/commands/authz_unfreeze.js +157 -0
- package/build/commands/authz_unfreeze.js.map +1 -0
- package/build/commands/main.d.ts +16 -8
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +59 -6
- package/build/commands/main.js.map +1 -1
- package/build/commands/openfga_provision.d.ts +92 -4
- package/build/commands/openfga_provision.d.ts.map +1 -1
- package/build/commands/openfga_provision.js +155 -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 -11
- package/build/index.d.ts.map +1 -1
- package/build/index.js +49 -7
- package/build/index.js.map +1 -1
- package/build/providers/authz_provider.d.ts +31 -2
- package/build/providers/authz_provider.d.ts.map +1 -1
- package/build/providers/authz_provider.js +65 -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 +206 -3
- package/build/src/define_config.d.ts.map +1 -1
- package/build/src/define_config.js +11 -0
- 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 +140 -0
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/database_relations_driver.js +748 -0
- package/build/src/drivers/database_relations_driver.js.map +1 -0
- package/build/src/drivers/openfga_driver.d.ts +777 -122
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +2228 -477
- 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 +186 -0
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/openfga_relations_driver.js +724 -0
- package/build/src/drivers/openfga_relations_driver.js.map +1 -0
- package/build/src/errors.d.ts +430 -6
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +453 -6
- package/build/src/errors.js.map +1 -1
- package/build/src/freeze.d.ts +200 -0
- package/build/src/freeze.d.ts.map +1 -0
- package/build/src/freeze.js +306 -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 +549 -6
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +2243 -193
- 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 +109 -0
- package/build/src/relations/define_relations_config.d.ts.map +1 -0
- package/build/src/relations/define_relations_config.js +211 -0
- package/build/src/relations/define_relations_config.js.map +1 -0
- package/build/src/relations/manager.d.ts +138 -0
- package/build/src/relations/manager.d.ts.map +1 -0
- package/build/src/relations/manager.js +494 -0
- package/build/src/relations/manager.js.map +1 -0
- package/build/src/relations/reconcile.d.ts +137 -0
- package/build/src/relations/reconcile.d.ts.map +1 -0
- package/build/src/relations/reconcile.js +294 -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 +123 -0
- package/build/src/scope_outbox.d.ts.map +1 -0
- package/build/src/scope_outbox.js +532 -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/{drivers → shared}/sql_expiry.d.ts +2 -0
- package/build/src/shared/sql_expiry.d.ts.map +1 -0
- package/build/src/{drivers → shared}/sql_expiry.js +4 -0
- package/build/src/shared/sql_expiry.js.map +1 -0
- package/build/src/shared/transaction_guard.d.ts +53 -0
- package/build/src/shared/transaction_guard.d.ts.map +1 -0
- package/build/src/shared/transaction_guard.js +74 -0
- package/build/src/shared/transaction_guard.js.map +1 -0
- package/build/src/sql_descendants.d.ts +55 -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 +915 -177
- package/build/src/testing/contract.js.map +1 -1
- package/build/src/testing/main.d.ts +10 -2
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +5 -1
- package/build/src/testing/main.js.map +1 -1
- package/build/src/testing/migration_contract.d.ts +293 -0
- package/build/src/testing/migration_contract.d.ts.map +1 -0
- package/build/src/testing/migration_contract.js +589 -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 +1238 -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/testing/scope_outbox_contract.d.ts +98 -0
- package/build/src/testing/scope_outbox_contract.d.ts.map +1 -0
- package/build/src/testing/scope_outbox_contract.js +677 -0
- package/build/src/testing/scope_outbox_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 +1537 -84
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +65 -0
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/authorization.stub +149 -5
- 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
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
11
|
+
import { AuthorizationConfigError, StaleSourceRefusedError } from '../src/errors.js';
|
|
12
|
+
/**
|
|
13
|
+
* Límites DECLARADOS de la identidad (cierre-2 de alpha.10 · C1 y C6):
|
|
14
|
+
* - la identidad es la REFERENCIA de la factory: dos factories DISTINTAS que
|
|
15
|
+
* construyen el mismo backend (`fga: () => new OpenFgaRelationsDriver(…mismo
|
|
16
|
+
* store…)`) NO se detectan — el alias pasa el guard. No se construye nada antes
|
|
17
|
+
* del guard, así que no hay backend que comparar; identidad declarada por el
|
|
18
|
+
* driver ⇒ roadmap 2.5. Registra un alias con la MISMA referencia
|
|
19
|
+
* (`const fga = () => …; drivers: { openfga: fga, alias: fga }`) o no lo registres.
|
|
20
|
+
* - `authorization` es estructural (`Pick<…>`): un objeto que FALSEA
|
|
21
|
+
* `relationsDriverKey`/`relationsDriverFactory` decide el activo y las factories.
|
|
22
|
+
* Pásale el manager del servicio, nunca uno construido o envuelto a mano.
|
|
23
|
+
*/
|
|
24
|
+
export async function runRelationsReconcile(authorization, options) {
|
|
25
|
+
const { reconcileRelations } = await import('../src/relations/reconcile.js');
|
|
26
|
+
const raw = options;
|
|
27
|
+
// Entradas que ya no gobiernan nada (L5: una opción inerte no cae en un
|
|
28
|
+
// default en silencio). `from`/`to` como instancias y `activeKey` eran
|
|
29
|
+
// justo las etiquetas desalineables del R4.
|
|
30
|
+
const retiredOptions = ['from', 'to', 'activeKey'].filter((name) => raw[name] !== undefined);
|
|
31
|
+
if (retiredOptions.length > 0) {
|
|
32
|
+
throw new AuthorizationConfigError(`runRelationsReconcile: ${retiredOptions.map((n) => `\`${n}\``).join(', ')} ya no se acepta(n) (cierre de 2.4.0-alpha.10): ` +
|
|
33
|
+
'el origen y el destino se resuelven por `fromKey`/`toKey` desde el config del manager, y el driver activo lo ' +
|
|
34
|
+
'dice el manager (`relations.default ?? default`). Pasar instancias o un activo propio permitía que la etiqueta ' +
|
|
35
|
+
'contradijera al driver y la dirección rancia pasara.');
|
|
36
|
+
}
|
|
37
|
+
if (typeof options.toKey !== 'string' || typeof options.fromKey !== 'string') {
|
|
38
|
+
throw new AuthorizationConfigError('runRelationsReconcile: hacen falta `toKey` y `fromKey` (las claves de relations.drivers del destino y del ORIGEN) ' +
|
|
39
|
+
'desde 2.4.0-alpha.10 — sin ellas no se puede negar la dirección rancia (re-escribir en el driver de relaciones ' +
|
|
40
|
+
'ACTIVO lo que el origen dejó de ver en el cutover).');
|
|
41
|
+
}
|
|
42
|
+
const { toKey, fromKey, allowStaleSource, ...rest } = options;
|
|
43
|
+
const toFactory = authorization.relationsDriverFactory(toKey);
|
|
44
|
+
const fromFactory = authorization.relationsDriverFactory(fromKey);
|
|
45
|
+
for (const [role, key, factory] of [['destino', toKey, toFactory], ['origen', fromKey, fromFactory]]) {
|
|
46
|
+
if (typeof factory !== 'function') {
|
|
47
|
+
throw new AuthorizationConfigError(`runRelationsReconcile: el ${role} '${key}' no es una clave de relations.drivers en el config del manager.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const active = authorization.relationsDriverKey();
|
|
51
|
+
/** Mismo driver ⇔ misma clave o misma factory resuelta (un alias es el mismo driver). */
|
|
52
|
+
const sameDriver = (a, b) => a === b || authorization.relationsDriverFactory(a) === authorization.relationsDriverFactory(b);
|
|
53
|
+
if (sameDriver(fromKey, toKey)) {
|
|
54
|
+
throw new AuthorizationConfigError(`runRelationsReconcile: el origen '${fromKey}' y el destino '${toKey}' son el MISMO driver ` +
|
|
55
|
+
`(${fromKey === toKey ? 'misma clave' : 'la misma factory bajo dos claves'}): no hay nada que migrar y un --prune ` +
|
|
56
|
+
'se compararía consigo mismo. Nombra el otro driver.');
|
|
57
|
+
}
|
|
58
|
+
const stale = sameDriver(toKey, active);
|
|
59
|
+
const allowStale = allowStaleSource === true;
|
|
60
|
+
if (stale && !allowStale && options.dryRun !== true) {
|
|
61
|
+
const alias = toKey === active ? '' : ` (la clave '${toKey}' resuelve la MISMA factory que el activo '${active}')`;
|
|
62
|
+
throw new StaleSourceRefusedError(`authz:relations:reconcile --to=${toKey} (origen '${fromKey}'): '${toKey}' es el driver de ` +
|
|
63
|
+
`relaciones ACTIVO (relations.default ?? default)${alias} y el origen no lo es. Esa dirección re-escribiría en el driver ` +
|
|
64
|
+
`activo tuplas que el origen ya no refleja: resucitaría las comparticiones retiradas desde el cutover (y con ` +
|
|
65
|
+
`--prune borraría las creadas después). No se ha construido, leído, congelado ni escrito nada. Tras el cutover el driver ` +
|
|
66
|
+
`activo ES la fuente de verdad de las relaciones: no hay nada que migrar hacia él; para volver atrás, migra en ` +
|
|
67
|
+
`la otra dirección (--to=${fromKey} --from=${toKey}) ANTES de cambiar relations.default/default. ` +
|
|
68
|
+
`Si de verdad el origen sigue siendo la fuente de verdad, la decisión humana es --allow-stale-source ` +
|
|
69
|
+
`(allowStaleSource: true).`);
|
|
70
|
+
}
|
|
71
|
+
const from = await fromFactory();
|
|
72
|
+
const to = await toFactory();
|
|
73
|
+
const pass = () => reconcileRelations({ ...rest, from, to });
|
|
74
|
+
const mark = (report) => {
|
|
75
|
+
if (stale)
|
|
76
|
+
report.staleSource = { to: toKey, from: fromKey, allowed: allowStale };
|
|
77
|
+
return report;
|
|
78
|
+
};
|
|
79
|
+
if (options.dryRun === true)
|
|
80
|
+
return mark(await pass());
|
|
81
|
+
const reason = `authz:relations:reconcile --to=${toKey}`;
|
|
82
|
+
return authorization.withFrozenWrites(reason, async (window) => {
|
|
83
|
+
const report = mark(await pass());
|
|
84
|
+
report.frozen = {
|
|
85
|
+
durable: true,
|
|
86
|
+
lapsed: await window.lapsed(),
|
|
87
|
+
leaseMs: window.leaseMs,
|
|
88
|
+
fence: window.fence,
|
|
89
|
+
...(window.untilMs === undefined ? {} : { untilMs: window.untilMs }),
|
|
90
|
+
...(window.window === undefined ? {} : { window: window.window }),
|
|
91
|
+
};
|
|
92
|
+
return report;
|
|
93
|
+
}, { kind: 'reconcile', operatorAsContext: true });
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Cómo se nombra la ventana en la que corrió la pasada (2.4.0-alpha.10 · 3):
|
|
97
|
+
* la del OPERADOR dice si caduca y cuándo — hasta alpha.9 una ventana abierta
|
|
98
|
+
* con `--lease-ms` salía como «ventana del operador» a secas, sin caducidad.
|
|
99
|
+
*/
|
|
100
|
+
export function frozenWindowLabel(frozen) {
|
|
101
|
+
// alpha.10 · S2: la ventana PROPIA se dice como tal — «no había ventana de
|
|
102
|
+
// operador viva» es justo lo que el operador de un cutover necesita leer.
|
|
103
|
+
if (frozen.window === 'own') {
|
|
104
|
+
return `, ventana PROPIA de la pasada (lease ${frozen.leaseMs} ms): NO había ventana de operador viva`;
|
|
105
|
+
}
|
|
106
|
+
if (frozen.window === 'enclosing')
|
|
107
|
+
return `, ventana que ya sostenía este proceso`;
|
|
108
|
+
if (frozen.untilMs !== undefined) {
|
|
109
|
+
return frozen.untilMs === null
|
|
110
|
+
? ', ventana del operador sin caducidad'
|
|
111
|
+
: `, ventana del operador que CADUCA el ${new Date(frozen.untilMs).toISOString()} (le quedaban ${frozen.leaseMs} ms)`;
|
|
112
|
+
}
|
|
113
|
+
return frozen.leaseMs === null ? ', ventana del operador' : `, lease ${frozen.leaseMs} ms`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Las líneas del reporte de `authz:relations:reconcile` y si la pasada es
|
|
117
|
+
* «limpia», en una función PURA (mismo patrón que `reconcileLines` de roles):
|
|
118
|
+
* es lo único del comando que DECIDE algo, y así tiene su caso sin montar un
|
|
119
|
+
* ace.
|
|
120
|
+
*
|
|
121
|
+
* Qué cuenta como deriva —y por tanto como exit ≠ 0—:
|
|
122
|
+
* - **`modelDrift`** (siempre): un tipo de relación presente en el ORIGEN que
|
|
123
|
+
* la config del DESTINO no declara. No cabe en su modelo fusionado, así que
|
|
124
|
+
* esas tuplas nunca resolverían allí — es la deriva del presupuesto de
|
|
125
|
+
* modelo compartido, no un detalle cosmético;
|
|
126
|
+
* - en `--dry-run`, **cualquier cosa que la pasada haría** (escribir, borrar o
|
|
127
|
+
* dejar `extra` sin `--prune`): es el verificador, y para eso está en CI.
|
|
128
|
+
*
|
|
129
|
+
* `extra` en una pasada que SÍ escribe (sin `--prune`) se AVISA —«repite con
|
|
130
|
+
* --prune»— pero no tumba: el operador eligió no podar. Es la misma cortesía
|
|
131
|
+
* que `authz:reconcile` de roles con la ventana del relay.
|
|
132
|
+
*/
|
|
133
|
+
export function relationsReconcileLines(report) {
|
|
134
|
+
const lines = [];
|
|
135
|
+
lines.push({
|
|
136
|
+
level: 'log',
|
|
137
|
+
message: `relaciones: escritas ${report.written}, actualizadas ${report.updated}, borradas ${report.deleted}, ` +
|
|
138
|
+
`iguales ${report.unchanged}, sobran ${report.extra}`,
|
|
139
|
+
});
|
|
140
|
+
// R-15: la caducada del origen es la pérdida DECLARADA de la migración (como
|
|
141
|
+
// `expired` en roles, 3b-8 · B2): se dice, no es deriva.
|
|
142
|
+
if (report.skipped.expired > 0) {
|
|
143
|
+
lines.push({
|
|
144
|
+
level: 'log',
|
|
145
|
+
message: `${report.skipped.expired} tupla(s) del origen estaban CADUCADAS al instante de la pasada y no se ` +
|
|
146
|
+
'migran (skipped.expired): no conceden nada y el destino no las necesita.',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// L-0: la tupla VIVA que el destino no declara (tipo o relación, F-05) no se
|
|
150
|
+
// escribió y se contó. No es una pérdida declarada como `expired` (que no
|
|
151
|
+
// concede nada): es una relación que no llegó ⇒ deriva, exit ≠ 0.
|
|
152
|
+
if (report.skipped.undeclared > 0) {
|
|
153
|
+
lines.push({
|
|
154
|
+
level: 'error',
|
|
155
|
+
message: `${report.skipped.undeclared} tupla(s) del origen NO se escribieron porque su tipo de objeto o su relación ` +
|
|
156
|
+
'no están declarados en defineRelationsConfig del DESTINO (skipped.undeclared, F-05 en el driver): ' +
|
|
157
|
+
'decláralos en el destino (y republica su modelo) y repite la pasada.',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
for (const type of report.modelDrift) {
|
|
161
|
+
lines.push({
|
|
162
|
+
level: 'error',
|
|
163
|
+
message: `El tipo de objeto '${type}' está en el ORIGEN pero NO en la config de relaciones del DESTINO: ` +
|
|
164
|
+
'no cabe en su modelo fusionado y esas tuplas no resolverían allí. Declara el tipo en ' +
|
|
165
|
+
'defineRelationsConfig del destino (y republica su modelo) antes de migrarlas.',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// R-17: el seguro de borrado masivo. En una pasada REAL sin --allow-mass-delete
|
|
169
|
+
// esto ya habría lanzado 500 antes de llegar aquí; en --dry-run NO lanza y lo
|
|
170
|
+
// marca, así que el verificador tiene que sacarlo como deriva (exit ≠ 0).
|
|
171
|
+
if (report.massDelete) {
|
|
172
|
+
lines.push({
|
|
173
|
+
level: 'error',
|
|
174
|
+
message: `--prune borraría ${report.extra || report.deleted} tupla(s) del destino y el ORIGEN no aportó NI UNA ` +
|
|
175
|
+
'utilizable: la firma de un --from equivocado o de un store vacío. En una pasada real es 500 ' +
|
|
176
|
+
'E_AUTHZ_MASS_RECONCILE_REFUSED; si de verdad quieres vaciar el destino, --allow-mass-delete.',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (report.extra > 0 && !report.dryRun) {
|
|
180
|
+
lines.push({
|
|
181
|
+
level: 'warning',
|
|
182
|
+
message: `${report.extra} tupla(s) del destino que el origen ya no respalda siguen ahí (no pasaste --prune): ` +
|
|
183
|
+
'repite con --prune si quieres borrarlas.',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// L-1 · J1: la garantía del freeze se DEMUESTRA (paridad con roles, 3b-7,
|
|
187
|
+
// juez C4). Un lease perdido a mitad = escrituras pudieron entrar durante la
|
|
188
|
+
// pasada y no aparecen en ningún contador ⇒ la pasada NO se certifica.
|
|
189
|
+
if (report.frozen) {
|
|
190
|
+
if (report.frozen.lapsed) {
|
|
191
|
+
lines.push({
|
|
192
|
+
level: 'error',
|
|
193
|
+
message: `El LEASE del freeze se perdió a MITAD de la pasada (fence ${report.frozen.fence}): otras escrituras de ` +
|
|
194
|
+
'relaciones o de roles pudieron entrar mientras se reconciliaba y no están en estos contadores. ' +
|
|
195
|
+
'La pasada NO se certifica: repite authz:relations:reconcile.',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
lines.push({
|
|
200
|
+
level: 'log',
|
|
201
|
+
message: `Escrituras congeladas durante la pasada (fence ${report.frozen.fence}` +
|
|
202
|
+
`${frozenWindowLabel(report.frozen)}): ` +
|
|
203
|
+
'la ventana aguantó entera.',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// alpha.10 · S1: la dirección rancia se dice siempre; en el verificador es deriva.
|
|
208
|
+
if (report.staleSource !== undefined) {
|
|
209
|
+
const { to, from, allowed } = report.staleSource;
|
|
210
|
+
lines.push({
|
|
211
|
+
level: 'warning',
|
|
212
|
+
message: `dirección RANCIA: '${to}' es el driver de relaciones ACTIVO y las tuplas salen de '${from}', que dejó de verlas ` +
|
|
213
|
+
'en el cutover — esta pasada resucita las comparticiones retiradas desde entonces (y con --prune borra las ' +
|
|
214
|
+
'nuevas). ' +
|
|
215
|
+
(allowed
|
|
216
|
+
? 'Levantada a sabiendas con --allow-stale-source.'
|
|
217
|
+
: 'Sin --allow-stale-source la pasada de verdad se niega (E_AUTHZ_STALE_SOURCE_REFUSED).'),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const cambios = report.written + report.updated + report.deleted + (report.dryRun ? report.extra : 0);
|
|
221
|
+
const clean = !(report.dryRun && report.staleSource !== undefined && !report.staleSource.allowed) &&
|
|
222
|
+
report.modelDrift.length === 0 &&
|
|
223
|
+
report.skipped.undeclared === 0 &&
|
|
224
|
+
!report.massDelete &&
|
|
225
|
+
!(report.frozen?.lapsed === true) &&
|
|
226
|
+
(!report.dryRun || cambios === 0);
|
|
227
|
+
return { lines, clean };
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* **`authz:relations:reconcile`** (Fase 4, lote 4-6) — migra las tuplas de
|
|
231
|
+
* RELACIÓN de un `RelationsDriver` a otro, idempotente y bidireccional, nunca
|
|
232
|
+
* silenciosa. Es el comando de plataforma que envuelve `reconcileRelations`
|
|
233
|
+
* (lote 4-5), análogo a `authz:reconcile` de roles pero para el puerto
|
|
234
|
+
* `RelationsDriver`.
|
|
235
|
+
*
|
|
236
|
+
* node ace authz:relations:reconcile --to=openfga --dry-run # el VERIFICADOR
|
|
237
|
+
* node ace authz:relations:reconcile --to=openfga # migra
|
|
238
|
+
* node ace authz:relations:reconcile --to=openfga --prune # y borra lo que el origen ya no respalda
|
|
239
|
+
*
|
|
240
|
+
* `--to`/`--from` nombran claves de `relations.drivers` en
|
|
241
|
+
* `config/authorization.ts` (NO las de `drivers` de roles): las relaciones
|
|
242
|
+
* tienen su propio puerto y sus propios drivers. La factory del driver
|
|
243
|
+
* `openfga` de relaciones entra por el subpath `/openfga` DENTRO de ella, así
|
|
244
|
+
* que este comando NUNCA importa el SDK (pureza).
|
|
245
|
+
*
|
|
246
|
+
* **Solo migra HECHOS de relación**: no hay árbol ni catálogo en `relations/`,
|
|
247
|
+
* así que —a diferencia de `authz:reconcile` de roles— no hay fases de marcador
|
|
248
|
+
* de raíz, proyección ni árbol. La migración es **POR partición**
|
|
249
|
+
* (mono-tenant: `APP_SCOPE`, el default); para migrar otra partición se pasan
|
|
250
|
+
* `--partition-type`/`--partition-uuid`, y migrar TODAS es iterar por el
|
|
251
|
+
* consumidor (una migración de tenant es un caso natural).
|
|
252
|
+
*
|
|
253
|
+
* **`--dry-run` es el verificador y es read-only por contrato** (como el de
|
|
254
|
+
* roles, S18): mismo recorrido, cero escrituras, los mismos números; NO hay
|
|
255
|
+
* `--fix`. Además vigila la **deriva del modelo fusionado** (`modelDrift`): un
|
|
256
|
+
* tipo del origen que el destino no declara sale como error.
|
|
257
|
+
*
|
|
258
|
+
* Es una operación de PLATAFORMA: no lleva actor, no se expone por HTTP.
|
|
259
|
+
*/
|
|
260
|
+
export default class AuthzRelationsReconcile extends BaseCommand {
|
|
261
|
+
static commandName = 'authz:relations:reconcile';
|
|
262
|
+
static description = 'Rebuild (or verify, with --dry-run) the relation tuples of one RelationsDriver from another';
|
|
263
|
+
static options = {
|
|
264
|
+
startApp: true,
|
|
265
|
+
};
|
|
266
|
+
async run() {
|
|
267
|
+
if (!this.to) {
|
|
268
|
+
this.logger.error("Falta --to: 'node ace authz:relations:reconcile --to=openfga'. Es la clave del driver DESTINO en " +
|
|
269
|
+
'`relations.drivers` de config/authorization.ts.');
|
|
270
|
+
this.exitCode = 1;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// Cierre del auditor de alpha.10 · R4: las claves, las factories y el driver
|
|
274
|
+
// ACTIVO salen del MISMO sitio — el manager de servicio (construido desde
|
|
275
|
+
// config/authorization.ts) —, que es también quien toma la ventana. Hasta el
|
|
276
|
+
// cierre el comando resolvía los drivers de `this.app.config` y le pasaba a
|
|
277
|
+
// `runRelationsReconcile` las instancias y, aparte, las etiquetas.
|
|
278
|
+
const { default: authorization } = await import('../services/main.js');
|
|
279
|
+
const keys = authorization.relationsDriverKeys();
|
|
280
|
+
if (keys.length === 0) {
|
|
281
|
+
this.logger.error('No hay `relations.drivers` en config/authorization.ts: declara los drivers de relaciones (por clave) ' +
|
|
282
|
+
'para poder migrar sus tuplas.');
|
|
283
|
+
this.exitCode = 1;
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// El ORIGEN: `--from` si se pasa; si no, el ÚNICO que no es el destino.
|
|
287
|
+
let fromKey = this.from;
|
|
288
|
+
if (!fromKey) {
|
|
289
|
+
const candidates = keys.filter((k) => k !== this.to);
|
|
290
|
+
if (candidates.length !== 1) {
|
|
291
|
+
this.logger.error(`No se puede elegir el ORIGEN solo: hay ${candidates.length} candidatos (${candidates.join(', ') || 'ninguno'}). ` +
|
|
292
|
+
'Nómbralo con --from=<clave>: de dónde salen las tuplas decide lo que queda escrito.');
|
|
293
|
+
this.exitCode = 1;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
fromKey = candidates[0];
|
|
297
|
+
}
|
|
298
|
+
if (authorization.relationsDriverFactory(this.to) === undefined) {
|
|
299
|
+
this.logger.error(`El destino '${this.to}' no es una clave de relations.drivers (${keys.join(', ')}).`);
|
|
300
|
+
this.exitCode = 1;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (authorization.relationsDriverFactory(fromKey) === undefined) {
|
|
304
|
+
this.logger.error(`El origen '${fromKey}' no es una clave de relations.drivers (${keys.join(', ')}).`);
|
|
305
|
+
this.exitCode = 1;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// La partición (mono-tenant por defecto). El reconcile es POR partición.
|
|
309
|
+
// Las DOS banderas van juntas o no va ninguna (cierre de alpha.9, 🟡 B del
|
|
310
|
+
// auditor): media pareja reconciliaba `app` en silencio, escribía CERO y
|
|
311
|
+
// salía `[success]` — la misma forma del 🔴 «migra 0 y reporta éxito» que
|
|
312
|
+
// este lote cierra, sobreviviendo a una errata del runbook de cutover.
|
|
313
|
+
if (this.partitionUuid && !this.partitionType) {
|
|
314
|
+
this.logger.error('--partition-uuid sin --partition-type: las dos banderas van juntas. ' +
|
|
315
|
+
'Sin --partition-type se reconciliaría la partición RAÍZ (app) y tus tuplas por tenant no se migrarían.');
|
|
316
|
+
this.exitCode = 1;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
// alpha.10 · 5 (sexto informe de COGNITIV): la variante que el cierre de
|
|
320
|
+
// alpha.9 no cazó — pareja COMPLETA con `app` también reconciliaba la raíz
|
|
321
|
+
// en silencio, tirando el uuid (una errata del runbook: `app` por el tipo).
|
|
322
|
+
if (this.partitionType === 'app' && this.partitionUuid) {
|
|
323
|
+
this.logger.error(`--partition-type=app con --partition-uuid=${this.partitionUuid}: la partición raíz no lleva uuid. ` +
|
|
324
|
+
'O quitas --partition-uuid (reconciliar la raíz) o pones el tipo del tenant en --partition-type.');
|
|
325
|
+
this.exitCode = 1;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (this.partitionType && this.partitionType !== 'app' && !this.partitionUuid) {
|
|
329
|
+
this.logger.error(`--partition-type=${this.partitionType} exige --partition-uuid (el tenant concreto a reconciliar).`);
|
|
330
|
+
this.exitCode = 1;
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const partition = this.partitionType && this.partitionType !== 'app'
|
|
334
|
+
? { type: this.partitionType, uuid: this.partitionUuid }
|
|
335
|
+
: { type: 'app', uuid: null };
|
|
336
|
+
// La config del DESTINO, para vigilar la deriva del modelo fusionado en
|
|
337
|
+
// --dry-run: es la persistida (una sola en el store compartido).
|
|
338
|
+
const { readRelationsConfig } = await import('../src/relations_config_store.js');
|
|
339
|
+
const toConfig = await readRelationsConfig().catch(() => undefined);
|
|
340
|
+
// L-1 · J1: la pasada que ESCRIBE corre bajo la ventana durable del
|
|
341
|
+
// manager de roles (la misma fila `id = 2`, toda la flota); `--dry-run` no.
|
|
342
|
+
// Los drivers los construye `runRelationsReconcile`, DESPUÉS del guard.
|
|
343
|
+
let report;
|
|
344
|
+
try {
|
|
345
|
+
report = await runRelationsReconcile(authorization, {
|
|
346
|
+
partition,
|
|
347
|
+
dryRun: this.dryRun === true,
|
|
348
|
+
prune: this.prune === true,
|
|
349
|
+
allowMassDelete: this.allowMassDelete === true,
|
|
350
|
+
allowStaleSource: this.allowStaleSource === true,
|
|
351
|
+
toKey: this.to,
|
|
352
|
+
fromKey,
|
|
353
|
+
...(toConfig ? { toConfig } : {}),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
// R-17: el seguro de borrado masivo (500) sale como error de comando, no
|
|
358
|
+
// como stack crudo. `--allow-mass-delete` es la salida humana.
|
|
359
|
+
if (error?.code === 'E_AUTHZ_MASS_RECONCILE_REFUSED' || error?.code === 'E_AUTHZ_STALE_SOURCE_REFUSED') {
|
|
360
|
+
this.logger.error(String(error.message ?? error));
|
|
361
|
+
this.exitCode = 1;
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
// L-1 · J1: un freeze vivo de OTRA pasada (423): dos migraciones no se
|
|
365
|
+
// pisan; el mensaje nombra al dueño y cómo se levanta.
|
|
366
|
+
if (error?.code === 'E_AUTHZ_FREEZE_HELD' || error?.code === 'E_AUTHZ_OPERATOR_WINDOW_EXPIRED') {
|
|
367
|
+
this.logger.error(String(error.message ?? error));
|
|
368
|
+
this.exitCode = 1;
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
// Cierre R4: origen y destino que resuelven el MISMO driver (misma clave o
|
|
372
|
+
// alias de la misma factory) es un error de configuración de la pasada.
|
|
373
|
+
if (error?.code === 'E_AUTHZ_CONFIG') {
|
|
374
|
+
this.logger.error(String(error.message ?? error));
|
|
375
|
+
this.exitCode = 1;
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
const { lines, clean } = relationsReconcileLines(report);
|
|
381
|
+
for (const { level, message } of lines)
|
|
382
|
+
this.logger[level](message);
|
|
383
|
+
const resumen = `escritas ${report.written}, actualizadas ${report.updated}, borradas ${report.deleted}, ` +
|
|
384
|
+
`iguales ${report.unchanged}, sobran ${report.extra}, caducadas omitidas ${report.skipped.expired}, ` +
|
|
385
|
+
`no declaradas en el destino ${report.skipped.undeclared}`;
|
|
386
|
+
if (report.dryRun) {
|
|
387
|
+
if (clean) {
|
|
388
|
+
this.logger.success(`Sin deriva: '${this.to}' coincide con '${fromKey}' (${resumen}).`);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
this.logger.error(`DERIVA con '${this.to}': ${resumen}. No se ha escrito nada (--dry-run).`);
|
|
392
|
+
this.exitCode = 1;
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
this.logger.success(`Relaciones de '${this.to}' reconciliadas desde '${fromKey}': ${resumen}.`);
|
|
396
|
+
if (!clean) {
|
|
397
|
+
this.logger.warning('Queda algo que esta pasada no arregla sola (mira las líneas de arriba).');
|
|
398
|
+
this.exitCode = 1;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
__decorate([
|
|
403
|
+
flags.string({ description: 'Destination driver: a key of `relations.drivers` in config/authorization.ts' }),
|
|
404
|
+
__metadata("design:type", Object)
|
|
405
|
+
], AuthzRelationsReconcile.prototype, "to", void 0);
|
|
406
|
+
__decorate([
|
|
407
|
+
flags.string({ description: 'Source driver (a key of `relations.drivers`); required when more than one is registered' }),
|
|
408
|
+
__metadata("design:type", Object)
|
|
409
|
+
], AuthzRelationsReconcile.prototype, "from", void 0);
|
|
410
|
+
__decorate([
|
|
411
|
+
flags.boolean({ description: 'Report what it would do and write nothing (this is the verifier)' }),
|
|
412
|
+
__metadata("design:type", Object)
|
|
413
|
+
], AuthzRelationsReconcile.prototype, "dryRun", void 0);
|
|
414
|
+
__decorate([
|
|
415
|
+
flags.boolean({ description: 'Also delete the tuples the source no longer backs (leftovers)' }),
|
|
416
|
+
__metadata("design:type", Object)
|
|
417
|
+
], AuthzRelationsReconcile.prototype, "prune", void 0);
|
|
418
|
+
__decorate([
|
|
419
|
+
flags.boolean({
|
|
420
|
+
description: 'Allow --prune to empty the destination when the source has no usable tuple (mass-delete safeguard)',
|
|
421
|
+
}),
|
|
422
|
+
__metadata("design:type", Object)
|
|
423
|
+
], AuthzRelationsReconcile.prototype, "allowMassDelete", void 0);
|
|
424
|
+
__decorate([
|
|
425
|
+
flags.boolean({
|
|
426
|
+
description: 'Allow --to=<ACTIVE relations driver> from another source: rewrites into the serving driver tuples the source no longer reflects',
|
|
427
|
+
}),
|
|
428
|
+
__metadata("design:type", Object)
|
|
429
|
+
], AuthzRelationsReconcile.prototype, "allowStaleSource", void 0);
|
|
430
|
+
__decorate([
|
|
431
|
+
flags.string({ description: 'Partition (tenant) scope type to migrate; default: app (mono-tenant)' }),
|
|
432
|
+
__metadata("design:type", Object)
|
|
433
|
+
], AuthzRelationsReconcile.prototype, "partitionType", void 0);
|
|
434
|
+
__decorate([
|
|
435
|
+
flags.string({ description: 'Partition (tenant) scope uuid; required with --partition-type' }),
|
|
436
|
+
__metadata("design:type", Object)
|
|
437
|
+
], AuthzRelationsReconcile.prototype, "partitionUuid", void 0);
|
|
438
|
+
//# sourceMappingURL=authz_relations_reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_relations_reconcile.js","sourceRoot":"","sources":["../../commands/authz_relations_reconcile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
import type { ScopeRelayQueue } from '../src/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* **La edad de la cola en el comando** (2.4.0-alpha.8; panel relay 2,
|
|
6
|
+
* cuestión 5), en una función PURA (patrón `requeueLines`): qué se imprime y
|
|
7
|
+
* si `--max-age` la convierte en salida ≠ 0.
|
|
8
|
+
*
|
|
9
|
+
* - Sin cabeza con fecha (`pendingAgeMs === null`: cola vacía, solo
|
|
10
|
+
* aparcadas, o una outbox de terceros sin `createdAt`) no se dice nada y
|
|
11
|
+
* nunca se alarma.
|
|
12
|
+
* - Con edad, se INFORMA siempre (es lo que faltaba en el B2 de COGNITIV:
|
|
13
|
+
* diez pasadas con el store caído y nadie sabía desde cuándo).
|
|
14
|
+
* - **Solo con umbral** (`--max-age=<ms>`) y edad POR ENCIMA es `exceeded`
|
|
15
|
+
* (exit ≠ 0): sin umbral un cron no puede quedarse en rojo para siempre
|
|
16
|
+
* por una cola que simplemente tiene backlog. Un umbral inválido lanza.
|
|
17
|
+
*/
|
|
18
|
+
export declare function queueAgeLines(queue: ScopeRelayQueue, maxAgeMs: number | undefined, remaining?: boolean): {
|
|
19
|
+
lines: Array<{
|
|
20
|
+
level: 'info' | 'warning' | 'error';
|
|
21
|
+
message: string;
|
|
22
|
+
}>;
|
|
23
|
+
exceeded: boolean;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Drena la OUTBOX del árbol de scopes y aplica los cambios al driver
|
|
27
|
+
* (3b-2d; panel 2, cruce 4 · S5).
|
|
28
|
+
*
|
|
29
|
+
* Con `scopes.outbox` declarada, `authorization.scopes.attached/moved/
|
|
30
|
+
* detached` no escriben en el backend: encolan el cambio DENTRO de la
|
|
31
|
+
* transacción del consumidor. Este comando es quien lo propaga.
|
|
32
|
+
*
|
|
33
|
+
* node ace authz:scopes:relay # drena
|
|
34
|
+
* node ace authz:scopes:relay --dry-run # solo dice qué haría
|
|
35
|
+
* node ace authz:scopes:relay --limit 500 # una pasada acotada
|
|
36
|
+
* node ace authz:scopes:relay --only=12,13 # SOLO esas entradas (2.4.0-alpha.5)
|
|
37
|
+
* node ace authz:scopes:relay --wait-lease-ms=5000 # espera el lease hasta 5 s en vez de salir `busy` (2.4.0-alpha.8)
|
|
38
|
+
* node ace authz:scopes:relay --max-age=300000 # exit ≠ 0 si la pendiente más antigua lleva > 5 min (2.4.0-alpha.8)
|
|
39
|
+
*
|
|
40
|
+
* **`--max-age`** (alpha.8, cuestión 5 del panel relay 2): el reporte lleva
|
|
41
|
+
* SIEMPRE la edad de la cabeza global de la cola (`queue.pendingAgeMs`, con
|
|
42
|
+
* el reloj de quien encoló) y se imprime; con umbral, superarlo es salida ≠ 0
|
|
43
|
+
* — la alarma que faltaba cuando el store está caído (un 503 no consume
|
|
44
|
+
* intento: `attempts` y `dead` se quedan a cero). Sin umbral solo informa.
|
|
45
|
+
*
|
|
46
|
+
* **`--only`** (alpha.5 · R1): releva solo los ids del recibo que devolvió
|
|
47
|
+
* `scopes.attached/moved/detached` (`{ queued }`). El orden por clave no se
|
|
48
|
+
* relaja: una entrada pedida que dependa de una anterior sin aplicar y no
|
|
49
|
+
* pedida sale «aplazada» (`depends-on:<id>`), nunca fuera de orden; `--only`
|
|
50
|
+
* vacío es un error, no «todo». La salida es POR ENTRADA (`entries`), y los
|
|
51
|
+
* ids que no estaban ni pendientes ni aparcados se dicen (`missing`).
|
|
52
|
+
*
|
|
53
|
+
* **Lo aparcado tapona por clave** (alpha.5 · R2a): un `attached`/`moved`
|
|
54
|
+
* que nombre un scope de una entrada APARCADA sale «aplazado» con
|
|
55
|
+
* `parked:<id>` en todas las pasadas hasta que alguien mire la aparcada; un
|
|
56
|
+
* `detached` de esa clave se aplica igual (una purga nunca queda bloqueada).
|
|
57
|
+
* Y un 503 del backend NO consume intento: la caída del store es común a
|
|
58
|
+
* todas las entradas y no dice nada de ninguna.
|
|
59
|
+
*
|
|
60
|
+
* Pensado para un supervisor (un bucle, un cron corto, un worker): la pasada
|
|
61
|
+
* es REANUDABLE —lo que no entra sigue pendiente—. Un cambio que falla
|
|
62
|
+
* **aplaza lo que depende de él** (todo cambio posterior que nombre alguno de
|
|
63
|
+
* sus scopes) y deja pasar el resto: hasta 3b-2h paraba la pasada entera, y
|
|
64
|
+
* eso convertía una entrada irreparable en un tapón permanente para todos los
|
|
65
|
+
* tenants (3b-2h · 🔴 2). Sale con código ≠ 0 si algo falló o hay entradas
|
|
66
|
+
* APARCADAS, para que el supervisor se entere.
|
|
67
|
+
*
|
|
68
|
+
* **Escritor ÚNICO**: si la outbox sabe dar lease (`sqlScopeOutbox` lo hace),
|
|
69
|
+
* una segunda pasada simultánea no hace nada y lo dice. Sin lease, no lances
|
|
70
|
+
* dos a la vez: la rezagada re-aplica cambios viejos sobre el árbol nuevo.
|
|
71
|
+
*
|
|
72
|
+
* **Lo que este comando no puede arreglar**: entre el commit del consumidor
|
|
73
|
+
* y la pasada hay un lag (segundos) en el que el backend decide con el árbol
|
|
74
|
+
* VIEJO. Es un fail-open temporal —el tenant antiguo conserva acceso tras un
|
|
75
|
+
* `moved`, los denies heredados no aplican tras un `attached`—. No hay 2PC.
|
|
76
|
+
* Un ciclo más corto acorta esa ventana **mientras la cola avance**: lo que
|
|
77
|
+
* falla o se aparca no está acotado por el ciclo y hay que mirarlo.
|
|
78
|
+
*
|
|
79
|
+
* Es una operación de PLATAFORMA (como `authz:catalog:prune-orphans`): se
|
|
80
|
+
* salta `requireActor`/`requireWithin` porque la policy ya se juzgó al
|
|
81
|
+
* ENCOLAR, con el árbol y la sesión de aquel momento. No se expone por HTTP.
|
|
82
|
+
*/
|
|
83
|
+
export default class AuthzScopesRelay extends BaseCommand {
|
|
84
|
+
static commandName: string;
|
|
85
|
+
static description: string;
|
|
86
|
+
static options: CommandOptions;
|
|
87
|
+
dryRun: boolean | undefined;
|
|
88
|
+
limit: number | undefined;
|
|
89
|
+
batchSize: number | undefined;
|
|
90
|
+
only: string[] | undefined;
|
|
91
|
+
waitLeaseMs: number | undefined;
|
|
92
|
+
maxAge: number | undefined;
|
|
93
|
+
/** `--only=12,13` o `--only=12 --only=13`: ids numéricos como número, el resto como cadena. */
|
|
94
|
+
static parseOnly(values: string[] | undefined): Array<string | number> | undefined;
|
|
95
|
+
run(): Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=authz_scopes_relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_scopes_relay.d.ts","sourceRoot":"","sources":["../../commands/authz_scopes_relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,GAAE,OAAe,GACzB;IAAE,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAoC/F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,SAAuB;IACzC,MAAM,CAAC,WAAW,SAAiF;IAEnG,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAKO,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAG3B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAGzB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAK7B,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAK1B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAK/B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAElC,+FAA+F;IAC/F,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS;IAS5E,GAAG;CAgMV"}
|