@jantstack/adonis-authz 2.5.0-alpha.3 → 2.5.0-alpha.4
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 +414 -35
- package/build/commands/authz_catalog_diff.d.ts +0 -1
- package/build/commands/authz_catalog_diff.js +0 -1
- package/build/commands/authz_catalog_prune_orphans.d.ts +0 -1
- package/build/commands/authz_catalog_prune_orphans.js +0 -1
- package/build/commands/authz_catalog_sync.d.ts +0 -1
- package/build/commands/authz_catalog_sync.js +0 -1
- package/build/commands/authz_freeze.d.ts +0 -1
- package/build/commands/authz_freeze.js +0 -1
- package/build/commands/authz_reconcile.d.ts +0 -1
- package/build/commands/authz_reconcile.js +0 -1
- package/build/commands/authz_relations_reconcile.d.ts +0 -1
- package/build/commands/authz_relations_reconcile.js +56 -8
- package/build/commands/authz_scopes_relay.d.ts +0 -1
- package/build/commands/authz_scopes_relay.js +49 -5
- package/build/commands/authz_scopes_requeue.d.ts +0 -1
- package/build/commands/authz_scopes_requeue.js +14 -2
- package/build/commands/authz_unfreeze.d.ts +0 -1
- package/build/commands/authz_unfreeze.js +0 -1
- package/build/commands/main.d.ts +0 -1
- package/build/commands/main.js +0 -1
- package/build/commands/openfga_provision.d.ts +10 -1
- package/build/commands/openfga_provision.js +43 -3
- package/build/configure.d.ts +0 -1
- package/build/configure.js +0 -1
- package/build/index.d.ts +7 -5
- package/build/index.js +10 -3
- package/build/providers/authz_provider.d.ts +0 -1
- package/build/providers/authz_provider.js +0 -1
- package/build/services/main.d.ts +0 -1
- package/build/services/main.js +0 -1
- package/build/services/relations.d.ts +0 -1
- package/build/services/relations.js +0 -1
- package/build/src/catalog/catalog.d.ts +0 -1
- package/build/src/catalog/catalog.js +0 -1
- package/build/src/catalog/catalog_cache.d.ts +13 -1
- package/build/src/catalog/catalog_cache.js +25 -1
- package/build/src/clock.d.ts +0 -1
- package/build/src/clock.js +0 -1
- package/build/src/define_config.d.ts +0 -1
- package/build/src/define_config.js +24 -1
- package/build/src/drivers/database_driver.d.ts +6 -1
- package/build/src/drivers/database_driver.js +22 -2
- package/build/src/drivers/database_relations_driver.d.ts +11 -5
- package/build/src/drivers/database_relations_driver.js +90 -18
- package/build/src/drivers/openfga_driver.d.ts +159 -26
- package/build/src/drivers/openfga_driver.js +882 -199
- package/build/src/drivers/openfga_facts.d.ts +91 -1
- package/build/src/drivers/openfga_facts.js +277 -3
- package/build/src/drivers/openfga_relations_driver.d.ts +41 -13
- package/build/src/drivers/openfga_relations_driver.js +422 -228
- package/build/src/errors.d.ts +121 -1
- package/build/src/errors.js +153 -1
- package/build/src/expiry.d.ts +51 -2
- package/build/src/expiry.js +96 -2
- package/build/src/freeze.d.ts +0 -1
- package/build/src/freeze.js +0 -1
- package/build/src/hierarchical_resolver.d.ts +0 -1
- package/build/src/hierarchical_resolver.js +0 -1
- package/build/src/http/app_access_middleware.d.ts +0 -1
- package/build/src/http/app_access_middleware.js +0 -1
- package/build/src/http/resource_access_middleware.d.ts +0 -1
- package/build/src/http/resource_access_middleware.js +0 -1
- package/build/src/identity.d.ts +2 -1
- package/build/src/identity.js +4 -1
- package/build/src/manager.d.ts +0 -1
- package/build/src/manager.js +156 -28
- package/build/src/memoize_ancestors.d.ts +0 -1
- package/build/src/memoize_ancestors.js +0 -1
- package/build/src/models/authz_assignment.d.ts +5 -6
- package/build/src/models/authz_assignment.js +0 -1
- package/build/src/models/authz_deny.d.ts +5 -6
- package/build/src/models/authz_deny.js +0 -1
- package/build/src/models/authz_permission.d.ts +5 -6
- package/build/src/models/authz_permission.js +0 -1
- package/build/src/models/authz_role.d.ts +5 -6
- package/build/src/models/authz_role.js +0 -1
- package/build/src/models/authz_role_permission.d.ts +5 -6
- package/build/src/models/authz_role_permission.js +0 -1
- package/build/src/openfga.d.ts +2 -3
- package/build/src/openfga.js +4 -2
- package/build/src/reconcile.d.ts +0 -1
- package/build/src/reconcile.js +0 -1
- package/build/src/relation_partition_trigger.d.ts +0 -1
- package/build/src/relation_partition_trigger.js +0 -1
- package/build/src/relations/check_many.d.ts +0 -1
- package/build/src/relations/check_many.js +0 -1
- package/build/src/relations/define_relations_config.d.ts +25 -3
- package/build/src/relations/define_relations_config.js +8 -3
- package/build/src/relations/deny_reach.d.ts +102 -0
- package/build/src/relations/deny_reach.js +230 -0
- package/build/src/relations/group_depth.d.ts +215 -0
- package/build/src/relations/group_depth.js +453 -0
- package/build/src/relations/manager.d.ts +14 -7
- package/build/src/relations/manager.js +60 -15
- package/build/src/relations/page.d.ts +19 -0
- package/build/src/relations/page.js +74 -0
- package/build/src/relations/reconcile.d.ts +58 -4
- package/build/src/relations/reconcile.js +266 -12
- package/build/src/relations/tuple_key.d.ts +0 -1
- package/build/src/relations/tuple_key.js +0 -1
- package/build/src/relations/tuple_order.d.ts +5 -4
- package/build/src/relations/tuple_order.js +5 -4
- package/build/src/relations_config_store.d.ts +57 -2
- package/build/src/relations_config_store.js +155 -5
- package/build/src/relations_reconcile.d.ts +0 -1
- package/build/src/relations_reconcile.js +0 -1
- package/build/src/scope_outbox.d.ts +0 -1
- package/build/src/scope_outbox.js +0 -1
- package/build/src/shared/backend_guard.d.ts +52 -1
- package/build/src/shared/backend_guard.js +164 -2
- package/build/src/shared/sql_expiry.d.ts +0 -1
- package/build/src/shared/sql_expiry.js +0 -1
- package/build/src/shared/transaction_guard.d.ts +0 -1
- package/build/src/shared/transaction_guard.js +0 -1
- package/build/src/sql_descendants.d.ts +0 -1
- package/build/src/sql_descendants.js +0 -1
- package/build/src/testing/contract.d.ts +7 -1
- package/build/src/testing/contract.js +77 -1
- package/build/src/testing/main.d.ts +0 -1
- package/build/src/testing/main.js +0 -1
- package/build/src/testing/migration_contract.d.ts +0 -1
- package/build/src/testing/migration_contract.js +0 -1
- package/build/src/testing/relations_contract.d.ts +7 -1
- package/build/src/testing/relations_contract.js +174 -7
- package/build/src/testing/relations_reconcile_contract.d.ts +0 -1
- package/build/src/testing/relations_reconcile_contract.js +0 -1
- package/build/src/testing/scope_outbox_contract.d.ts +0 -1
- package/build/src/testing/scope_outbox_contract.js +0 -1
- package/build/src/testing/scope_tree.d.ts +0 -1
- package/build/src/testing/scope_tree.js +0 -1
- package/build/src/testing/test_clock.d.ts +0 -1
- package/build/src/testing/test_clock.js +0 -1
- package/build/src/traits/authz_scopes.d.ts +0 -1
- package/build/src/traits/authz_scopes.js +0 -1
- package/build/src/traits/has_uuid.d.ts +5 -6
- package/build/src/traits/has_uuid.js +0 -1
- package/build/src/types.d.ts +89 -14
- package/build/src/types.js +4 -1
- package/build/stubs/config/authorization.stub +22 -6
- package/build/stubs/main.d.ts +0 -1
- package/build/stubs/main.js +0 -1
- package/package.json +4 -3
- package/build/commands/authz_catalog_diff.d.ts.map +0 -1
- package/build/commands/authz_catalog_diff.js.map +0 -1
- package/build/commands/authz_catalog_prune_orphans.d.ts.map +0 -1
- package/build/commands/authz_catalog_prune_orphans.js.map +0 -1
- package/build/commands/authz_catalog_sync.d.ts.map +0 -1
- package/build/commands/authz_catalog_sync.js.map +0 -1
- package/build/commands/authz_freeze.d.ts.map +0 -1
- package/build/commands/authz_freeze.js.map +0 -1
- package/build/commands/authz_reconcile.d.ts.map +0 -1
- package/build/commands/authz_reconcile.js.map +0 -1
- package/build/commands/authz_relations_reconcile.d.ts.map +0 -1
- package/build/commands/authz_relations_reconcile.js.map +0 -1
- package/build/commands/authz_scopes_relay.d.ts.map +0 -1
- package/build/commands/authz_scopes_relay.js.map +0 -1
- package/build/commands/authz_scopes_requeue.d.ts.map +0 -1
- package/build/commands/authz_scopes_requeue.js.map +0 -1
- package/build/commands/authz_unfreeze.d.ts.map +0 -1
- package/build/commands/authz_unfreeze.js.map +0 -1
- package/build/commands/main.d.ts.map +0 -1
- package/build/commands/main.js.map +0 -1
- package/build/commands/openfga_provision.d.ts.map +0 -1
- package/build/commands/openfga_provision.js.map +0 -1
- package/build/configure.d.ts.map +0 -1
- package/build/configure.js.map +0 -1
- package/build/index.d.ts.map +0 -1
- package/build/index.js.map +0 -1
- package/build/providers/authz_provider.d.ts.map +0 -1
- package/build/providers/authz_provider.js.map +0 -1
- package/build/services/main.d.ts.map +0 -1
- package/build/services/main.js.map +0 -1
- package/build/services/relations.d.ts.map +0 -1
- package/build/services/relations.js.map +0 -1
- package/build/src/catalog/catalog.d.ts.map +0 -1
- package/build/src/catalog/catalog.js.map +0 -1
- package/build/src/catalog/catalog_cache.d.ts.map +0 -1
- package/build/src/catalog/catalog_cache.js.map +0 -1
- package/build/src/clock.d.ts.map +0 -1
- package/build/src/clock.js.map +0 -1
- package/build/src/define_config.d.ts.map +0 -1
- package/build/src/define_config.js.map +0 -1
- package/build/src/drivers/database_driver.d.ts.map +0 -1
- package/build/src/drivers/database_driver.js.map +0 -1
- package/build/src/drivers/database_relations_driver.d.ts.map +0 -1
- package/build/src/drivers/database_relations_driver.js.map +0 -1
- package/build/src/drivers/openfga_driver.d.ts.map +0 -1
- package/build/src/drivers/openfga_driver.js.map +0 -1
- package/build/src/drivers/openfga_facts.d.ts.map +0 -1
- package/build/src/drivers/openfga_facts.js.map +0 -1
- package/build/src/drivers/openfga_relations_driver.d.ts.map +0 -1
- package/build/src/drivers/openfga_relations_driver.js.map +0 -1
- package/build/src/errors.d.ts.map +0 -1
- package/build/src/errors.js.map +0 -1
- package/build/src/expiry.d.ts.map +0 -1
- package/build/src/expiry.js.map +0 -1
- package/build/src/freeze.d.ts.map +0 -1
- package/build/src/freeze.js.map +0 -1
- package/build/src/hierarchical_resolver.d.ts.map +0 -1
- package/build/src/hierarchical_resolver.js.map +0 -1
- package/build/src/http/app_access_middleware.d.ts.map +0 -1
- package/build/src/http/app_access_middleware.js.map +0 -1
- package/build/src/http/resource_access_middleware.d.ts.map +0 -1
- package/build/src/http/resource_access_middleware.js.map +0 -1
- package/build/src/identity.d.ts.map +0 -1
- package/build/src/identity.js.map +0 -1
- package/build/src/manager.d.ts.map +0 -1
- package/build/src/manager.js.map +0 -1
- package/build/src/memoize_ancestors.d.ts.map +0 -1
- package/build/src/memoize_ancestors.js.map +0 -1
- package/build/src/models/authz_assignment.d.ts.map +0 -1
- package/build/src/models/authz_assignment.js.map +0 -1
- package/build/src/models/authz_deny.d.ts.map +0 -1
- package/build/src/models/authz_deny.js.map +0 -1
- package/build/src/models/authz_permission.d.ts.map +0 -1
- package/build/src/models/authz_permission.js.map +0 -1
- package/build/src/models/authz_role.d.ts.map +0 -1
- package/build/src/models/authz_role.js.map +0 -1
- package/build/src/models/authz_role_permission.d.ts.map +0 -1
- package/build/src/models/authz_role_permission.js.map +0 -1
- package/build/src/openfga.d.ts.map +0 -1
- package/build/src/openfga.js.map +0 -1
- package/build/src/reconcile.d.ts.map +0 -1
- package/build/src/reconcile.js.map +0 -1
- package/build/src/relation_partition_trigger.d.ts.map +0 -1
- package/build/src/relation_partition_trigger.js.map +0 -1
- package/build/src/relations/check_many.d.ts.map +0 -1
- package/build/src/relations/check_many.js.map +0 -1
- package/build/src/relations/define_relations_config.d.ts.map +0 -1
- package/build/src/relations/define_relations_config.js.map +0 -1
- package/build/src/relations/manager.d.ts.map +0 -1
- package/build/src/relations/manager.js.map +0 -1
- package/build/src/relations/reconcile.d.ts.map +0 -1
- package/build/src/relations/reconcile.js.map +0 -1
- package/build/src/relations/tuple_key.d.ts.map +0 -1
- package/build/src/relations/tuple_key.js.map +0 -1
- package/build/src/relations/tuple_order.d.ts.map +0 -1
- package/build/src/relations/tuple_order.js.map +0 -1
- package/build/src/relations_config_store.d.ts.map +0 -1
- package/build/src/relations_config_store.js.map +0 -1
- package/build/src/relations_reconcile.d.ts.map +0 -1
- package/build/src/relations_reconcile.js.map +0 -1
- package/build/src/scope_outbox.d.ts.map +0 -1
- package/build/src/scope_outbox.js.map +0 -1
- package/build/src/shared/backend_guard.d.ts.map +0 -1
- package/build/src/shared/backend_guard.js.map +0 -1
- package/build/src/shared/sql_expiry.d.ts.map +0 -1
- package/build/src/shared/sql_expiry.js.map +0 -1
- package/build/src/shared/transaction_guard.d.ts.map +0 -1
- package/build/src/shared/transaction_guard.js.map +0 -1
- package/build/src/sql_descendants.d.ts.map +0 -1
- package/build/src/sql_descendants.js.map +0 -1
- package/build/src/testing/contract.d.ts.map +0 -1
- package/build/src/testing/contract.js.map +0 -1
- package/build/src/testing/main.d.ts.map +0 -1
- package/build/src/testing/main.js.map +0 -1
- package/build/src/testing/migration_contract.d.ts.map +0 -1
- package/build/src/testing/migration_contract.js.map +0 -1
- package/build/src/testing/relations_contract.d.ts.map +0 -1
- package/build/src/testing/relations_contract.js.map +0 -1
- package/build/src/testing/relations_reconcile_contract.d.ts.map +0 -1
- package/build/src/testing/relations_reconcile_contract.js.map +0 -1
- package/build/src/testing/scope_outbox_contract.d.ts.map +0 -1
- package/build/src/testing/scope_outbox_contract.js.map +0 -1
- package/build/src/testing/scope_tree.d.ts.map +0 -1
- package/build/src/testing/scope_tree.js.map +0 -1
- package/build/src/testing/test_clock.d.ts.map +0 -1
- package/build/src/testing/test_clock.js.map +0 -1
- package/build/src/traits/authz_scopes.d.ts.map +0 -1
- package/build/src/traits/authz_scopes.js.map +0 -1
- package/build/src/traits/has_uuid.d.ts.map +0 -1
- package/build/src/traits/has_uuid.js.map +0 -1
- package/build/src/types.d.ts.map +0 -1
- package/build/src/types.js.map +0 -1
- package/build/stubs/main.d.ts.map +0 -1
- package/build/stubs/main.js.map +0 -1
|
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
11
11
|
import { RELATION_USERSET_RESIDUE_DELETE } from '../src/relations/define_relations_config.js';
|
|
12
|
+
import { denyReachLine } from '../src/relations/deny_reach.js';
|
|
12
13
|
import { RelationConfigError } from '../src/errors.js';
|
|
13
14
|
// Plan 2.5 · lote B · B7: la vía pública vive en su subpath (`src/relations_reconcile.ts`,
|
|
14
15
|
// `@jantstack/adonis-authz/relations/reconcile`); este módulo es el COMANDO y la importa de allí.
|
|
@@ -84,6 +85,34 @@ export function relationsReconcileLines(report) {
|
|
|
84
85
|
`el deny concedería lo que el origen niega. Receta: ${refusal.recipe}`,
|
|
85
86
|
});
|
|
86
87
|
}
|
|
88
|
+
// Lote RC · sublote (8): el DETECTOR del alcance de los denies (solo si la pasada lo pidió: `--dry-run`). Una línea por
|
|
89
|
+
// hallazgo con su prefijo ESTABLE (punto 7) y su receta: «deny con alcance ENCOGIDO» / «deny HUÉRFANO» (persistida →
|
|
90
|
+
// servida) y «línea base RANCIA» (servida → persistida, firma (8 · rancia)) son deriva (exit ≠ 0). Sin línea base y con
|
|
91
|
+
// denies vivos sin juzgar, la línea «alcance de denies: SIN línea base y N deny(s) vivos sin juzgar» y exit 0 (firma
|
|
92
|
+
// (8 · `--dry-run`), g2: el runbook no persiste la config y no cambia). Sin nada de eso, UNA línea que dice qué se comparó.
|
|
93
|
+
if (report.denyReach !== undefined) {
|
|
94
|
+
const { baseline, findings, stale, unjudged } = report.denyReach;
|
|
95
|
+
for (const finding of [...findings, ...stale]) {
|
|
96
|
+
lines.push({ level: 'error', message: `${denyReachLine(finding)} (lado: ${finding.sides.join(', ')})` });
|
|
97
|
+
}
|
|
98
|
+
if (baseline === 'none' && unjudged > 0) {
|
|
99
|
+
lines.push({
|
|
100
|
+
level: 'warning',
|
|
101
|
+
message: `alcance de denies: SIN línea base y ${unjudged} deny(s) vivos sin juzgar (no hay config persistida en authz_relations_config): ` +
|
|
102
|
+
`solo se buscaron huérfanos, y un estrechamiento de su alcance NO se ve sin línea base. Si usas deny, persistir la config es ` +
|
|
103
|
+
`paso obligatorio de release: adóptala una vez con saveRelationsConfig(spec, { establishBaseline: true }) y vigila con ` +
|
|
104
|
+
`checkRelationsConfigDenyReach (el detector de un solo driver).`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else if (findings.length === 0 && stale.length === 0) {
|
|
108
|
+
lines.push({
|
|
109
|
+
level: 'log',
|
|
110
|
+
message: baseline === 'persisted'
|
|
111
|
+
? 'alcance de denies: ningún deny vivo de la partición encogió ni quedó huérfano entre la config persistida (línea base) y la que sirve este proceso, y la línea base no es más estrecha que lo servido.'
|
|
112
|
+
: 'alcance de denies: SIN línea base (no hay config persistida en authz_relations_config) y ningún deny vivo en la partición que juzgar.',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
87
116
|
// R-15: la caducada del origen es la pérdida DECLARADA de la migración (como
|
|
88
117
|
// `expired` en roles, 3b-8 · B2): se dice, no es deriva.
|
|
89
118
|
if (report.skipped.expired > 0) {
|
|
@@ -137,7 +166,9 @@ export function relationsReconcileLines(report) {
|
|
|
137
166
|
level: 'error',
|
|
138
167
|
message: detail.reason === 'userset-unsupported'
|
|
139
168
|
? `sin migrar ${what}. Es un residuo pre-2.5 (B3) del store de ORIGEN: el destino no lo admite por ninguna puerta y ningún --prune lo toca. Bórrala a mano (los tres motores): ${RELATION_USERSET_RESIDUE_DELETE} y repite la pasada.`
|
|
140
|
-
:
|
|
169
|
+
: detail.reason === 'invalid-condition'
|
|
170
|
+
? `sin migrar ${what}. Es una tupla cuya condición el paquete no escribe ni sabe leer (L11): no se migra como permanente ni como caducada. Revísala y corrígela o bórrala a mano (SDK/CLI de OpenFGA) y repite la pasada.`
|
|
171
|
+
: `sin migrar ${what}. Es un dato del store de ORIGEN, no una pregunta: corrígela o bórrala a mano (SQL/SDK) y repite la pasada.`,
|
|
141
172
|
});
|
|
142
173
|
continue;
|
|
143
174
|
}
|
|
@@ -164,11 +195,13 @@ export function relationsReconcileLines(report) {
|
|
|
164
195
|
level: 'error',
|
|
165
196
|
message: detail.reason === 'userset-unsupported'
|
|
166
197
|
? `sobra ${what}. Es un residuo pre-2.5 (B3) del DESTINO: --prune ${verdict} lo borra${detail.pruned === false ? '' : 'ría'} (nunca un DELETE de plataforma sobre una fila que la API no admite). Bórrala a mano (los tres motores): ${RELATION_USERSET_RESIDUE_DELETE} y repite la pasada.`
|
|
167
|
-
: detail.reason === '
|
|
168
|
-
? `sobra ${what}. Es
|
|
169
|
-
: detail.reason === '
|
|
170
|
-
? `sobra ${what}. Es
|
|
171
|
-
:
|
|
198
|
+
: detail.reason === 'invalid-condition'
|
|
199
|
+
? `sobra ${what}. Es una tupla del DESTINO con una condición que el paquete no escribe ni sabe leer (L11): presente e intocable —--prune ${verdict} la borra${detail.pruned === false ? '' : 'ría'}, y su clave no se reescribe desde el origen—. Revísala y bórrala a mano (SDK/CLI de OpenFGA) y repite la pasada.`
|
|
200
|
+
: detail.reason === 'deny-conditional'
|
|
201
|
+
? `sobra ${what}. Es un deny CONDICIONAL del DESTINO (con caducidad, escrito fuera del paquete) que el driver destino no puede rehacer permanente (openfga no reemplaza la misma clave en un Write, M5): niega solo mientras viva (si ya venció, no niega nada) y su partición queda NEGADA (línea «partición NEGADA»: cero escrituras en ella).`
|
|
202
|
+
: detail.reason === 'undeclared'
|
|
203
|
+
? `sobra ${what}. Es una fila del DESTINO cuya relación (o tipo) el destino YA NO declara (F-05): --prune ${verdict} la borra${detail.pruned === false ? '' : 'ría'} (unrelate la rechaza por F-05 y la pasada no insiste: nunca un DELETE de plataforma). Sigue ahí: retírala a mano (SQL/SDK) o vuelve a declarar la relación en el destino (y re-guarda su config persistida) y repite la pasada.`
|
|
204
|
+
: `sobra ${what}. Es un dato del DESTINO fuera de la gramática: --prune ${verdict} la borra${detail.pruned === false ? '' : 'ría'} (ningún driver del paquete la admite en unrelate, R-16, y la pasada no se la pide a nadie: nunca un DELETE de plataforma). Sigue ahí: corrígela o bórrala a mano (SQL/SDK) y repite la pasada.`,
|
|
172
205
|
});
|
|
173
206
|
}
|
|
174
207
|
// Cierre 5 de D2 (decisión del dueño §2026-10-16 (10), opción A): la PARTICIÓN NEGADA — ≥ 1 `renewal-unsafe` o
|
|
@@ -182,7 +215,7 @@ export function relationsReconcileLines(report) {
|
|
|
182
215
|
const later = evolves.length > 0 ? ` OJO, ese estado EVOLUCIONA: ${evolves.slice(0, 5).join('; ')}${evolves.length > 5 ? '; …' : ''}.` : '';
|
|
183
216
|
lines.push({
|
|
184
217
|
level: 'error',
|
|
185
|
-
message: `partición NEGADA ${refusal.partition}: ${causes.length} causa(s) que
|
|
218
|
+
message: `partición NEGADA ${refusal.partition}: ${causes.length} causa(s) por las que la pasada no puede escribir en ella sin conceder de más (una ventana entre dos Write, una tupla que el paquete no sabe leer, o una cadena de grupos anidados más larga que el techo del destino) — ` +
|
|
186
219
|
`${causes.slice(0, 5).join('; ')}${causes.length > 5 ? '; …' : ''}. Cero escrituras y cero borrados en esta partición ` +
|
|
187
220
|
`(también en la pasada real${report.prune === true ? ' y con --prune' : ''}): su destino queda COMO ESTABA, con la deriva ` +
|
|
188
221
|
`previa a la vista (por escribir ${refusal.pending.write}, por renovar ${refusal.pending.update}, por borrar ` +
|
|
@@ -287,6 +320,8 @@ export function relationsReconcileLines(report) {
|
|
|
287
320
|
pendingRenewals === 0 &&
|
|
288
321
|
(report.partitionsRefused?.length ?? 0) === 0 &&
|
|
289
322
|
(report.denyRefused?.length ?? 0) === 0 &&
|
|
323
|
+
(report.denyReach?.findings.length ?? 0) === 0 &&
|
|
324
|
+
(report.denyReach?.stale.length ?? 0) === 0 &&
|
|
290
325
|
!(report.massDelete && !massDeleteLifted) &&
|
|
291
326
|
!(report.frozen?.lapsed === true) &&
|
|
292
327
|
(!report.dryRun || cambios === 0);
|
|
@@ -461,6 +496,19 @@ export default class AuthzRelationsReconcile extends BaseCommand {
|
|
|
461
496
|
else {
|
|
462
497
|
this.logger.info(`Config del destino: leída de authz_relations_config (${toConfig.objectTypes.length} tipo(s)): la deriva del modelo se vigila.`);
|
|
463
498
|
}
|
|
499
|
+
// Lote RC · sublote (8): el DETECTOR del alcance de los denies, solo en el verificador (`--dry-run`): línea base = la
|
|
500
|
+
// config persistida (`toConfig`), config nueva = la que SIRVE este proceso (`relations.config`). Sin `relations.config`
|
|
501
|
+
// en el proceso no hay con qué comparar: se dice (punto 7) y la pasada sigue.
|
|
502
|
+
let denyReach;
|
|
503
|
+
if (this.dryRun === true) {
|
|
504
|
+
const current = this.app.config?.get('authorization')?.relations?.config;
|
|
505
|
+
if (current && Array.isArray(current.objectTypes)) {
|
|
506
|
+
denyReach = { baseline: toConfig, current };
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
this.logger.info('alcance de denies: NO se vigila en esta pasada (este proceso no tiene relations.config en config/authorization.ts con el que comparar).');
|
|
510
|
+
}
|
|
511
|
+
}
|
|
464
512
|
// L-1 · J1: la pasada que ESCRIBE corre bajo la ventana durable del
|
|
465
513
|
// manager de roles (la misma fila `id = 2`, toda la flota); `--dry-run` no.
|
|
466
514
|
// Los drivers los construye `runRelationsReconcile`, DESPUÉS del guard.
|
|
@@ -475,6 +523,7 @@ export default class AuthzRelationsReconcile extends BaseCommand {
|
|
|
475
523
|
toKey: this.to,
|
|
476
524
|
fromKey,
|
|
477
525
|
...(toConfig ? { toConfig } : {}),
|
|
526
|
+
...(denyReach ? { denyReach } : {}),
|
|
478
527
|
});
|
|
479
528
|
}
|
|
480
529
|
catch (error) {
|
|
@@ -583,4 +632,3 @@ __decorate([
|
|
|
583
632
|
flags.string({ description: 'Partition (tenant) scope uuid; required with --partition-type' }),
|
|
584
633
|
__metadata("design:type", Object)
|
|
585
634
|
], AuthzRelationsReconcile.prototype, "partitionUuid", void 0);
|
|
586
|
-
//# sourceMappingURL=authz_relations_reconcile.js.map
|
|
@@ -252,8 +252,23 @@ export default class AuthzScopesRelay extends BaseCommand {
|
|
|
252
252
|
// Qué fue de cada entrada, una a una (`entries`): la pasada no es atómica
|
|
253
253
|
// y un contador no permite retomar nada. Lo aparcado ya se dijo arriba.
|
|
254
254
|
for (const entry of report.entries) {
|
|
255
|
-
if (entry.status === 'applied')
|
|
256
|
-
|
|
255
|
+
if (entry.status === 'applied') {
|
|
256
|
+
// Asentar · cierre 1 (auditor ⚪ 8): la auditoría dice el padre APLICADO, no solo el encolado.
|
|
257
|
+
if (entry.reason === 'superseded') {
|
|
258
|
+
this.logger.warning(`aplicado SIN arista (superseded: el nodo ya no existe y su detached está en la cola): ${linea(entry)}`);
|
|
259
|
+
}
|
|
260
|
+
else if (entry.reason === 'consumer-parent' && entry.appliedParent) {
|
|
261
|
+
// Asentar · cierre 2 (C1-1 (b), firma del dueño 2026-10-21; O-1 del re-ataque): exit ≠ 0. La carrera S1 y una
|
|
262
|
+
// caché rancia con dos movimientos del nodo dentro de su TTL son INDISTINGUIBLES para el relay, y un cron no lee
|
|
263
|
+
// avisos: la carrera hace saltar la alarma una vez; la caché, cada vez que pase.
|
|
264
|
+
this.logger.warning(`aplicado con el padre del CONSUMIDOR ${entry.appliedParent.type}:${entry.appliedParent.uuid ?? ''} ` +
|
|
265
|
+
`(no el encolado; otra entrada del nodo lo respalda): ${linea(entry)} — sale ≠ 0: comprueba que el resolutor ` +
|
|
266
|
+
'del relay lee lo CONFIRMADO, sin caché ni réplica atrasada (authz:reconcile --dry-run lo contrasta)');
|
|
267
|
+
this.exitCode = 1;
|
|
268
|
+
}
|
|
269
|
+
else
|
|
270
|
+
this.logger.log(`aplicado: ${linea(entry)}`);
|
|
271
|
+
}
|
|
257
272
|
if (entry.status === 'deferred') {
|
|
258
273
|
const motivo = entry.reason?.startsWith('parked:')
|
|
259
274
|
? `aplazado por APARCADO (${entry.reason}): no se aplica hasta que alguien mire esa entrada`
|
|
@@ -267,8 +282,38 @@ export default class AuthzScopesRelay extends BaseCommand {
|
|
|
267
282
|
notEvaluatedLines();
|
|
268
283
|
if (report.failures.length > 0) {
|
|
269
284
|
for (const item of report.failures) {
|
|
270
|
-
|
|
271
|
-
|
|
285
|
+
const target = `#${item.id} (${item.change.op} ${item.change.child.type}:${item.change.child.uuid ?? ''})`;
|
|
286
|
+
// Cierre-2 de L11 (⚪ C2; punto 7): el fallo de DESPLIEGUE tiene su prefijo ESTABLE —el backend SÍ respondió, lo
|
|
287
|
+
// que no cuadra es el despliegue con la config—; «el backend no respondió» queda para el 503 de verdad. Desde R3
|
|
288
|
+
// la marca la ponen también el store/modelo inexistente, las credenciales (401/403) y el 4xx desconocido.
|
|
289
|
+
if (item.deployment === true) {
|
|
290
|
+
this.logger.error(`fallo de DESPLIEGUE ${target}: ${item.error} — el despliegue no es el que espera la config (modelo fijado, ` +
|
|
291
|
+
'store, credenciales o un 4xx del servidor): este fallo NO consume intento (corrige el despliegue —' +
|
|
292
|
+
'openfga:provision publica el modelo de la config— y repite)');
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
// Cierre de R3 (⚪ R3-A7; punto 7): la carrera y el límite de resolución son transitorios, pero el backend SÍ
|
|
296
|
+
// respondió — cada uno con su prefijo ESTABLE.
|
|
297
|
+
if (item.transient && item.code === 'E_AUTHZ_WRITE_CONFLICT') {
|
|
298
|
+
this.logger.error(`CARRERA en ${target}: ${item.error} — otro escritor (p. ej. un grant concurrente) tocó las mismas tuplas: ` +
|
|
299
|
+
'este fallo NO consume intento (la pasada siguiente relee y re-aplica)');
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (item.transient && item.code === 'E_AUTHZ_RESOLUTION_LIMIT') {
|
|
303
|
+
this.logger.error(`límite de RESOLUCIÓN en ${target}: ${item.error} — el backend respondió que el grafo excede lo que ` +
|
|
304
|
+
'resuelve (ver «Limits» en el README): este fallo NO consume intento');
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
// Lote «antes de RC» · F4 (⚪ RC-2 del auditor de R3; punto 7): un deadline vencido —el timeout del cliente, o un
|
|
308
|
+
// 504 / `deadline_exceeded` de una ESCRITURA, que es indeterminada desde el cierre de R3— no es «el backend no
|
|
309
|
+
// respondió»: con un 504 SÍ respondió (o un proxy), y la escritura pudo aplicarse. Prefijo ESTABLE propio.
|
|
310
|
+
if (item.transient && item.code === 'E_AUTHZ_BACKEND_TIMEOUT') {
|
|
311
|
+
this.logger.error(`PLAZO VENCIDO en ${target}: ${item.error} — venció el deadline (timeout del cliente, o un 504/deadline_exceeded ` +
|
|
312
|
+
'del servidor o de un proxy): si era una escritura pudo aplicarse o no (INDETERMINADA); la pasada siguiente ' +
|
|
313
|
+
'relee el store y re-aplica: este fallo NO consume intento');
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
this.logger.error(`falló ${target}: ${item.error}` +
|
|
272
317
|
(item.transient ? ' — el backend no respondió: este fallo NO consume intento' : ''));
|
|
273
318
|
}
|
|
274
319
|
this.logger.warning('Lo que DEPENDE de un cambio fallido no se adelanta (el orden del árbol importa: aparece como «aplazado»), ' +
|
|
@@ -341,4 +386,3 @@ __decorate([
|
|
|
341
386
|
}),
|
|
342
387
|
__metadata("design:type", Object)
|
|
343
388
|
], AuthzScopesRelay.prototype, "maxAge", void 0);
|
|
344
|
-
//# sourceMappingURL=authz_scopes_relay.js.map
|
|
@@ -90,7 +90,20 @@ export function requeueLines(input) {
|
|
|
90
90
|
const relay = report.relay;
|
|
91
91
|
let clean = true;
|
|
92
92
|
for (const entry of relay.entries) {
|
|
93
|
-
|
|
93
|
+
// Asentar · cierre 2 (gemelo del relay, C1-1): el padre APLICADO cuando no es el encolado, con su línea propia. No
|
|
94
|
+
// cambia `clean`: aquí hay un operador delante, y es justo la salida de una aparcada por DRIFT (re-notificar el
|
|
95
|
+
// padre actual y reencolar) la que termina en esta línea.
|
|
96
|
+
if (entry.status === 'applied' && entry.reason === 'consumer-parent' && entry.appliedParent) {
|
|
97
|
+
lines.push({
|
|
98
|
+
level: 'warning',
|
|
99
|
+
message: `aplicada con el padre del CONSUMIDOR ${entry.appliedParent.type}:${entry.appliedParent.uuid ?? ''} ` +
|
|
100
|
+
`(no el encolado; otra entrada del nodo lo respalda): ${linea(entry)}`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else if (entry.status === 'applied' && entry.reason === 'superseded') {
|
|
104
|
+
lines.push({ level: 'warning', message: `aplicada SIN arista (superseded: el nodo ya no existe y su detached está en la cola): ${linea(entry)}` });
|
|
105
|
+
}
|
|
106
|
+
else if (entry.status === 'applied')
|
|
94
107
|
lines.push({ level: 'log', message: `aplicada: ${linea(entry)}` });
|
|
95
108
|
else if (entry.status === 'deferred') {
|
|
96
109
|
clean = false;
|
|
@@ -227,4 +240,3 @@ __decorate([
|
|
|
227
240
|
}),
|
|
228
241
|
__metadata("design:type", Object)
|
|
229
242
|
], AuthzScopesRequeue.prototype, "waitLeaseMs", void 0);
|
|
230
|
-
//# sourceMappingURL=authz_scopes_requeue.js.map
|
package/build/commands/main.d.ts
CHANGED
|
@@ -14,4 +14,3 @@ export { OpenFgaProvision, AuthzCatalogDiff, AuthzCatalogSync, AuthzCatalogPrune
|
|
|
14
14
|
export declare function getMetaData(): Promise<CommandMetaData[]>;
|
|
15
15
|
/** Contrato de loader de ace (`LoadersContract.getCommand`): la clase, o `null` si el nombre no es nuestro. */
|
|
16
16
|
export declare function getCommand(metaData: CommandMetaData): Promise<any>;
|
|
17
|
-
//# sourceMappingURL=main.d.ts.map
|
package/build/commands/main.js
CHANGED
|
@@ -83,6 +83,16 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
83
83
|
*/
|
|
84
84
|
prepare(): Promise<void>;
|
|
85
85
|
run(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* **La RELECTURA de lo publicado** (lote «antes de RC» · L11, diseño 1.3 D del analista; condición de aceptación C12 del
|
|
88
|
+
* tester): tras `writeAuthorizationModel` se LEE el modelo por su id y se compara ENTERO (`slice: 'all'`) con el que
|
|
89
|
+
* genera la config. Ancla el canonicalizador de L11 contra el servidor REAL en cada publicación: un servidor que
|
|
90
|
+
* serialice distinto (otra versión, un campo nuevo) se descubre en el despliegue, no con un 500 en la primera petición
|
|
91
|
+
* de producción. Si la relectura falla o difiere: exit 1, prefijo ESTABLE «provision RELECTURA FALLIDA:» (punto 7) y la
|
|
92
|
+
* letra que no miente (H11 c): el modelo YA está publicado en el store, y NO hay que fijarlo — por eso la línea
|
|
93
|
+
* `OPENFGA_MODEL_ID=` NO se imprime. Devuelve si se puede fijar.
|
|
94
|
+
*/
|
|
95
|
+
private reread;
|
|
86
96
|
/**
|
|
87
97
|
* **La guarda contra la MUTILACIÓN del modelo** (plan 2.5 · lote D · D2; analista §4.6, juez ronda 2 · R2-4 c y H3;
|
|
88
98
|
* cierre 1 de D2 · 🟡 3 y 🟠 2). `openfga:provision --store-id` publica desde la config del PROCESO: si ese proceso ya
|
|
@@ -174,4 +184,3 @@ export declare function countLiveDenies(client: any, dropped: string[]): Promise
|
|
|
174
184
|
count: number;
|
|
175
185
|
read: number;
|
|
176
186
|
}>;
|
|
177
|
-
//# sourceMappingURL=openfga_provision.d.ts.map
|
|
@@ -105,7 +105,7 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
105
105
|
}
|
|
106
106
|
const { OpenFgaClient } = await import('@openfga/sdk');
|
|
107
107
|
// El subpath `/openfga` es la única puerta al SDK (D9).
|
|
108
|
-
const { openFgaFactsModel, provisionOpenFgaStore, assertFactsModelPublishable } = await import('../src/openfga.js');
|
|
108
|
+
const { openFgaFactsModel, provisionOpenFgaStore, assertFactsModelPublishable, factsModelDifferences } = await import('../src/openfga.js');
|
|
109
109
|
// Los holders del consumidor viven en SU config, no en el paquete.
|
|
110
110
|
const holderTypes = config?.holderTypes ?? {};
|
|
111
111
|
// Los PERMISOS del modelo (c2r) salen de los catálogos del config. Se
|
|
@@ -130,7 +130,8 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
130
130
|
// esto salía como error crudo del SDK con una request ya hecha (⚪ N1
|
|
131
131
|
// del auditor de alpha.4). Cero requests si no es publicable.
|
|
132
132
|
assertFactsModelPublishable(holderTypes, permissions, undefined, relations);
|
|
133
|
-
|
|
133
|
+
// Injerto 15 del juez (H12): sin reintentos a escondidas (desde el SDK 0.9.6 un cliente sin `retryParams` reintenta).
|
|
134
|
+
const client = new OpenFgaClient({ apiUrl, storeId: this.storeId, retryParams: { maxRetry: 0 } });
|
|
134
135
|
const next = openFgaFactsModel(holderTypes, permissions, relations);
|
|
135
136
|
// Lote D · D2: la guarda contra la MUTILACIÓN del modelo (R2-4 c / H3) — ANTES de escribir nada. Cierre 1 de D2:
|
|
136
137
|
// juzga el ESTADO del store (🟡 3) y devuelve las relaciones que hay que RE-CONTAR justo antes de publicar (🟠 2).
|
|
@@ -147,6 +148,8 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
147
148
|
if (relations)
|
|
148
149
|
this.logger.info(`Tipos de relación fusionados: ${relations.objectTypes.map((t) => t.type).join(', ')}`);
|
|
149
150
|
this.logger.log(`OPENFGA_STORE_ID=${this.storeId}`);
|
|
151
|
+
if (!(await this.reread(OpenFgaClient, factsModelDifferences, apiUrl, this.storeId, model.authorization_model_id, holderTypes, relations)))
|
|
152
|
+
return;
|
|
150
153
|
this.logger.log(`OPENFGA_MODEL_ID=${model.authorization_model_id}`);
|
|
151
154
|
return;
|
|
152
155
|
}
|
|
@@ -158,8 +161,46 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
158
161
|
this.logger.log('Añade al .env (junto a AUTHZ_DRIVER=openfga):');
|
|
159
162
|
this.logger.log(`OPENFGA_URL=${apiUrl}`);
|
|
160
163
|
this.logger.log(`OPENFGA_STORE_ID=${storeId}`);
|
|
164
|
+
if (!(await this.reread(OpenFgaClient, factsModelDifferences, apiUrl, storeId, modelId, holderTypes, relations)))
|
|
165
|
+
return;
|
|
161
166
|
this.logger.log(`OPENFGA_MODEL_ID=${modelId}`);
|
|
162
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* **La RELECTURA de lo publicado** (lote «antes de RC» · L11, diseño 1.3 D del analista; condición de aceptación C12 del
|
|
170
|
+
* tester): tras `writeAuthorizationModel` se LEE el modelo por su id y se compara ENTERO (`slice: 'all'`) con el que
|
|
171
|
+
* genera la config. Ancla el canonicalizador de L11 contra el servidor REAL en cada publicación: un servidor que
|
|
172
|
+
* serialice distinto (otra versión, un campo nuevo) se descubre en el despliegue, no con un 500 en la primera petición
|
|
173
|
+
* de producción. Si la relectura falla o difiere: exit 1, prefijo ESTABLE «provision RELECTURA FALLIDA:» (punto 7) y la
|
|
174
|
+
* letra que no miente (H11 c): el modelo YA está publicado en el store, y NO hay que fijarlo — por eso la línea
|
|
175
|
+
* `OPENFGA_MODEL_ID=` NO se imprime. Devuelve si se puede fijar.
|
|
176
|
+
*/
|
|
177
|
+
async reread(OpenFgaClient, factsModelDifferences, apiUrl, storeId, modelId, holderTypes, relations) {
|
|
178
|
+
let differences;
|
|
179
|
+
try {
|
|
180
|
+
const client = new OpenFgaClient({ apiUrl, storeId, authorizationModelId: modelId, retryParams: { maxRetry: 0 } });
|
|
181
|
+
const response = await client.readAuthorizationModel();
|
|
182
|
+
differences = factsModelDifferences(response?.authorization_model, { holderTypes, relations, slice: 'all' }).differences.map((d) => d.path);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
this.logger.error(`provision RELECTURA FALLIDA: el modelo ${modelId} SE PUBLICÓ en el store ${storeId}, pero no se pudo releer y ` +
|
|
186
|
+
`comparar con el que genera la config (${String(error?.message ?? error)}). NO lo fijes (OPENFGA_MODEL_ID): los ` +
|
|
187
|
+
'drivers lo comparan al arrancar y, sin esta comprobación, podrían negarse a servir. Repite el comando cuando el ' +
|
|
188
|
+
'servidor responda.');
|
|
189
|
+
this.exitCode = 1;
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (differences.length === 0) {
|
|
193
|
+
// Punto 7 de CLAUDE.md (cierre de L11, 🟡-1 del tester): la rama de éxito también dice que corrió, con prefijo ESTABLE.
|
|
194
|
+
this.logger.success(`provision RELECTURA OK: el modelo ${modelId} releído del store ${storeId} es IGUAL (entero) al que genera la config; ya se puede fijar.`);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
this.logger.error(`provision RELECTURA FALLIDA: el modelo ${modelId} SE PUBLICÓ en el store ${storeId}, pero lo que el servidor devuelve ` +
|
|
198
|
+
`NO es igual al que genera la config (L11): ${differences.slice(0, 20).join('; ')}${differences.length > 20 ? ' …' : ''}. ` +
|
|
199
|
+
'NO lo fijes (OPENFGA_MODEL_ID): los drivers openfga lo compararían igual al arrancar y responderían 500 E_AUTHZ_CONFIG. ' +
|
|
200
|
+
'Suele ser un servidor de otra versión que serializa el modelo distinto: compruébalo (CI prueba OpenFGA v1.19.0) y repórtalo.');
|
|
201
|
+
this.exitCode = 1;
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
163
204
|
/**
|
|
164
205
|
* **La guarda contra la MUTILACIÓN del modelo** (plan 2.5 · lote D · D2; analista §4.6, juez ronda 2 · R2-4 c y H3;
|
|
165
206
|
* cierre 1 de D2 · 🟡 3 y 🟠 2). `openfga:provision --store-id` publica desde la config del PROCESO: si ese proceso ya
|
|
@@ -411,4 +452,3 @@ export async function countLiveDenies(client, dropped) {
|
|
|
411
452
|
} while (continuationToken);
|
|
412
453
|
return { live, count, read };
|
|
413
454
|
}
|
|
414
|
-
//# sourceMappingURL=openfga_provision.js.map
|
package/build/configure.d.ts
CHANGED
package/build/configure.js
CHANGED
package/build/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export type { SqlScopeOutboxOptions } from './src/scope_outbox.js';
|
|
|
50
50
|
* obligaría a importar ese SDK — acoplando el call-site al backend que este
|
|
51
51
|
* paquete existe para abstraer.
|
|
52
52
|
*/
|
|
53
|
-
export { AuthorizationBackendError, AuthorizationBackendTimeoutError, ScopeResolverError, InvalidIdentityError, InvalidSlugError, AuthorizationConfigError, AuthorizationInternalError, UnknownScopeError, NoScopeResolverError, UnknownRoleError, UnknownPermissionError, CatalogConflictError, RoleIsNotAccessError, ScopeCycleError, PurgeIncompleteError, WriteConflictError, RenewalUnsafeError, RenewalRacedError, ActorRequiredError, CheckManyBatchError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, ScopeDepthLimitError, ScopeRequeueRefusedError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationConfigUnknownKeyError, RelationTypeUnknownError, RelationUnknownError, RelationUsersetUnsupportedError, RelationNotDenyableError, RelationDenyExpiryError, MembersOfDenyLimitError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, DenyMigrationRefusedError, StaleSourceRefusedError, OperatorWindowExpiredError, ReconcileTooLargeError, markPartialWrite, isPartialWrite, } from './src/errors.js';
|
|
53
|
+
export { AuthorizationBackendError, AuthorizationBackendTimeoutError, ResolutionLimitError, ScopeResolverError, InvalidIdentityError, InvalidSlugError, AuthorizationConfigError, AuthorizationInternalError, UnknownScopeError, NoScopeResolverError, UnknownRoleError, UnknownPermissionError, CatalogConflictError, RoleIsNotAccessError, ScopeCycleError, PurgeIncompleteError, WriteConflictError, RenewalUnsafeError, RenewalRacedError, ActorRequiredError, CheckManyBatchError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, ScopeDepthLimitError, GroupDepthLimitError, ScopeRequeueRefusedError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationConfigUnknownKeyError, RelationTypeUnknownError, RelationUnknownError, RelationUsersetUnsupportedError, RelationNotDenyableError, RelationDenyExpiryError, MembersOfDenyLimitError, InvalidRelationPageError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, DenyMigrationRefusedError, DenyReachRefusedError, StaleSourceRefusedError, OperatorWindowExpiredError, ReconcileTooLargeError, markPartialWrite, isPartialWrite, } from './src/errors.js';
|
|
54
54
|
/**
|
|
55
55
|
* Validación de identidad y de slugs (422). La aplica el manager en cada
|
|
56
56
|
* llamada y los drivers por defensa en profundidad; se exporta para que un
|
|
@@ -72,9 +72,12 @@ export type { RelationsManagerOptions } from './src/relations/manager.js';
|
|
|
72
72
|
export { relationTupleKey, relationSubjectKey } from './src/relations/tuple_key.js';
|
|
73
73
|
export type { RelationTupleKind, RelationTupleIdentity } from './src/relations/tuple_key.js';
|
|
74
74
|
export { RELATIONS_CHECK_MANY_MAX } from './src/relations/check_many.js';
|
|
75
|
-
export
|
|
76
|
-
export {
|
|
77
|
-
export type {
|
|
75
|
+
export { RELATIONS_ENUMERATE_DEFAULT_LIMIT, RELATIONS_ENUMERATE_MAX_LIMIT } from './src/relations/page.js';
|
|
76
|
+
export { RELATIONS_GROUP_DEPTH_MEASURED, GROUP_DEPTH_CONFIG_MAX, GROUP_DEPTH_MAX_NODES, GROUP_DEPTH_MAX_READS, GROUP_DEPTH_MAX_TUPLES, GROUP_DEPTH_DEADLINE_MS, GROUP_DEPTH_CALIBRATION_BACKOFF_MS, GROUP_DEPTH_RECALIBRATION_MIN_INTERVAL_MS, } from './src/relations/group_depth.js';
|
|
77
|
+
export type { RelationsReconcileReport, RelationsReconcileDetail, RelationsPartitionRefusal, RelationsPartitionRefusalReason } from './src/relations/reconcile.js';
|
|
78
|
+
export { saveRelationsConfig, readRelationsConfig, readRelationsConfigSpec, checkRelationsConfigDenyReach, } from './src/relations_config_store.js';
|
|
79
|
+
export type { RelationsConfigStoreOptions, SaveRelationsConfigOptions, RelationsConfigDenyReachCheck } from './src/relations_config_store.js';
|
|
80
|
+
export type { DenyReachFinding, DenyReachReason } from './src/relations/deny_reach.js';
|
|
78
81
|
export { DatabaseRelationsDriver, RELATIONS_MEMBERS_DENY_CHECK_MAX } from './src/drivers/database_relations_driver.js';
|
|
79
82
|
export type { DatabaseRelationsDriverOptions } from './src/drivers/database_relations_driver.js';
|
|
80
83
|
export { resolveGrantExpiry, isActiveExpiry, sameInstant } from './src/expiry.js';
|
|
@@ -106,4 +109,3 @@ export { default as AuthzAssignment } from './src/models/authz_assignment.js';
|
|
|
106
109
|
export { default as AuthzDeny } from './src/models/authz_deny.js';
|
|
107
110
|
export { withAuthzScopes } from './src/traits/authz_scopes.js';
|
|
108
111
|
export { hasUuid } from './src/traits/has_uuid.js';
|
|
109
|
-
//# sourceMappingURL=index.d.ts.map
|
package/build/index.js
CHANGED
|
@@ -43,7 +43,7 @@ export { sqlScopeOutbox } from './src/scope_outbox.js';
|
|
|
43
43
|
* obligaría a importar ese SDK — acoplando el call-site al backend que este
|
|
44
44
|
* paquete existe para abstraer.
|
|
45
45
|
*/
|
|
46
|
-
export { AuthorizationBackendError, AuthorizationBackendTimeoutError, ScopeResolverError, InvalidIdentityError, InvalidSlugError, AuthorizationConfigError, AuthorizationInternalError, UnknownScopeError, NoScopeResolverError, UnknownRoleError, UnknownPermissionError, CatalogConflictError, RoleIsNotAccessError, ScopeCycleError, PurgeIncompleteError, WriteConflictError, RenewalUnsafeError, RenewalRacedError, ActorRequiredError, CheckManyBatchError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, ScopeDepthLimitError, ScopeRequeueRefusedError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationConfigUnknownKeyError, RelationTypeUnknownError, RelationUnknownError, RelationUsersetUnsupportedError, RelationNotDenyableError, RelationDenyExpiryError, MembersOfDenyLimitError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, DenyMigrationRefusedError, StaleSourceRefusedError, OperatorWindowExpiredError, ReconcileTooLargeError,
|
|
46
|
+
export { AuthorizationBackendError, AuthorizationBackendTimeoutError, ResolutionLimitError, ScopeResolverError, InvalidIdentityError, InvalidSlugError, AuthorizationConfigError, AuthorizationInternalError, UnknownScopeError, NoScopeResolverError, UnknownRoleError, UnknownPermissionError, CatalogConflictError, RoleIsNotAccessError, ScopeCycleError, PurgeIncompleteError, WriteConflictError, RenewalUnsafeError, RenewalRacedError, ActorRequiredError, CheckManyBatchError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, ScopeDepthLimitError, GroupDepthLimitError, ScopeRequeueRefusedError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationConfigUnknownKeyError, RelationTypeUnknownError, RelationUnknownError, RelationUsersetUnsupportedError, RelationNotDenyableError, RelationDenyExpiryError, MembersOfDenyLimitError, InvalidRelationPageError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, DenyMigrationRefusedError, DenyReachRefusedError, StaleSourceRefusedError, OperatorWindowExpiredError, ReconcileTooLargeError,
|
|
47
47
|
// Cierre de alpha.3 (invariante 13 en purgas multi-request): un driver de
|
|
48
48
|
// relaciones de terceros marca con `markPartialWrite` el fallo que llega
|
|
49
49
|
// DESPUÉS de haber borrado parte; el manager notifica `indeterminate: true`.
|
|
@@ -66,10 +66,18 @@ export { RelationsManager } from './src/relations/manager.js';
|
|
|
66
66
|
// Plan 2.5 · lote A: la clave PURA de una tupla (P3, con `kind` desde el primer día) y el tope de `checkMany` (P4).
|
|
67
67
|
export { relationTupleKey, relationSubjectKey } from './src/relations/tuple_key.js';
|
|
68
68
|
export { RELATIONS_CHECK_MANY_MAX } from './src/relations/check_many.js';
|
|
69
|
+
// Lote «antes de RC» · 7.4: la página de `enumerate*` (default y techo; por encima se recorta con cursor).
|
|
70
|
+
export { RELATIONS_ENUMERATE_DEFAULT_LIMIT, RELATIONS_ENUMERATE_MAX_LIMIT } from './src/relations/page.js';
|
|
71
|
+
// Lote «antes de RC» · R1: las cotas de la anidación de grupos (la fila de «Limits»); la regla vive en los drivers.
|
|
72
|
+
export { RELATIONS_GROUP_DEPTH_MEASURED, GROUP_DEPTH_CONFIG_MAX, GROUP_DEPTH_MAX_NODES,
|
|
73
|
+
// Cierre de R1 (🟠 1 del auditor): el presupuesto de la comprobación de una arista en openfga.
|
|
74
|
+
GROUP_DEPTH_MAX_READS, GROUP_DEPTH_MAX_TUPLES, GROUP_DEPTH_DEADLINE_MS,
|
|
75
|
+
// Cierre-2 de R1 (D5): la espera tras una calibración fallida y la cota de la re-calibración tras un 503 de profundidad.
|
|
76
|
+
GROUP_DEPTH_CALIBRATION_BACKOFF_MS, GROUP_DEPTH_RECALIBRATION_MIN_INTERVAL_MS, } from './src/relations/group_depth.js';
|
|
69
77
|
// La config de relaciones PERSISTIDA (🟡3): API de PLATAFORMA (bajo el gate de
|
|
70
78
|
// versión, invariante 14). La CONSUME `authz:relations:reconcile` (config del
|
|
71
79
|
// destino, para la deriva del modelo fusionado en --dry-run).
|
|
72
|
-
export { saveRelationsConfig, readRelationsConfig, readRelationsConfigSpec, } from './src/relations_config_store.js';
|
|
80
|
+
export { saveRelationsConfig, readRelationsConfig, readRelationsConfigSpec, checkRelationsConfigDenyReach, } from './src/relations_config_store.js';
|
|
73
81
|
// El driver `database` de relaciones (el consumidor lo cablea en
|
|
74
82
|
// `relations.drivers` de config/authorization.ts). El driver `openfga` de
|
|
75
83
|
// relaciones NO sale de aquí: vive en el subpath `/openfga` (peer opcional).
|
|
@@ -100,4 +108,3 @@ export { default as AuthzAssignment } from './src/models/authz_assignment.js';
|
|
|
100
108
|
export { default as AuthzDeny } from './src/models/authz_deny.js';
|
|
101
109
|
export { withAuthzScopes } from './src/traits/authz_scopes.js';
|
|
102
110
|
export { hasUuid } from './src/traits/has_uuid.js';
|
|
103
|
-
//# sourceMappingURL=index.js.map
|
package/build/services/main.d.ts
CHANGED
package/build/services/main.js
CHANGED
|
@@ -286,4 +286,3 @@ export declare function runCatalogDiff(catalogs: CatalogSource[], options?: {
|
|
|
286
286
|
export declare function syncCatalogs(catalogs: CatalogSource[], options?: SyncCatalogOptions): Promise<{
|
|
287
287
|
count: number;
|
|
288
288
|
} & CatalogSyncReport>;
|
|
289
|
-
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -304,6 +304,19 @@ export declare class CatalogCache {
|
|
|
304
304
|
* vuelve a intentarlo.
|
|
305
305
|
*/
|
|
306
306
|
view(): Promise<CatalogView>;
|
|
307
|
+
/**
|
|
308
|
+
* **La foto para una ESCRITURA** (lote C · carreras, resolución 2 del juez = K4): contrasta la versión con la fila
|
|
309
|
+
* SIEMPRE, ignore o no la ventana `{ everyMs }`. Con `'always'` es exactamente `view()`. La ventana es para DECIDIR
|
|
310
|
+
* (invariante 14: una pregunta puede responder con un catálogo de hasta `everyMs` de antigüedad); una escritura del driver
|
|
311
|
+
* deja ESTADO en el store que no caduca —el barrido de `moved`, el settle, la proyección—, y con un memo rancio un rol
|
|
312
|
+
* recién definido en otro proceso no se barre (fail-open persistente medido, K4). Coste: el SELECT por PK de `'always'`.
|
|
313
|
+
*
|
|
314
|
+
* **Nunca se suma a una comprobación ni a una carga EN VUELO** (asentar · cierre 2, ⚪ 6 / O-6 del auditor): la que ya
|
|
315
|
+
* estaba en marcha pudo leer la fila ANTES de la escritura que precede a esta llamada (la propia, o la de otro proceso
|
|
316
|
+
* que la escritura acaba de observar) y serviría esa foto. Se espera la carga en vuelo y se lee la versión otra vez;
|
|
317
|
+
* una carga que arranca DESPUÉS de esta llamada sí se comparte (ya es posterior).
|
|
318
|
+
*/
|
|
319
|
+
freshView(): Promise<CatalogView>;
|
|
307
320
|
/**
|
|
308
321
|
* Olvida la foto de ESTE memo. La siguiente pregunta recarga. Es un bump
|
|
309
322
|
* de generación, no un `#view = null`: una carga en vuelo capturó la
|
|
@@ -321,4 +334,3 @@ export declare class CatalogCache {
|
|
|
321
334
|
* disfrazado de dato.
|
|
322
335
|
*/
|
|
323
336
|
export declare function parseAssignableAt(slug: string, raw: unknown): readonly ScopeType[] | null;
|
|
324
|
-
//# sourceMappingURL=catalog_cache.d.ts.map
|