@jantstack/adonis-authz 1.1.0 → 2.4.0-alpha.1
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/LICENSE +1 -1
- package/README.md +837 -51
- package/build/commands/authz_catalog_diff.d.ts +28 -0
- package/build/commands/authz_catalog_diff.d.ts.map +1 -0
- package/build/commands/authz_catalog_diff.js +67 -0
- package/build/commands/authz_catalog_diff.js.map +1 -0
- 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 +37 -0
- package/build/commands/authz_catalog_sync.d.ts.map +1 -0
- package/build/commands/authz_catalog_sync.js +81 -0
- package/build/commands/authz_catalog_sync.js.map +1 -0
- 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 +73 -0
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
- package/build/commands/authz_relations_reconcile.js +225 -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 +8 -1
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +8 -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 +91 -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 +75 -7
- package/build/index.d.ts.map +1 -1
- package/build/index.js +67 -5
- 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 +48 -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/catalog.d.ts +289 -0
- package/build/src/catalog/catalog.d.ts.map +1 -0
- package/build/src/catalog/catalog.js +859 -0
- package/build/src/catalog/catalog.js.map +1 -0
- package/build/src/catalog/catalog_cache.d.ts +324 -0
- package/build/src/catalog/catalog_cache.d.ts.map +1 -0
- package/build/src/catalog/catalog_cache.js +666 -0
- package/build/src/catalog/catalog_cache.js.map +1 -0
- package/build/src/clock.d.ts +24 -0
- package/build/src/clock.d.ts.map +1 -0
- package/build/src/clock.js +7 -0
- package/build/src/clock.js.map +1 -0
- package/build/src/define_config.d.ts +201 -7
- 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 +324 -15
- package/build/src/drivers/database_driver.d.ts.map +1 -1
- package/build/src/drivers/database_driver.js +1107 -128
- package/build/src/drivers/database_driver.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +75 -0
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/database_relations_driver.js +450 -0
- package/build/src/drivers/database_relations_driver.js.map +1 -0
- package/build/src/drivers/openfga_driver.d.ts +963 -55
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +2693 -372
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_facts.d.ts +369 -0
- package/build/src/drivers/openfga_facts.d.ts.map +1 -0
- package/build/src/drivers/openfga_facts.js +813 -0
- package/build/src/drivers/openfga_facts.js.map +1 -0
- package/build/src/drivers/openfga_relations_driver.d.ts +120 -0
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/openfga_relations_driver.js +466 -0
- package/build/src/drivers/openfga_relations_driver.js.map +1 -0
- package/build/src/errors.d.ts +586 -0
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +583 -0
- package/build/src/errors.js.map +1 -1
- package/build/src/expiry.d.ts +27 -0
- package/build/src/expiry.d.ts.map +1 -0
- package/build/src/expiry.js +50 -0
- package/build/src/expiry.js.map +1 -0
- package/build/src/freeze.d.ts +120 -0
- package/build/src/freeze.d.ts.map +1 -0
- package/build/src/freeze.js +172 -0
- package/build/src/freeze.js.map +1 -0
- package/build/src/hierarchical_resolver.d.ts +56 -0
- package/build/src/hierarchical_resolver.d.ts.map +1 -0
- package/build/src/hierarchical_resolver.js +87 -0
- package/build/src/hierarchical_resolver.js.map +1 -0
- package/build/src/{middleware → http}/app_access_middleware.d.ts +8 -6
- package/build/src/http/app_access_middleware.d.ts.map +1 -0
- package/build/src/{middleware → http}/app_access_middleware.js +10 -26
- 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 +228 -0
- package/build/src/identity.d.ts.map +1 -0
- package/build/src/identity.js +457 -0
- package/build/src/identity.js.map +1 -0
- package/build/src/manager.d.ts +536 -7
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +2676 -23
- package/build/src/manager.js.map +1 -1
- package/build/src/memoize_ancestors.d.ts +23 -0
- package/build/src/memoize_ancestors.d.ts.map +1 -0
- package/build/src/memoize_ancestors.js +42 -0
- package/build/src/memoize_ancestors.js.map +1 -0
- package/build/src/models/authz_assignment.d.ts +11 -11
- package/build/src/models/authz_assignment.d.ts.map +1 -1
- package/build/src/models/authz_deny.d.ts +11 -11
- package/build/src/models/authz_deny.d.ts.map +1 -1
- package/build/src/models/authz_permission.d.ts +17 -11
- package/build/src/models/authz_permission.d.ts.map +1 -1
- package/build/src/models/authz_permission.js +4 -0
- package/build/src/models/authz_permission.js.map +1 -1
- package/build/src/models/authz_role.d.ts +19 -12
- package/build/src/models/authz_role.d.ts.map +1 -1
- package/build/src/models/authz_role.js +6 -1
- package/build/src/models/authz_role.js.map +1 -1
- package/build/src/models/authz_role_permission.d.ts +11 -11
- package/build/src/models/authz_role_permission.d.ts.map +1 -1
- package/build/src/openfga.d.ts +29 -0
- package/build/src/openfga.d.ts.map +1 -0
- package/build/src/openfga.js +26 -0
- package/build/src/openfga.js.map +1 -0
- 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 +85 -0
- package/build/src/relation_partition_trigger.js.map +1 -0
- package/build/src/relations/define_relations_config.d.ts +58 -0
- package/build/src/relations/define_relations_config.d.ts.map +1 -0
- package/build/src/relations/define_relations_config.js +144 -0
- package/build/src/relations/define_relations_config.js.map +1 -0
- package/build/src/relations/manager.d.ts +38 -0
- package/build/src/relations/manager.d.ts.map +1 -0
- package/build/src/relations/manager.js +156 -0
- package/build/src/relations/manager.js.map +1 -0
- package/build/src/relations/reconcile.d.ts +62 -0
- package/build/src/relations/reconcile.d.ts.map +1 -0
- package/build/src/relations/reconcile.js +138 -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 +291 -0
- package/build/src/scope_outbox.js.map +1 -0
- package/build/src/shared/backend_guard.d.ts +106 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -0
- package/build/src/shared/backend_guard.js +246 -0
- package/build/src/shared/backend_guard.js.map +1 -0
- package/build/src/shared/sql_expiry.d.ts +53 -0
- package/build/src/shared/sql_expiry.d.ts.map +1 -0
- package/build/src/shared/sql_expiry.js +66 -0
- package/build/src/shared/sql_expiry.js.map +1 -0
- package/build/src/sql_descendants.d.ts +97 -0
- package/build/src/sql_descendants.d.ts.map +1 -0
- package/build/src/sql_descendants.js +203 -0
- package/build/src/sql_descendants.js.map +1 -0
- package/build/src/testing/contract.d.ts +212 -7
- package/build/src/testing/contract.d.ts.map +1 -1
- package/build/src/testing/contract.js +3449 -24
- 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 +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 +51 -0
- package/build/src/testing/relations_contract.d.ts.map +1 -0
- package/build/src/testing/relations_contract.js +654 -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 +172 -0
- package/build/src/testing/relations_reconcile_contract.js.map +1 -0
- package/build/src/testing/scope_tree.d.ts +65 -0
- package/build/src/testing/scope_tree.d.ts.map +1 -0
- package/build/src/testing/scope_tree.js +145 -0
- package/build/src/testing/scope_tree.js.map +1 -0
- package/build/src/traits/authz_scopes.d.ts +30 -6
- package/build/src/traits/authz_scopes.d.ts.map +1 -1
- package/build/src/traits/authz_scopes.js +30 -18
- package/build/src/traits/authz_scopes.js.map +1 -1
- package/build/src/traits/has_uuid.d.ts +12 -12
- package/build/src/traits/has_uuid.d.ts.map +1 -1
- package/build/src/types.d.ts +1313 -28
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +20 -4
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/app_acl.stub +4 -2
- package/build/stubs/config/authorization.stub +168 -14
- package/build/stubs/migration.stub +183 -13
- package/build/stubs/scopes_outbox_migration.stub +57 -0
- package/package.json +14 -7
- package/build/commands/openfga_import.d.ts +0 -28
- package/build/commands/openfga_import.d.ts.map +0 -1
- package/build/commands/openfga_import.js +0 -74
- package/build/commands/openfga_import.js.map +0 -1
- package/build/src/catalog.d.ts +0 -3
- package/build/src/catalog.d.ts.map +0 -1
- package/build/src/catalog.js +0 -103
- package/build/src/catalog.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
|
@@ -0,0 +1,225 @@
|
|
|
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
|
+
/**
|
|
12
|
+
* Las líneas del reporte de `authz:relations:reconcile` y si la pasada es
|
|
13
|
+
* «limpia», en una función PURA (mismo patrón que `reconcileLines` de roles):
|
|
14
|
+
* es lo único del comando que DECIDE algo, y así tiene su caso sin montar un
|
|
15
|
+
* ace.
|
|
16
|
+
*
|
|
17
|
+
* Qué cuenta como deriva —y por tanto como exit ≠ 0—:
|
|
18
|
+
* - **`modelDrift`** (siempre): un tipo de relación presente en el ORIGEN que
|
|
19
|
+
* la config del DESTINO no declara. No cabe en su modelo fusionado, así que
|
|
20
|
+
* esas tuplas nunca resolverían allí — es la deriva del presupuesto de
|
|
21
|
+
* modelo compartido, no un detalle cosmético;
|
|
22
|
+
* - en `--dry-run`, **cualquier cosa que la pasada haría** (escribir, borrar o
|
|
23
|
+
* dejar `extra` sin `--prune`): es el verificador, y para eso está en CI.
|
|
24
|
+
*
|
|
25
|
+
* `extra` en una pasada que SÍ escribe (sin `--prune`) se AVISA —«repite con
|
|
26
|
+
* --prune»— pero no tumba: el operador eligió no podar. Es la misma cortesía
|
|
27
|
+
* que `authz:reconcile` de roles con la ventana del relay.
|
|
28
|
+
*/
|
|
29
|
+
export function relationsReconcileLines(report) {
|
|
30
|
+
const lines = [];
|
|
31
|
+
lines.push({
|
|
32
|
+
level: 'log',
|
|
33
|
+
message: `relaciones: escritas ${report.written}, borradas ${report.deleted}, ` +
|
|
34
|
+
`iguales ${report.unchanged}, sobran ${report.extra}`,
|
|
35
|
+
});
|
|
36
|
+
for (const type of report.modelDrift) {
|
|
37
|
+
lines.push({
|
|
38
|
+
level: 'error',
|
|
39
|
+
message: `El tipo de objeto '${type}' está en el ORIGEN pero NO en la config de relaciones del DESTINO: ` +
|
|
40
|
+
'no cabe en su modelo fusionado y esas tuplas no resolverían allí. Declara el tipo en ' +
|
|
41
|
+
'defineRelationsConfig del destino (y republica su modelo) antes de migrarlas.',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// R-17: el seguro de borrado masivo. En una pasada REAL sin --allow-mass-delete
|
|
45
|
+
// esto ya habría lanzado 500 antes de llegar aquí; en --dry-run NO lanza y lo
|
|
46
|
+
// marca, así que el verificador tiene que sacarlo como deriva (exit ≠ 0).
|
|
47
|
+
if (report.massDelete) {
|
|
48
|
+
lines.push({
|
|
49
|
+
level: 'error',
|
|
50
|
+
message: `--prune borraría ${report.extra || report.deleted} tupla(s) del destino y el ORIGEN no aportó NI UNA ` +
|
|
51
|
+
'utilizable: la firma de un --from equivocado o de un store vacío. En una pasada real es 500 ' +
|
|
52
|
+
'E_AUTHZ_MASS_RECONCILE_REFUSED; si de verdad quieres vaciar el destino, --allow-mass-delete.',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (report.extra > 0 && !report.dryRun) {
|
|
56
|
+
lines.push({
|
|
57
|
+
level: 'warning',
|
|
58
|
+
message: `${report.extra} tupla(s) del destino que el origen ya no respalda siguen ahí (no pasaste --prune): ` +
|
|
59
|
+
'repite con --prune si quieres borrarlas.',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const cambios = report.written + report.deleted + (report.dryRun ? report.extra : 0);
|
|
63
|
+
const clean = report.modelDrift.length === 0 && !report.massDelete && (!report.dryRun || cambios === 0);
|
|
64
|
+
return { lines, clean };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* **`authz:relations:reconcile`** (Fase 4, lote 4-6) — migra las tuplas de
|
|
68
|
+
* RELACIÓN de un `RelationsDriver` a otro, idempotente y bidireccional, nunca
|
|
69
|
+
* silenciosa. Es el comando de plataforma que envuelve `reconcileRelations`
|
|
70
|
+
* (lote 4-5), análogo a `authz:reconcile` de roles pero para el puerto
|
|
71
|
+
* `RelationsDriver`.
|
|
72
|
+
*
|
|
73
|
+
* node ace authz:relations:reconcile --to=openfga --dry-run # el VERIFICADOR
|
|
74
|
+
* node ace authz:relations:reconcile --to=openfga # migra
|
|
75
|
+
* node ace authz:relations:reconcile --to=openfga --prune # y borra lo que el origen ya no respalda
|
|
76
|
+
*
|
|
77
|
+
* `--to`/`--from` nombran claves de `relations.drivers` en
|
|
78
|
+
* `config/authorization.ts` (NO las de `drivers` de roles): las relaciones
|
|
79
|
+
* tienen su propio puerto y sus propios drivers. La factory del driver
|
|
80
|
+
* `openfga` de relaciones entra por el subpath `/openfga` DENTRO de ella, así
|
|
81
|
+
* que este comando NUNCA importa el SDK (pureza).
|
|
82
|
+
*
|
|
83
|
+
* **Solo migra HECHOS de relación**: no hay árbol ni catálogo en `relations/`,
|
|
84
|
+
* así que —a diferencia de `authz:reconcile` de roles— no hay fases de marcador
|
|
85
|
+
* de raíz, proyección ni árbol. La migración es **POR partición**
|
|
86
|
+
* (mono-tenant: `APP_SCOPE`, el default); para migrar otra partición se pasan
|
|
87
|
+
* `--partition-type`/`--partition-uuid`, y migrar TODAS es iterar por el
|
|
88
|
+
* consumidor (una migración de tenant es un caso natural).
|
|
89
|
+
*
|
|
90
|
+
* **`--dry-run` es el verificador y es read-only por contrato** (como el de
|
|
91
|
+
* roles, S18): mismo recorrido, cero escrituras, los mismos números; NO hay
|
|
92
|
+
* `--fix`. Además vigila la **deriva del modelo fusionado** (`modelDrift`): un
|
|
93
|
+
* tipo del origen que el destino no declara sale como error.
|
|
94
|
+
*
|
|
95
|
+
* Es una operación de PLATAFORMA: no lleva actor, no se expone por HTTP.
|
|
96
|
+
*/
|
|
97
|
+
export default class AuthzRelationsReconcile extends BaseCommand {
|
|
98
|
+
static commandName = 'authz:relations:reconcile';
|
|
99
|
+
static description = 'Rebuild (or verify, with --dry-run) the relation tuples of one RelationsDriver from another';
|
|
100
|
+
static options = {
|
|
101
|
+
startApp: true,
|
|
102
|
+
};
|
|
103
|
+
async run() {
|
|
104
|
+
if (!this.to) {
|
|
105
|
+
this.logger.error("Falta --to: 'node ace authz:relations:reconcile --to=openfga'. Es la clave del driver DESTINO en " +
|
|
106
|
+
'`relations.drivers` de config/authorization.ts.');
|
|
107
|
+
this.exitCode = 1;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const config = this.app.config.get('authorization');
|
|
111
|
+
const factories = config?.relations?.drivers ?? {};
|
|
112
|
+
const keys = Object.keys(factories);
|
|
113
|
+
if (keys.length === 0) {
|
|
114
|
+
this.logger.error('No hay `relations.drivers` en config/authorization.ts: declara los drivers de relaciones (por clave) ' +
|
|
115
|
+
'para poder migrar sus tuplas.');
|
|
116
|
+
this.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// El ORIGEN: `--from` si se pasa; si no, el ÚNICO que no es el destino.
|
|
120
|
+
let fromKey = this.from;
|
|
121
|
+
if (!fromKey) {
|
|
122
|
+
const candidates = keys.filter((k) => k !== this.to);
|
|
123
|
+
if (candidates.length !== 1) {
|
|
124
|
+
this.logger.error(`No se puede elegir el ORIGEN solo: hay ${candidates.length} candidatos (${candidates.join(', ') || 'ninguno'}). ` +
|
|
125
|
+
'Nómbralo con --from=<clave>: de dónde salen las tuplas decide lo que queda escrito.');
|
|
126
|
+
this.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
fromKey = candidates[0];
|
|
130
|
+
}
|
|
131
|
+
if (!factories[this.to]) {
|
|
132
|
+
this.logger.error(`El destino '${this.to}' no es una clave de relations.drivers (${keys.join(', ')}).`);
|
|
133
|
+
this.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!factories[fromKey]) {
|
|
137
|
+
this.logger.error(`El origen '${fromKey}' no es una clave de relations.drivers (${keys.join(', ')}).`);
|
|
138
|
+
this.exitCode = 1;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
// La partición (mono-tenant por defecto). El reconcile es POR partición.
|
|
142
|
+
const partition = this.partitionType && this.partitionType !== 'app'
|
|
143
|
+
? { type: this.partitionType, uuid: this.partitionUuid ?? '' }
|
|
144
|
+
: { type: 'app', uuid: null };
|
|
145
|
+
const { reconcileRelations } = await import('../src/relations/reconcile.js');
|
|
146
|
+
// La config del DESTINO, para vigilar la deriva del modelo fusionado en
|
|
147
|
+
// --dry-run: es la persistida (una sola en el store compartido).
|
|
148
|
+
const { readRelationsConfig } = await import('../src/relations_config_store.js');
|
|
149
|
+
const toConfig = await readRelationsConfig().catch(() => undefined);
|
|
150
|
+
const from = await factories[fromKey]();
|
|
151
|
+
const to = await factories[this.to]();
|
|
152
|
+
let report;
|
|
153
|
+
try {
|
|
154
|
+
report = await reconcileRelations({
|
|
155
|
+
from,
|
|
156
|
+
to,
|
|
157
|
+
partition,
|
|
158
|
+
dryRun: this.dryRun === true,
|
|
159
|
+
prune: this.prune === true,
|
|
160
|
+
allowMassDelete: this.allowMassDelete === true,
|
|
161
|
+
...(toConfig ? { toConfig } : {}),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
// R-17: el seguro de borrado masivo (500) sale como error de comando, no
|
|
166
|
+
// como stack crudo. `--allow-mass-delete` es la salida humana.
|
|
167
|
+
if (error?.code === 'E_AUTHZ_MASS_RECONCILE_REFUSED') {
|
|
168
|
+
this.logger.error(String(error.message ?? error));
|
|
169
|
+
this.exitCode = 1;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
const { lines, clean } = relationsReconcileLines(report);
|
|
175
|
+
for (const { level, message } of lines)
|
|
176
|
+
this.logger[level](message);
|
|
177
|
+
const resumen = `escritas ${report.written}, borradas ${report.deleted}, iguales ${report.unchanged}, sobran ${report.extra}`;
|
|
178
|
+
if (report.dryRun) {
|
|
179
|
+
if (clean) {
|
|
180
|
+
this.logger.success(`Sin deriva: '${this.to}' coincide con '${fromKey}' (${resumen}).`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
this.logger.error(`DERIVA con '${this.to}': ${resumen}. No se ha escrito nada (--dry-run).`);
|
|
184
|
+
this.exitCode = 1;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.logger.success(`Relaciones de '${this.to}' reconciliadas desde '${fromKey}': ${resumen}.`);
|
|
188
|
+
if (!clean) {
|
|
189
|
+
this.logger.warning('Queda algo que esta pasada no arregla sola (mira las líneas de arriba).');
|
|
190
|
+
this.exitCode = 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
__decorate([
|
|
195
|
+
flags.string({ description: 'Destination driver: a key of `relations.drivers` in config/authorization.ts' }),
|
|
196
|
+
__metadata("design:type", Object)
|
|
197
|
+
], AuthzRelationsReconcile.prototype, "to", void 0);
|
|
198
|
+
__decorate([
|
|
199
|
+
flags.string({ description: 'Source driver (a key of `relations.drivers`); required when more than one is registered' }),
|
|
200
|
+
__metadata("design:type", Object)
|
|
201
|
+
], AuthzRelationsReconcile.prototype, "from", void 0);
|
|
202
|
+
__decorate([
|
|
203
|
+
flags.boolean({ name: 'dry-run', description: 'Report what it would do and write nothing (this is the verifier)' }),
|
|
204
|
+
__metadata("design:type", Object)
|
|
205
|
+
], AuthzRelationsReconcile.prototype, "dryRun", void 0);
|
|
206
|
+
__decorate([
|
|
207
|
+
flags.boolean({ description: 'Also delete the tuples the source no longer backs (leftovers)' }),
|
|
208
|
+
__metadata("design:type", Object)
|
|
209
|
+
], AuthzRelationsReconcile.prototype, "prune", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
flags.boolean({
|
|
212
|
+
name: 'allow-mass-delete',
|
|
213
|
+
description: 'Allow --prune to empty the destination when the source has no usable tuple (mass-delete safeguard)',
|
|
214
|
+
}),
|
|
215
|
+
__metadata("design:type", Object)
|
|
216
|
+
], AuthzRelationsReconcile.prototype, "allowMassDelete", void 0);
|
|
217
|
+
__decorate([
|
|
218
|
+
flags.string({ name: 'partition-type', description: 'Partition (tenant) scope type to migrate; default: app (mono-tenant)' }),
|
|
219
|
+
__metadata("design:type", Object)
|
|
220
|
+
], AuthzRelationsReconcile.prototype, "partitionType", void 0);
|
|
221
|
+
__decorate([
|
|
222
|
+
flags.string({ name: 'partition-uuid', description: 'Partition (tenant) scope uuid; required with --partition-type' }),
|
|
223
|
+
__metadata("design:type", Object)
|
|
224
|
+
], AuthzRelationsReconcile.prototype, "partitionUuid", void 0);
|
|
225
|
+
//# 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,47 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
/**
|
|
4
|
+
* Drena la OUTBOX del árbol de scopes y aplica los cambios al driver
|
|
5
|
+
* (3b-2d; panel 2, cruce 4 · S5).
|
|
6
|
+
*
|
|
7
|
+
* Con `scopes.outbox` declarada, `authorization.scopes.attached/moved/
|
|
8
|
+
* detached` no escriben en el backend: encolan el cambio DENTRO de la
|
|
9
|
+
* transacción del consumidor. Este comando es quien lo propaga.
|
|
10
|
+
*
|
|
11
|
+
* node ace authz:scopes:relay # drena
|
|
12
|
+
* node ace authz:scopes:relay --dry-run # solo dice qué haría
|
|
13
|
+
* node ace authz:scopes:relay --limit 500 # una pasada acotada
|
|
14
|
+
*
|
|
15
|
+
* Pensado para un supervisor (un bucle, un cron corto, un worker): la pasada
|
|
16
|
+
* es REANUDABLE —lo que no entra sigue pendiente—. Un cambio que falla
|
|
17
|
+
* **aplaza lo que depende de él** (todo cambio posterior que nombre alguno de
|
|
18
|
+
* sus scopes) y deja pasar el resto: hasta 3b-2h paraba la pasada entera, y
|
|
19
|
+
* eso convertía una entrada irreparable en un tapón permanente para todos los
|
|
20
|
+
* tenants (3b-2h · 🔴 2). Sale con código ≠ 0 si algo falló o hay entradas
|
|
21
|
+
* APARCADAS, para que el supervisor se entere.
|
|
22
|
+
*
|
|
23
|
+
* **Escritor ÚNICO**: si la outbox sabe dar lease (`sqlScopeOutbox` lo hace),
|
|
24
|
+
* una segunda pasada simultánea no hace nada y lo dice. Sin lease, no lances
|
|
25
|
+
* dos a la vez: la rezagada re-aplica cambios viejos sobre el árbol nuevo.
|
|
26
|
+
*
|
|
27
|
+
* **Lo que este comando no puede arreglar**: entre el commit del consumidor
|
|
28
|
+
* y la pasada hay un lag (segundos) en el que el backend decide con el árbol
|
|
29
|
+
* VIEJO. Es un fail-open temporal —el tenant antiguo conserva acceso tras un
|
|
30
|
+
* `moved`, los denies heredados no aplican tras un `attached`—. No hay 2PC.
|
|
31
|
+
* Un ciclo más corto acorta esa ventana **mientras la cola avance**: lo que
|
|
32
|
+
* falla o se aparca no está acotado por el ciclo y hay que mirarlo.
|
|
33
|
+
*
|
|
34
|
+
* Es una operación de PLATAFORMA (como `authz:catalog:prune-orphans`): se
|
|
35
|
+
* salta `requireActor`/`requireWithin` porque la policy ya se juzgó al
|
|
36
|
+
* ENCOLAR, con el árbol y la sesión de aquel momento. No se expone por HTTP.
|
|
37
|
+
*/
|
|
38
|
+
export default class AuthzScopesRelay extends BaseCommand {
|
|
39
|
+
static commandName: string;
|
|
40
|
+
static description: string;
|
|
41
|
+
static options: CommandOptions;
|
|
42
|
+
dryRun: boolean | undefined;
|
|
43
|
+
limit: number | undefined;
|
|
44
|
+
batchSize: number | undefined;
|
|
45
|
+
run(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
//# 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;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;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;IAMO,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAG3B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAGzB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/B,GAAG;CAkFV"}
|
|
@@ -0,0 +1,141 @@
|
|
|
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
|
+
/**
|
|
12
|
+
* Drena la OUTBOX del árbol de scopes y aplica los cambios al driver
|
|
13
|
+
* (3b-2d; panel 2, cruce 4 · S5).
|
|
14
|
+
*
|
|
15
|
+
* Con `scopes.outbox` declarada, `authorization.scopes.attached/moved/
|
|
16
|
+
* detached` no escriben en el backend: encolan el cambio DENTRO de la
|
|
17
|
+
* transacción del consumidor. Este comando es quien lo propaga.
|
|
18
|
+
*
|
|
19
|
+
* node ace authz:scopes:relay # drena
|
|
20
|
+
* node ace authz:scopes:relay --dry-run # solo dice qué haría
|
|
21
|
+
* node ace authz:scopes:relay --limit 500 # una pasada acotada
|
|
22
|
+
*
|
|
23
|
+
* Pensado para un supervisor (un bucle, un cron corto, un worker): la pasada
|
|
24
|
+
* es REANUDABLE —lo que no entra sigue pendiente—. Un cambio que falla
|
|
25
|
+
* **aplaza lo que depende de él** (todo cambio posterior que nombre alguno de
|
|
26
|
+
* sus scopes) y deja pasar el resto: hasta 3b-2h paraba la pasada entera, y
|
|
27
|
+
* eso convertía una entrada irreparable en un tapón permanente para todos los
|
|
28
|
+
* tenants (3b-2h · 🔴 2). Sale con código ≠ 0 si algo falló o hay entradas
|
|
29
|
+
* APARCADAS, para que el supervisor se entere.
|
|
30
|
+
*
|
|
31
|
+
* **Escritor ÚNICO**: si la outbox sabe dar lease (`sqlScopeOutbox` lo hace),
|
|
32
|
+
* una segunda pasada simultánea no hace nada y lo dice. Sin lease, no lances
|
|
33
|
+
* dos a la vez: la rezagada re-aplica cambios viejos sobre el árbol nuevo.
|
|
34
|
+
*
|
|
35
|
+
* **Lo que este comando no puede arreglar**: entre el commit del consumidor
|
|
36
|
+
* y la pasada hay un lag (segundos) en el que el backend decide con el árbol
|
|
37
|
+
* VIEJO. Es un fail-open temporal —el tenant antiguo conserva acceso tras un
|
|
38
|
+
* `moved`, los denies heredados no aplican tras un `attached`—. No hay 2PC.
|
|
39
|
+
* Un ciclo más corto acorta esa ventana **mientras la cola avance**: lo que
|
|
40
|
+
* falla o se aparca no está acotado por el ciclo y hay que mirarlo.
|
|
41
|
+
*
|
|
42
|
+
* Es una operación de PLATAFORMA (como `authz:catalog:prune-orphans`): se
|
|
43
|
+
* salta `requireActor`/`requireWithin` porque la policy ya se juzgó al
|
|
44
|
+
* ENCOLAR, con el árbol y la sesión de aquel momento. No se expone por HTTP.
|
|
45
|
+
*/
|
|
46
|
+
export default class AuthzScopesRelay extends BaseCommand {
|
|
47
|
+
static commandName = 'authz:scopes:relay';
|
|
48
|
+
static description = 'Drain the scope-tree outbox and apply the pending tree changes to the driver';
|
|
49
|
+
static options = {
|
|
50
|
+
startApp: true,
|
|
51
|
+
};
|
|
52
|
+
async run() {
|
|
53
|
+
const { default: authorization } = await import('../services/main.js');
|
|
54
|
+
const report = await authorization.relayScopeChanges({
|
|
55
|
+
dryRun: this.dryRun === true,
|
|
56
|
+
limit: this.limit,
|
|
57
|
+
batchSize: this.batchSize,
|
|
58
|
+
});
|
|
59
|
+
const linea = (item) => {
|
|
60
|
+
const { change } = item;
|
|
61
|
+
const scope = (s) => `${s.type}:${s.uuid ?? ''}`;
|
|
62
|
+
const destino = change.op === 'detached' ? '' : ` → ${scope(change.parent)}`;
|
|
63
|
+
const intentos = item.attempts ? ` (intentos previos: ${item.attempts})` : '';
|
|
64
|
+
return `#${item.id} ${change.op} ${scope(change.child)}${destino}${intentos}`;
|
|
65
|
+
};
|
|
66
|
+
// Lo APARCADO se dice siempre, aplique o no esta pasada: es una
|
|
67
|
+
// divergencia permanente del árbol del backend, no un incidente pasado.
|
|
68
|
+
for (const item of report.dead) {
|
|
69
|
+
this.logger.error(`APARCADO tras agotar los intentos: ${linea(item)}${item.error ? ` — ${item.error}` : ''}`);
|
|
70
|
+
}
|
|
71
|
+
if (report.dead.length > 0) {
|
|
72
|
+
this.logger.warning('Una entrada aparcada NO se va a aplicar sola: el árbol del backend está divergente en ese nodo. ' +
|
|
73
|
+
'Revísala en la cola (el consumidor puede volver a notificar el cambio) o reconcilia.');
|
|
74
|
+
}
|
|
75
|
+
if (report.busy) {
|
|
76
|
+
this.logger.info('Otra pasada del relay tiene el lease de la cola: esta no ha aplicado nada (el relay es escritor único).');
|
|
77
|
+
if (report.dead.length > 0)
|
|
78
|
+
this.exitCode = 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (report.dryRun) {
|
|
82
|
+
for (const item of report.wouldApply)
|
|
83
|
+
this.logger.log(`pendiente: ${linea(item)}`);
|
|
84
|
+
if (report.wouldApply.length === 0) {
|
|
85
|
+
if (report.dead.length > 0)
|
|
86
|
+
this.exitCode = 1;
|
|
87
|
+
else
|
|
88
|
+
this.logger.success('La outbox del árbol está vacía: nada que propagar.');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.logger.warning(`${report.wouldApply.length} cambio(s) del árbol sin propagar. No se ha aplicado nada: repite sin --dry-run. ` +
|
|
92
|
+
`Mientras tanto el backend decide con el árbol VIEJO.`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Qué se aplicó, uno a uno: la pasada no es atómica y un contador no
|
|
96
|
+
// permite retomar nada.
|
|
97
|
+
for (const item of report.applied)
|
|
98
|
+
this.logger.log(`aplicado: ${linea(item)}`);
|
|
99
|
+
for (const item of report.deferred) {
|
|
100
|
+
this.logger.warning(`aplazado: ${linea(item)}${item.error ? ` — ${item.error}` : ''}`);
|
|
101
|
+
}
|
|
102
|
+
if (report.failures.length > 0) {
|
|
103
|
+
for (const item of report.failures) {
|
|
104
|
+
this.logger.error(`falló #${item.id} (${item.change.op} ` +
|
|
105
|
+
`${item.change.child.type}:${item.change.child.uuid ?? ''}): ${item.error}`);
|
|
106
|
+
}
|
|
107
|
+
this.logger.warning('Lo que DEPENDE de un cambio fallido no se adelanta (el orden del árbol importa: aparece como «aplazado»), ' +
|
|
108
|
+
'pero el resto de la cola sí avanza. Arregla la causa y vuelve a ejecutar; retoma donde lo dejó. ' +
|
|
109
|
+
'Agotados los intentos que declare tu outbox, una entrada irreparable se APARCA y se reporta.');
|
|
110
|
+
this.exitCode = 1;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (report.dead.length > 0)
|
|
114
|
+
this.exitCode = 1;
|
|
115
|
+
if (report.applied.length === 0) {
|
|
116
|
+
if (report.dead.length === 0)
|
|
117
|
+
this.logger.success('La outbox del árbol está vacía: nada que propagar.');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.logger.success(`${report.applied.length} cambio(s) del árbol propagado(s) al driver.`);
|
|
121
|
+
if (report.remaining) {
|
|
122
|
+
this.logger.warning('Quedan cambios pendientes (se alcanzó el límite de la pasada o hay aplazados): vuelve a ejecutar.');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
__decorate([
|
|
127
|
+
flags.boolean({
|
|
128
|
+
name: 'dry-run',
|
|
129
|
+
description: 'List the pending tree changes without applying any of them',
|
|
130
|
+
}),
|
|
131
|
+
__metadata("design:type", Object)
|
|
132
|
+
], AuthzScopesRelay.prototype, "dryRun", void 0);
|
|
133
|
+
__decorate([
|
|
134
|
+
flags.number({ description: 'Stop after applying this many changes (the rest stays pending)' }),
|
|
135
|
+
__metadata("design:type", Object)
|
|
136
|
+
], AuthzScopesRelay.prototype, "limit", void 0);
|
|
137
|
+
__decorate([
|
|
138
|
+
flags.number({ name: 'batch-size', description: 'How many pending changes to read per round trip' }),
|
|
139
|
+
__metadata("design:type", Object)
|
|
140
|
+
], AuthzScopesRelay.prototype, "batchSize", void 0);
|
|
141
|
+
//# sourceMappingURL=authz_scopes_relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_scopes_relay.js","sourceRoot":"","sources":["../../commands/authz_scopes_relay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
import type { FreezeStatus } from '../src/manager.js';
|
|
4
|
+
/**
|
|
5
|
+
* Qué hacer con el freeze vivo, en una función PURA (mismo patrón que
|
|
6
|
+
* `reconcileLines`): es la única decisión del comando y así tiene su caso
|
|
7
|
+
* sin montar un ace.
|
|
8
|
+
*
|
|
9
|
+
* Reglas:
|
|
10
|
+
* - Sin freeze vivo ⇒ no-op en verde (idempotente: descongelar lo
|
|
11
|
+
* descongelado no es un incidente).
|
|
12
|
+
* - Freeze de OPERADOR ⇒ se levanta (este comando es su otra mitad).
|
|
13
|
+
* - Freeze de OTRO dueño (`reconcile`, `platform`) ⇒ NO se levanta sin
|
|
14
|
+
* `--fence`: levantar la barrera de una pasada viva es exactamente el
|
|
15
|
+
* A1.3 que el token cierra. `--fence=<n>` es la decisión humana explícita
|
|
16
|
+
* («ese proceso murió sin lease»), y tiene que coincidir con el fence
|
|
17
|
+
* vivo — un fence viejo no levanta la ventana de otro.
|
|
18
|
+
*/
|
|
19
|
+
export declare function unfreezePlan(status: FreezeStatus | null, fence: number | undefined): {
|
|
20
|
+
action: 'noop' | 'lift' | 'refuse';
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* **Cierra la ventana de cutover** (3b-7): levanta el freeze de operador que
|
|
25
|
+
* abrió `authz:freeze`. La otra mitad de ese comando; ver su docblock.
|
|
26
|
+
*
|
|
27
|
+
* node ace authz:unfreeze # levanta la ventana del operador
|
|
28
|
+
* node ace authz:unfreeze --fence=7 # decisión humana: levanta ESE freeze aunque no sea de operador
|
|
29
|
+
*/
|
|
30
|
+
export default class AuthzUnfreeze extends BaseCommand {
|
|
31
|
+
static commandName: string;
|
|
32
|
+
static description: string;
|
|
33
|
+
static options: CommandOptions;
|
|
34
|
+
fence: number | undefined;
|
|
35
|
+
run(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=authz_unfreeze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_unfreeze.d.ts","sourceRoot":"","sources":["../../commands/authz_unfreeze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,GAAG,IAAI,EAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA0BzD;AAED;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IACpD,MAAM,CAAC,WAAW,SAAmB;IACrC,MAAM,CAAC,WAAW,SAAqF;IAEvG,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAKO,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAE3B,GAAG;CAqBV"}
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
/**
|
|
12
|
+
* Qué hacer con el freeze vivo, en una función PURA (mismo patrón que
|
|
13
|
+
* `reconcileLines`): es la única decisión del comando y así tiene su caso
|
|
14
|
+
* sin montar un ace.
|
|
15
|
+
*
|
|
16
|
+
* Reglas:
|
|
17
|
+
* - Sin freeze vivo ⇒ no-op en verde (idempotente: descongelar lo
|
|
18
|
+
* descongelado no es un incidente).
|
|
19
|
+
* - Freeze de OPERADOR ⇒ se levanta (este comando es su otra mitad).
|
|
20
|
+
* - Freeze de OTRO dueño (`reconcile`, `platform`) ⇒ NO se levanta sin
|
|
21
|
+
* `--fence`: levantar la barrera de una pasada viva es exactamente el
|
|
22
|
+
* A1.3 que el token cierra. `--fence=<n>` es la decisión humana explícita
|
|
23
|
+
* («ese proceso murió sin lease»), y tiene que coincidir con el fence
|
|
24
|
+
* vivo — un fence viejo no levanta la ventana de otro.
|
|
25
|
+
*/
|
|
26
|
+
export function unfreezePlan(status, fence) {
|
|
27
|
+
if (status === null) {
|
|
28
|
+
return { action: 'noop', message: 'No hay ningún freeze vivo: nada que levantar.' };
|
|
29
|
+
}
|
|
30
|
+
if (fence !== undefined && fence !== status.fence) {
|
|
31
|
+
return {
|
|
32
|
+
action: 'refuse',
|
|
33
|
+
message: `El fence vivo es ${status.fence} y pediste levantar el ${fence}: ese freeze ya no existe. ` +
|
|
34
|
+
`Mira el estado y repite con el fence actual si de verdad quieres levantarlo.`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (status.kind !== 'operator' && fence === undefined) {
|
|
38
|
+
return {
|
|
39
|
+
action: 'refuse',
|
|
40
|
+
message: `El freeze vivo NO es de operador: lo sostiene ${status.holder} (${status.reason}). ` +
|
|
41
|
+
`Levantárselo dejaría su pasada corriendo sin barrera (y su reporte lo delatará como lapsed). ` +
|
|
42
|
+
`Espera a que termine${status.untilMs === null ? '' : ' — o a que su lease venza solo'}; si su proceso ` +
|
|
43
|
+
`murió sin lease, la decisión humana es --fence=${status.fence}.`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
action: 'lift',
|
|
48
|
+
message: `Freeze de ${status.holder} (${status.reason}, fence ${status.fence}) levantado: la flota vuelve a escribir.`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* **Cierra la ventana de cutover** (3b-7): levanta el freeze de operador que
|
|
53
|
+
* abrió `authz:freeze`. La otra mitad de ese comando; ver su docblock.
|
|
54
|
+
*
|
|
55
|
+
* node ace authz:unfreeze # levanta la ventana del operador
|
|
56
|
+
* node ace authz:unfreeze --fence=7 # decisión humana: levanta ESE freeze aunque no sea de operador
|
|
57
|
+
*/
|
|
58
|
+
export default class AuthzUnfreeze extends BaseCommand {
|
|
59
|
+
static commandName = 'authz:unfreeze';
|
|
60
|
+
static description = 'Close the cutover window: lift the operator freeze and let the fleet write again';
|
|
61
|
+
static options = {
|
|
62
|
+
startApp: true,
|
|
63
|
+
};
|
|
64
|
+
async run() {
|
|
65
|
+
const { default: authorization } = await import('../services/main.js');
|
|
66
|
+
const status = await authorization.freezeStatus();
|
|
67
|
+
const plan = unfreezePlan(status, this.fence);
|
|
68
|
+
if (plan.action === 'noop') {
|
|
69
|
+
this.logger.success(plan.message);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (plan.action === 'refuse') {
|
|
73
|
+
this.logger.error(plan.message);
|
|
74
|
+
this.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const lifted = await authorization.unfreeze({ fence: status.fence, holder: status.holder });
|
|
78
|
+
if (!lifted) {
|
|
79
|
+
// Entre el SELECT y el UPDATE alguien lo levantó o venció: no es un error.
|
|
80
|
+
this.logger.success('El freeze ya no estaba (lo levantó su dueño o venció su lease).');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.logger.success(plan.message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
__decorate([
|
|
87
|
+
flags.number({
|
|
88
|
+
description: 'Lift exactly this freeze even if it is not an operator window (its owner died without a lease)',
|
|
89
|
+
}),
|
|
90
|
+
__metadata("design:type", Object)
|
|
91
|
+
], AuthzUnfreeze.prototype, "fence", void 0);
|
|
92
|
+
//# sourceMappingURL=authz_unfreeze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_unfreeze.js","sourceRoot":"","sources":["../../commands/authz_unfreeze.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAIvD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAC1B,MAA2B,EAC3B,KAAyB;IAEzB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAA;IACrF,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;QAClD,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,OAAO,EACL,oBAAoB,MAAM,CAAC,KAAK,0BAA0B,KAAK,6BAA6B;gBAC5F,8EAA8E;SACjF,CAAA;IACH,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACtD,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,OAAO,EACL,iDAAiD,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,KAAK;gBACrF,+FAA+F;gBAC/F,uBAAuB,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gCAAgC,kBAAkB;gBACxG,kDAAkD,MAAM,CAAC,KAAK,GAAG;SACpE,CAAA;IACH,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,aAAa,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,WAAW,MAAM,CAAC,KAAK,0CAA0C;KACvH,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IACpD,MAAM,CAAC,WAAW,GAAG,gBAAgB,CAAA;IACrC,MAAM,CAAC,WAAW,GAAG,kFAAkF,CAAA;IAEvG,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,IAAI;KACf,CAAA;IAOD,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAA;QACtE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,YAAY,EAAE,CAAA;QACjD,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACjC,OAAM;QACR,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAC/B,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC7F,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,2EAA2E;YAC3E,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,iEAAiE,CAAC,CAAA;YACtF,OAAM;QACR,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnC,CAAC;;AAtBO;IAHP,KAAK,CAAC,MAAM,CAAC;QACZ,WAAW,EAAE,gGAAgG;KAC9G,CAAC;;4CAC+B"}
|
package/build/commands/main.d.ts
CHANGED
|
@@ -3,5 +3,12 @@
|
|
|
3
3
|
* node ace configure @jantstack/adonis-authz
|
|
4
4
|
*/
|
|
5
5
|
export { default as OpenFgaProvision } from './openfga_provision.js';
|
|
6
|
-
export { default as
|
|
6
|
+
export { default as AuthzCatalogDiff } from './authz_catalog_diff.js';
|
|
7
|
+
export { default as AuthzCatalogSync } from './authz_catalog_sync.js';
|
|
8
|
+
export { default as AuthzCatalogPruneOrphans } from './authz_catalog_prune_orphans.js';
|
|
9
|
+
export { default as AuthzScopesRelay } from './authz_scopes_relay.js';
|
|
10
|
+
export { default as AuthzReconcile } from './authz_reconcile.js';
|
|
11
|
+
export { default as AuthzRelationsReconcile } from './authz_relations_reconcile.js';
|
|
12
|
+
export { default as AuthzFreeze } from './authz_freeze.js';
|
|
13
|
+
export { default as AuthzUnfreeze } from './authz_unfreeze.js';
|
|
7
14
|
//# sourceMappingURL=main.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AACtF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACnF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
|
package/build/commands/main.js
CHANGED
|
@@ -3,5 +3,12 @@
|
|
|
3
3
|
* node ace configure @jantstack/adonis-authz
|
|
4
4
|
*/
|
|
5
5
|
export { default as OpenFgaProvision } from './openfga_provision.js';
|
|
6
|
-
export { default as
|
|
6
|
+
export { default as AuthzCatalogDiff } from './authz_catalog_diff.js';
|
|
7
|
+
export { default as AuthzCatalogSync } from './authz_catalog_sync.js';
|
|
8
|
+
export { default as AuthzCatalogPruneOrphans } from './authz_catalog_prune_orphans.js';
|
|
9
|
+
export { default as AuthzScopesRelay } from './authz_scopes_relay.js';
|
|
10
|
+
export { default as AuthzReconcile } from './authz_reconcile.js';
|
|
11
|
+
export { default as AuthzRelationsReconcile } from './authz_relations_reconcile.js';
|
|
12
|
+
export { default as AuthzFreeze } from './authz_freeze.js';
|
|
13
|
+
export { default as AuthzUnfreeze } from './authz_unfreeze.js';
|
|
7
14
|
//# sourceMappingURL=main.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AACtF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACnF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
|