@jantstack/adonis-authz 2.4.0-alpha.2 → 2.4.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 +73 -3
- package/build/commands/main.d.ts +15 -13
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +57 -11
- package/build/commands/main.js.map +1 -1
- package/build/commands/openfga_provision.d.ts +49 -3
- package/build/commands/openfga_provision.d.ts.map +1 -1
- package/build/commands/openfga_provision.js +72 -7
- package/build/commands/openfga_provision.js.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5 -1
- package/build/index.js.map +1 -1
- package/build/providers/authz_provider.d.ts +6 -1
- package/build/providers/authz_provider.d.ts.map +1 -1
- package/build/providers/authz_provider.js +11 -2
- package/build/providers/authz_provider.js.map +1 -1
- package/build/src/define_config.d.ts +37 -1
- package/build/src/define_config.d.ts.map +1 -1
- package/build/src/define_config.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +10 -0
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -1
- package/build/src/drivers/database_relations_driver.js +28 -2
- package/build/src/drivers/database_relations_driver.js.map +1 -1
- package/build/src/drivers/openfga_driver.d.ts +4 -2
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +14 -4
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_relations_driver.d.ts +13 -0
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_relations_driver.js +106 -30
- package/build/src/drivers/openfga_relations_driver.js.map +1 -1
- package/build/src/errors.d.ts +48 -1
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +78 -0
- package/build/src/errors.js.map +1 -1
- package/build/src/manager.d.ts +30 -2
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +32 -1
- package/build/src/manager.js.map +1 -1
- package/build/src/relations/define_relations_config.d.ts +42 -8
- package/build/src/relations/define_relations_config.d.ts.map +1 -1
- package/build/src/relations/define_relations_config.js +51 -13
- package/build/src/relations/define_relations_config.js.map +1 -1
- package/build/src/relations/manager.d.ts +61 -6
- package/build/src/relations/manager.d.ts.map +1 -1
- package/build/src/relations/manager.js +244 -15
- package/build/src/relations/manager.js.map +1 -1
- package/build/src/sql_descendants.d.ts +8 -0
- package/build/src/sql_descendants.d.ts.map +1 -1
- package/build/src/sql_descendants.js.map +1 -1
- package/build/src/testing/relations_contract.d.ts.map +1 -1
- package/build/src/testing/relations_contract.js +174 -5
- package/build/src/testing/relations_contract.js.map +1 -1
- package/build/src/types.d.ts +117 -8
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +55 -0
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/authorization.stub +45 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@ node ace authz:catalog:sync
|
|
|
31
31
|
|
|
32
32
|
`configure` registers the provider, the commands and the `appAccess` middleware, defines the env variables, and **publishes into your project** what belongs to you: the migration for the `authz_*` tables and two config files (`config/authorization.ts` for drivers and the scope tree, `config/app_acl.ts` for your role catalog). The suite compiles both published configs against the package on every run, so they cannot drift from it.
|
|
33
33
|
|
|
34
|
+
> **Upgrading to 2.4.0-alpha.4 — how the commands are registered.** `@jantstack/adonis-authz/commands` is now a real ace loader (it exports `getMetaData()`/`getCommand()`, built with `ListLoader` over the nine command classes, next to the named class exports). Until alpha.4 it only re-exported the classes, so the line `configure` writes — `commands: [() => import('@jantstack/adonis-authz/commands')]` — made **the whole `node ace` crash** at boot (`TypeError: loaderInstance.getMetaData is not a function`). If you registered the commands by wrapping the barrel in a `ListLoader(Object.values(m))` (the only way it worked before alpha.4), remove the wrapper and register the module directly: `() => import('@jantstack/adonis-authz/commands')`, which is what `node ace configure` writes. The wrapper now fails with `TypeError: command.serialize is not a function`, because the module also exports the loader functions: `Object.values(m)` includes them and `ListLoader.getMetaData()` calls `serialize()` on each value. The module has no `default` export on purpose — ace calls `getMetaData()` on the module namespace and never unwraps `default` (same shape as `@adonisjs/core/commands`).
|
|
35
|
+
|
|
34
36
|
For the OpenFGA driver, also install its SDK (optional peer): `npm i @openfga/sdk`. Everything that touches the SDK lives behind the **`@jantstack/adonis-authz/openfga` subpath** (`OpenFgaAuthorizationDriver`, `provisionOpenFgaStore`, `importAuthzFactsToOpenFga`, `openFgaAuthorizationModel`, `assertHolderTypes`); the main entry never imports it, so a database-only install boots without the SDK (the suite loads `index.ts` in a child process with `@openfga/sdk` blocked to prove it: *"index.ts (la entrada principal) carga con el SDK bloqueado"*). The published config imports the driver from that subpath *inside* the factory.
|
|
35
37
|
|
|
36
38
|
## Semantics (what every driver guarantees)
|
|
@@ -565,6 +567,32 @@ platform escape hatch) and `authz:relations:reconcile` — which writes through
|
|
|
565
567
|
it back in red). `manager.driver()` still skips the manager's *other* barriers (`actor`,
|
|
566
568
|
`assertWrite`, `onRelationWrite`), as documented — F-05 is no longer one of them.
|
|
567
569
|
|
|
570
|
+
**And since `2.4.0-alpha.3` F-05 covers **all eight operations of the port**, not only the two writes that
|
|
571
|
+
compose a tuple.** The alpha.3 audit found the chokepoint one call-site short (pre-existing in alpha.2, in
|
|
572
|
+
code alpha.3 had just rewritten): `purgeObject`/`purgeSubject`/`check`/`listObjects`/`listSubjects`/`membersOf`
|
|
573
|
+
never called `assertRelationDeclared`, so in the shared store `purgeObject({ type: 'role_binding', id: R }, S)`
|
|
574
|
+
composed — byte for byte — the id of a real binding and **deleted the real binding** with proof of zero
|
|
575
|
+
(`authorize` went from `true` to `false`, zero tuples left, and a clean `purgeObject` audit event), and
|
|
576
|
+
`listSubjects('assignee', role_binding)` enumerated the holders assigned to a role in a scope through the
|
|
577
|
+
relations port, with no catalog, no `within`, no `requireActor` and no role visibility. Now the same
|
|
578
|
+
check runs first on the eight, in the manager **and** in both drivers (`manager.driver()` is not an
|
|
579
|
+
exit, same pattern as L-0 and L-5) — and it is **two functions, not an optional**: `assertRelationDeclared`
|
|
580
|
+
is **strict** (object type *and* relation, and the relation is mandatory) for `relate`/`unrelate`/`check`/
|
|
581
|
+
`listObjects`/`listSubjects`/`membersOf` and for the userset of any subject, while `assertObjectTypeDeclared`
|
|
582
|
+
(type only) serves `purgeObject`, which names no relation (`purgeSubject` validates the userset of its
|
|
583
|
+
subject). The first closing lot had made `relation` optional on one shared function, and the auditor's second
|
|
584
|
+
pass showed what that opened: `relate(u, undefined, doc, p)` — `request.input('relation')` missing — went past
|
|
585
|
+
F-05 (it wrote on the doubles, came back as a 503 from the OpenFGA server, and was saved only by the
|
|
586
|
+
`database` driver's own grammar), and `listSubjects(undefined, doc, p)` returned the **union** of every
|
|
587
|
+
relation's holders. So a missing `relation` (`undefined`) is 422 `E_AUTHZ_RELATION_UNKNOWN` on the six that
|
|
588
|
+
name one, on both drivers, with zero calls — 422 `E_AUTHZ_RELATION_TYPE_UNKNOWN` / `E_AUTHZ_RELATION_UNKNOWN`,
|
|
589
|
+
**zero client calls, zero events**, everywhere. The exploit is pinned against the shared store on the real
|
|
590
|
+
server (*"por el MANAGER: purgeObject(role_binding:R) ⇒ 422 … authorize(U, read, S) SIGUE true"*, *"por el
|
|
591
|
+
DRIVER en directo"*, *"cierre-2 · relate/unrelate/check/listSubjects/listObjects con relation: undefined"*),
|
|
592
|
+
and the published runner plants it on the six for any third-party driver (*"F-05 ·
|
|
593
|
+
purgeObject/purgeSubject(userset)/check/listObjects/listSubjects/membersOf"*). `membersOf` runs F-05 **before**
|
|
594
|
+
its capability check, so a `role_binding` there is 422 even where `membersOf` itself would be 500.
|
|
595
|
+
|
|
568
596
|
**`membersOf` is `database`-only.** `membersOf(group, 'member', partition)` returns the **transitive**
|
|
569
597
|
membership (through nested groups). Only the `database` driver has it (a recursive CTE); in `openfga`
|
|
570
598
|
it is 500 `E_AUTHZ_UNSUPPORTED` naming it — the transitive form is `ListUsers`, which truncates
|
|
@@ -615,6 +643,30 @@ on the three engines, with the mutant (column back to nullable ⇒ two rows) red
|
|
|
615
643
|
before L-4b runs [the recipe](#upgrading-from-240-alpha2-before-l-4b-authz_relationssubject_relation): the
|
|
616
644
|
driver tolerates old `NULL` rows on read meanwhile.
|
|
617
645
|
|
|
646
|
+
### The relation write event (2.4.0-alpha.3)
|
|
647
|
+
|
|
648
|
+
`onRelationWrite` gives **the same guarantees as `onWrite`** — every one of them was missing or half-true on the relations port until `2.4.0-alpha.3`, and COGNITIV found the first two in real consumption. The event (`RelationWriteEvent`) mirrors the shape of `AuthzWriteEvent`: one type, optional fields, no discriminated union.
|
|
649
|
+
|
|
650
|
+
| `operation` | What the event carries | What it does not |
|
|
651
|
+
|---|---|---|
|
|
652
|
+
| `relate` | `subject`, `relation`, `object`, `partition`, `expiresAt` if you passed it, `actor` if you passed it | — |
|
|
653
|
+
| `unrelate` | `subject`, `relation`, `object`, `partition`, `actor` | — |
|
|
654
|
+
| `purgeObject` | `object`, `partition`, `actor` | no `subject`, no `relation` |
|
|
655
|
+
| `purgeSubject` | `subject`, `partition`, `actor` | no `object`, no `relation` |
|
|
656
|
+
|
|
657
|
+
A purge notifies **one event per call** — even when the driver sweeps both spellings of the partition uuid underneath, and even when it deletes many tuples — and it **does not count**: the port returns `void` (a count would break the port for third-party drivers, and inside `{ transaction }` it would be a number your rollback contradicts). If you need the "how many", call `enumerateRelations` (or `listSubjects`) before purging — inside your transaction, through your transaction. And a purge that deletes nothing notifies all the same: the auditable fact is *someone asked to delete the keys of X*, whether or not there were any. `scopes.detached` on the roles port emits one `scope_purged` per spelling because there the **manager** canonicalises; the relations manager does not, so the divergence is on purpose and pinned (*"E6 · UN evento por llamada aunque el driver barra DOS ortografías"*). A purge does not go through `assertWrite` — `RelationRef.operation` is `'relate' | 'unrelate'`, the published type of the R-13 hook — and that is pinned too (*"E9 · negativo declarado"*). But F-05 runs first on the purges too (2.4.0-alpha.3, closing the audit's critical finding): `purgeObject` validates the object type and `purgeSubject` the userset of its subject **before** the actor, the scope and the driver, so a purge of a `role_binding` is 422 with zero driver calls and zero events — see [F-05 on the seven operations](#relations-rebac-24) above. `purgeSubject` also validates its subject like `relate` does (a malformed holder or `{}` is 422 `E_AUTHZ_INVALID_IDENTITY` and never reaches the event).
|
|
658
|
+
|
|
659
|
+
**The four guarantees, on the four writes** (`relate`, `unrelate`, `purgeObject`, `purgeSubject`):
|
|
660
|
+
|
|
661
|
+
- **`transactional: true`** when the write went through your `{ transaction }` (lot L-4): at that instant the tuple exists only inside your transaction and is a fact if and only if you commit — the package never sees that; a sink that wants the last word hangs itself on `trx.after('commit')`, and the suite runs exactly that recipe: rollback ⇒ the deferred sink holds zero firm records and the census holds zero tuples, commit ⇒ one and one (*"B2 · rollback ⇒ ni tupla NI evento contabilizado como definitivo"*, *"E4 · purgeObject/purgeSubject dentro de una trx"*). Absent — not `false` — without a transaction, and `{ transaction: undefined }`/`null` count as none. The published runner judges it as the other face of the `transactionalWrites` capability: with `true`, the eight events of the four writes inside a transaction carry it; with `false`, a refused write emits nothing.
|
|
662
|
+
- **`indeterminate: true`** — invariant 13, now on this port too: a relation write that elapses its deadline notifies `onRelationWrite` with `indeterminate: true` before propagating the 503 `E_AUTHZ_BACKEND_TIMEOUT`, with the rest of the event intact (*"C1 · relate/unrelate que vencen el deadline notifican indeterminate: true"*, *"E5 · purgeObject/purgeSubject que vencen el deadline"*). Both drivers have a deadline to elapse: `database` through `guardSql` (`timeoutMs`), `openfga` through the SDK's `baseOptions.timeout` with `retryParams.maxRetry: 0` — measured against a socket that accepts and never answers, without a server (*"C4 · openfga (relations) contra un servidor MUDO"*). Inside your transaction the event carries `indeterminate: true` *and* `transactional: true` (PostgreSQL leaves your transaction aborted, MySQL kills the statement and leaves it alive, SQLite cannot elapse a deadline: `SQLITE_BUSY` is a 503 that is not a timeout and emits nothing — *"C3 · deadline vencido DENTRO de la trx externa"*); on `openfga` the event never carries `transactional` (gate 1 makes `{ transaction }` impossible there). A 422 (F-05, R-16, a bad `expiresAt`), an `assertWrite` that throws and the freeze emit **nothing** and call nothing: that write did not happen (*"C2"*). A 503 that is not a timeout emits nothing **when it arrives before the first statement or `Write`** — and that is the only case on `relate`/`unrelate` (one statement, one `Write`); on `database` a purge is one `DELETE`, so it holds there too; on `openfga` only when nothing had been deleted yet, because a purge on `openfga` is **not one request**: it is one `Read` + `deleteTuples` + `Read` (the proof of zero) per spelling of the partition uuid and, for `purgeSubject`, per declared type. When such a purge fails **after** it has already deleted part of its tuples — a connection error, or the 500 `E_AUTHZ_PURGE_INCOMPLETE` of the proof of zero — the driver marks the error (`markPartialWrite`, exported from the package: a property, the class and the `code` are unchanged) and the manager notifies `indeterminate: true` as well, before propagating, exactly like a deadline: *may have happened* is the honest word there (*"purgeObject: la demostración de cero falla tras el deleteTuples ⇒ … indeterminate: true"*, and its control: a failure on the first `Read` emits nothing). A third-party driver whose purge is multi-request has the same duty.
|
|
663
|
+
- **A hook that throws — sync or async — is logged and swallowed**: the write is already applied when the hook runs, so propagating would report a failure for an operation that did happen and invite a retry of something done. Until alpha.3 the hook was called with neither `await` nor `try/catch`: a failing audit sink turned an applied `relate` into an error for the caller, and an `async` hook that rejected vanished as an unhandled rejection. Now the returned promise is awaited, the failure is logged naming the operation (`logger.error`, or `console.error` outside an app), and the caller gets its result (*"D1 · un onRelationWrite SÍNCRONO que lanza no propaga"*, *"D2 · un onRelationWrite ASYNC que rechaza tampoco propaga"*). The flip side of that guarantee: the sink is **awaited**, so it is on the critical path of the write — its latency is the write's latency — and, with `{ transaction }`, inside your transaction: a sink that never resolves keeps it open (and, since `{ transaction }` requires a pool ≥ 2, a few of those exhaust the engine's connections and the freeze barrier starts answering 503 to everybody). There is no deadline on the hook, on purpose — parity with `hooks.onWrite`, and a deadline would be a design decision of its own — so defer anything slow to `trx.after('commit')` or to a queue, and never do I/O without a timeout in there.
|
|
664
|
+
- **`actor` is validated like any identity, then required**: a malformed `actor` is 422 `E_AUTHZ_INVALID_IDENTITY` before the driver, on the four writes, and never travels to the event (until alpha.3 only its *presence* was checked, so `{ uuid: 'A#B' }` poisoned the trail and `actor: {}` satisfied `requireActor`); `requireActor: true` + `actor: {}` is the identity 422, not "actor required" (*"D4"*). **Breaking**: `requireActor` now reaches `purgeObject`/`purgeSubject` — a mass deletion of keys is the write that most needs an actor — so a deployment with `requireActor: true` that purged without one gets 422 `E_AUTHZ_ACTOR_REQUIRED` (*"E3"*); the purges take `RelationPurgeOptions` (`transaction` + `actor`).
|
|
665
|
+
|
|
666
|
+
**`assertWrite`.** `assertWrite` is **synchronous, pure and has no `actor`** on purpose (R-13): it receives the full tuple reference (`subject`, `relation`, `object`, `partition`, `operation`, `expiresAt`) and throws to refuse; it runs after F-05 and before the driver, so a refusal is zero driver calls and zero events. Because a function returning `Promise<void>` is assignable to `void`, `assertWrite: async (ref) => { throw … }` compiles — and until alpha.3 the promise was discarded and the write went in: a policy gate that refused nothing, in silence. Since alpha.3 **returning a promise is 500 `E_AUTHZ_CONFIG`** before touching the driver — the message names `assertWrite`, "synchronous", and the recipe — whether the promise would have rejected or resolved (*"D3 · un assertWrite ASYNC no es un assertWrite"*). And more generally `assertWrite` **does not return verdicts**: it throws or it does not. `assertWrite: (ref) => allowed.has(ref.object.type)` compiles under `--strict` just the same, and until the alpha.3 closing lot returning `false` did not refuse anything — the write went in — so now returning **any value at all** — `false`, `true`, a thenable *function*, `null`, an object — is 500 `E_AUTHZ_CONFIG` before the driver, with zero calls and zero events (*"assertWrite que DEVUELVE algo (false, true, thenable-función, null) es 500"*). The policy that needs the actor, your database or an `await` belongs in your service, before calling `relate`/`unrelate`.
|
|
667
|
+
|
|
668
|
+
**Wiring.** The three live in `relations` of `defineConfig` — `relations.assertWrite`, `relations.onRelationWrite`, `relations.requireActor` — and not in `hooks`: one home for the port, no precedence rule (the published stub declares them there, and a case compiles it). Until alpha.3 the provider passed neither hook to the service manager (`authz.relations`), so through the service there was no policy gate and no audit trail at all; `requireActor` *was* wired (inherited from the root). `relations.requireActor` overrides the root per port, `relations.requireActor ?? requireActor`, exactly like `relations.requireTransactionalWrites`: unset inherits, `true` requires it here even if the root does not, `false` waives it here even if the root requires it (*"A3 · relations.requireActor anula el raíz por PUERTO, en los tres sentidos"*). Read the third direction as what it is: `relations.requireActor: false` under a root `requireActor: true` **overrides the root for relations, silently** — `warnOnOptInSecurity` does not cover it — so a template that carries the key waives the actor on the four relation writes without a warning; write it only on purpose. `manager.driver()` still skips all three, as documented above.
|
|
669
|
+
|
|
618
670
|
**Still not in 2.4.** `includes` with `from` (cross-object inheritance like `viewer from parent`),
|
|
619
671
|
which would add a TTU between object types and force re-measuring depth. Relation expiry (R-15)
|
|
620
672
|
**landed in 2.4.0-alpha.2** (see above); an installation that migrated 2.4.0-alpha.1 adds the column with
|
|
@@ -645,7 +697,7 @@ Every error the package raises carries `status` and `code`. A standard AdonisJS
|
|
|
645
697
|
| `E_AUTHZ_NOT_WITHIN` | 422 | any of the nine writes with `within` not in the chain of the scope it writes to (the new parent **and** the child's current chain for `scopes.moved`, and for `scopes.attached` of an existing child; the role's owner for the delegation API since 2.2) (2.1) |
|
|
646
698
|
| `E_AUTHZ_WITHIN_REQUIRED` | 422 | `requireWithin` set and a write without `within` (2.1) |
|
|
647
699
|
| `E_AUTHZ_WITHIN_ROOT_FORBIDDEN` | 422 | `requireWithin: 'non-root'` and `within: APP_SCOPE` (2.1) |
|
|
648
|
-
| `E_AUTHZ_ACTOR_REQUIRED` | 422 | `requireActor: true` and a write without `actor` (2.1) |
|
|
700
|
+
| `E_AUTHZ_ACTOR_REQUIRED` | 422 | `requireActor: true` and a write without `actor` (2.1); on the relations port (`relations.requireActor ?? requireActor`) the four writes, `purgeObject`/`purgeSubject` included since 2.4.0-alpha.3 |
|
|
649
701
|
| `E_AUTHZ_TOO_MANY_SCOPES` | 422 | `authorizedScopes`/`expandExcludedSubtrees` over `maxScopes`, or `descendantsOf` over `maxNodes` (`sqlDescendantsOf`: also a possible cycle) — never a partial list (2.1) |
|
|
650
702
|
| `E_AUTHZ_BACKEND_UNAVAILABLE` | 503 | facts backend or SQL catalog did not answer (both drivers, catalog sync/diff and the `authz_catalog_version` check included); the version row is missing or unreadable ("migración 2.0 no aplicada": fail-closed, never version 0); a per-check `error` in an OpenFGA `batchCheck` |
|
|
651
703
|
| `E_AUTHZ_BACKEND_TIMEOUT` | 503 | `timeoutMs` elapsed (subclass of the above) |
|
|
@@ -653,10 +705,10 @@ Every error the package raises carries `status` and `code`. A standard AdonisJS
|
|
|
653
705
|
| `E_AUTHZ_FREEZE_HELD` | 423 | `freeze()` (or a second `authz:reconcile`) found a live freeze owned by someone else: two windows never interleave, and only the owner's token — or `authz:unfreeze` — lifts one. The message names the holder, the reason and the fence |
|
|
654
706
|
| `E_AUTHZ_RESOLVER_FAILED` | 503 | your `resolveChain`, `parentOf` or `descendantsOf` threw or answered a malformed scope; `descendantsOf` and `resolveChain` disagree in `authorizedScopes`; a subtree to exclude cannot be enumerated |
|
|
655
707
|
| `E_AUTHZ_WRITE_CONFLICT` | 409 | an `openfga` write kept clashing with another transaction over the same tuples (FGA answers `Aborted`/409, or 400 "cannot write a tuple which already exists"): the driver re-reads and re-applies, and only gives up after three rounds. The backend answered, so this is never a 503 — retry the write. Also (2.4.0-alpha.2, lot L-3) a `database` `grant`/`deny` **inside your transaction** (`{ transaction }`) that hits the UNIQUE because another transaction committed the same fact while yours was open: outside a transaction the driver re-reads and keeps the winner's row; inside yours it cannot (PostgreSQL has already aborted your transaction — every later statement is `25P02` until you roll back — and under REPEATABLE READ the re-read would not see the winner), so it **poisons your transaction**: roll back and retry. Measured on all three engines, see [writing inside your transaction](#writing-inside-your-transaction-240-alpha2). The same for a `database` `relate`/`unrelate`/`purge*` inside your transaction (lot L-4) — on a UNIQUE clash of a tuple (userset, and since lot L-4b holder too: `subject_relation` is `''`, never `NULL`), and on a **deadlock** (two transactions writing two tuples in crossed order: PostgreSQL `40P01`, MySQL `1213`; the engine's victim gets this 409, never a hang nor the raw error) |
|
|
656
|
-
| `E_AUTHZ_CONFIG` | 500 | contradictory config (`holderTypes` not injective or a holder type not declared in it, `scopes.*` without resolver, `appAccess` without `permission`, `catalog` together with `catalogRevalidate`, an invalid `maxAgeMs`); `bumpAuthzCatalogVersion` called without the writing transaction's client; a `{ transaction }` handed to `sqlScopeOutbox` that is not an open Lucid transaction of the outbox's own connection (another connection, a `QueryClient`, or the whole `db` — the message names both connections); `requireTransactionalWrites: true` (root, or `relations.requireTransactionalWrites`) with an active driver that does not declare `transactionalWrites: true` — raised **when the driver is resolved**, so reads fail too and the deployment does not start (2.4) |
|
|
708
|
+
| `E_AUTHZ_CONFIG` | 500 | contradictory config (`holderTypes` not injective or a holder type not declared in it, `scopes.*` without resolver, `appAccess` without `permission`, `catalog` together with `catalogRevalidate`, an invalid `maxAgeMs`); `bumpAuthzCatalogVersion` called without the writing transaction's client; a `{ transaction }` handed to `sqlScopeOutbox` that is not an open Lucid transaction of the outbox's own connection (another connection, a `QueryClient`, or the whole `db` — the message names both connections); `requireTransactionalWrites: true` (root, or `relations.requireTransactionalWrites`) with an active driver that does not declare `transactionalWrites: true` — raised **when the driver is resolved**, so reads fail too and the deployment does not start (2.4); a `relations.assertWrite` that returns a promise — or any value at all: `false`, `true`, a thenable function (it must be synchronous and it does not return verdicts: it throws or it does not; the write is refused before the driver, 2.4.0-alpha.3) |
|
|
657
709
|
| `E_AUTHZ_ROLE_IS_NOT_ACCESS` | 500 | `appAccess({ role })` |
|
|
658
710
|
| `E_AUTHZ_INTERNAL` | 500 | package invariant violated (empty scope set on a write, misaligned batch, a third-party `authorizeMany` answering the wrong shape, a `Read` continuation token that never advances or more than 10,000 pages, a corrupt `assignable_at`/`owner_scope_key` row) |
|
|
659
|
-
| `E_AUTHZ_PURGE_INCOMPLETE` | 500 | `purgeScope` could not prove zero |
|
|
711
|
+
| `E_AUTHZ_PURGE_INCOMPLETE` | 500 | `purgeScope` could not prove zero; on the relations port, `purgeObject`/`purgeSubject` of `openfga` could not prove zero after deleting — the error carries the `markPartialWrite` mark and the event was emitted with `indeterminate: true` (2.4.0-alpha.3) |
|
|
660
712
|
| `E_AUTHZ_RECONCILE_TOO_LARGE` | 500 | the destination's dump does not fit the declared `--max-tuples` cap (default 1 000 000) and reconciling needs the whole snapshot to know what is left over. Nothing was written; raise the cap if your process can take it |
|
|
661
713
|
| `E_AUTHZ_MASS_RECONCILE_REFUSED` | 500 | `authz:reconcile --prune` would delete facts from the destination while the source has not contributed a single **usable** one — empty, or every fact it returned was skipped (expired, on scopes your tree no longer resolves): the signature of a wrong connection, of the wrong source, or of a blind resolver. Nothing was deleted; pass `--allow-mass-delete` if you really mean to empty it (`--dry-run` flags it instead of throwing). A source that is merely **stale** but still usable is covered by reading the facts from whoever owns them |
|
|
662
714
|
| `E_AUTHZ_MASS_PURGE_REFUSED` | 500 | `pruneOrphanRoles({ force: true })` would purge every distinct owner (or more than half the local roles): the signature of a blind `resolveChain`. Nothing was deleted; pass `allowMassPurge: true` (`--allow-mass-purge`) if the prune is real |
|
|
@@ -715,6 +767,22 @@ Every driver **declares** this through `driver.capabilities`, and each declared
|
|
|
715
767
|
|
|
716
768
|
**The anti-cycle checks are the package's, in both modes, and they are not optional.** Measured against OpenFGA v1.19: the server *accepts* an edge that closes a cycle, does not hang, answers in 2–7 ms, and from then on inheritance runs both ways — a grant on a descendant grants on its ancestor, and with the root inside the cycle it grants everywhere. Nothing is logged and there is no error to catch. That is why `child ≠ app`, "the parent exists" and `child ∉ ancestors(parent)` are checked before anything is written (422, no edge), and why you should not expect the backend to be a second line of defence.
|
|
717
769
|
|
|
770
|
+
### Reading capabilities at runtime (2.4.0-alpha.4)
|
|
771
|
+
|
|
772
|
+
The table above is what each driver **declares**; your service code can read it from the manager instead of guessing from the driver name — and instead of calling `driver()`:
|
|
773
|
+
|
|
774
|
+
```ts
|
|
775
|
+
// The same service code serves both drivers, without a single driver() in it.
|
|
776
|
+
if ((await authorization.capabilities()).transactionalWrites) {
|
|
777
|
+
await authorization.grant(user, 'editor', unit, { transaction: trx }) // both or neither, with YOUR transaction
|
|
778
|
+
} else {
|
|
779
|
+
await authorization.grant(user, 'editor', unit) // openfga: the recipe by direction
|
|
780
|
+
}
|
|
781
|
+
if (relations.capabilities().transactionalWrites) { /* relate(…, { transaction: trx }) */ }
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
`authorization.capabilities()` is `async` — it resolves the active driver through `driver()`, so it pays the driver factory once (cached from then on) and **inherits its two gates**: with `requireTransactionalWrites: true` and a driver that does not comply it is the same 500 `E_AUTHZ_CONFIG` the deployment already fails with, and a `facts` driver without `scopes.outbox` or the signature is the same 500 `E_AUTHZ_SCOPE_DRIFT_UNGUARDED`. That is deliberate: capabilities belong to the *instance* (`database` decides `transactionalWrites` from its options), and a reading that resolved the driver by any other path would leave `driver()` serving an instance that never passed the gates. Call it once at boot (a `booted`/`ready` hook), not per request. `relations.capabilities()` is synchronous: the relations driver is built eagerly and its gate already ran. Both return a **read-only, frozen, normalised copy** typed as `AuthorizationDriverCapabilities` / `RelationsDriverCapabilities` (both exported): every known key is present, only `true` is `true` — the same reading the write gates apply — so a third-party driver with partial or no `capabilities` reads as `false`, **never `undefined`**; mutating the copy throws in strict mode and changes nothing. The read does not soften anything: `{ transaction }` to a driver that declares `false` is still 500 `E_AUTHZ_UNSUPPORTED` whether or not you asked first, and **`driver()` is still platform API** — tenant code should not call it, and a code review can keep grepping for it.
|
|
785
|
+
|
|
718
786
|
## Performance
|
|
719
787
|
|
|
720
788
|
Two optimisations landed in 2.1, both measured and both **without changing a single answer** (the contract suite is the proof: same cases, both drivers, before and after). Reproduce the numbers with `OPENFGA_TEST_URL=http://localhost:8101 node --import @poppinss/ts-exec scripts/bench_authorize.mjs` (chain of 3 scopes through your resolver, 5 roles per level, 20 permissions, N=200 after 30 warm-up calls, HTTP round-trip included; OpenFGA v1.19.0 on the same machine):
|
|
@@ -876,6 +944,8 @@ This is the **only** migration and verification primitive of the package, and th
|
|
|
876
944
|
|
|
877
945
|
**It needs `scopes.enumerateEdges`** in your config: the whole tree, paginated with a cursor. `sqlScopeEdges({ table, uuidColumn, parentColumn, typeColumn })` implements it over a table with a parent column, exactly like `sqlDescendantsOf`. Without it the command refuses (500 `E_AUTHZ_CONFIG`) instead of assuming a flat tree — a flat tree would be an invented hierarchy, and an invented hierarchy grants.
|
|
878
946
|
|
|
947
|
+
**`sqlScopeEdges` covers ONE table, and concatenating several does not cover a tree that lives in more than one (2.4.0-alpha.4).** The helper does a `LEFT JOIN` of the table *against itself* on `parentColumn = uuidColumn`, and a row whose declared parent has no match in *that* table is **skipped in silence** — that is the documented "a parent that does not exist is not emitted", and it has no way to tell "orphan" from "the parent lives in another table". So if your tree is spread over several tables (say `organizations` → `organization_units` → `ia_spaces`), the obvious composition — one `sqlScopeEdges` per table, drained one after the other — **does not emit the edges between tables**: every `ia_space → organization_unit` and `organization_unit → organization` edge is dropped without a word, and the parent's *type* would come out wrong anyway (it is the child table's fixed `scopeType`). With `authz:reconcile --to=openfga` that is not "some edges are missing": the destination sees them as surplus and deletes them, and by invariant 9 (`can_<P>` requires `rooted`) **the whole subtree denies**. Fail-closed, noisy, nobody escalates — and a day lost. Write your own `enumerateEdges` instead: one query that concatenates the tables (`UNION ALL` of `(child_uuid, child_type, parent_uuid, parent_type)` across the tables, `parent IS NULL` meaning `app`), ordered by a total and stable key (the child uuid works) with the cursor over that key, and emitting an edge only when the parent exists in *any* of the tables; then compare with `authz:reconcile --to=<driver> --dry-run` before the first real pass. Alternatively chain the tables in a fixed order and encode *which table* inside the opaque cursor — the contract only asks for a total, stable order across calls. *Recipe not verified by the suite* (it has no multi-table fixture); a composition helper (`composeScopeEdges`, or `parentTable` on `sqlScopeEdges`) is deferred to 2.6.
|
|
948
|
+
|
|
879
949
|
What one pass does, in order: the **root marker** (`scope:app#rooted`, without which the whole store denies), the **catalog projection** (`role:<uuid>#permits_<P>`, read with the very same function `syncAuthzCatalog` uses), the **tree** from `enumerateEdges`, and the **facts** from `authz_assignments` / `authz_denies` — each assignment as its `assignee` tuple plus the two (c2) edges, each deny as `scope:<key>#denied_<P>`. It is **idempotent** (a second pass writes zero — the word the requirement used, and it has its case), **resumable** (the source is read in batches of 100 with a cursor over the primary key; repeating a pass converges) and **never silent**: the report carries `{ written, updated, unchanged, extra, deleted, skipped{reason} }` per phase, plus each row that did not migrate with its reason (`unknown-scope`, `unknown-role`, `unknown-permission`, `unknown-holder-type`, `expired`, `role-not-visible`, `cycle`, …).
|
|
880
950
|
|
|
881
951
|
**The source is read in one consistent snapshot — in the direction where the source is `authz_*` (2.3).** The two sweeps (`authz_assignments`, then `authz_denies`) used to run on the global connection, one after the other, so the gap between them — the time it takes to walk the first table in batches of 100 — let *composite* business operations slip through. An offboarding is `revoke` + `removeDeny`: landing in that gap, the pass kept one half of each and wrote **the role without its deny** into the destination, granting a permission that **neither the previous nor the following state granted**, while the report said `written=13 extra=0 skipped={} clean=true`. That is not a lost permission, it is a fabricated one — an escalation, with nothing in the report to distrust. Both sweeps now run inside **one repeatable-read transaction**, so the worst outcome of the window is *the consistent state of `t0`* — recoverable drift that the next pass repairs — instead of a state that never existed. What each engine guarantees is not the same and is stated rather than assumed: PostgreSQL takes the snapshot with `BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ`; MySQL/InnoDB is sent `SET TRANSACTION ISOLATION LEVEL REPEATABLE READ` explicitly (it is InnoDB's default, but a server setting is not a promise of this package) and fixes the consistent read on the first query; SQLite takes no isolation level at all (knex warns and ignores it) because a read transaction is already a snapshot there, so the package does not send it one.
|
package/build/commands/main.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
|
|
13
|
-
export
|
|
1
|
+
import type { CommandMetaData } from '@adonisjs/core/types/ace';
|
|
2
|
+
import OpenFgaProvision from './openfga_provision.js';
|
|
3
|
+
import AuthzCatalogDiff from './authz_catalog_diff.js';
|
|
4
|
+
import AuthzCatalogSync from './authz_catalog_sync.js';
|
|
5
|
+
import AuthzCatalogPruneOrphans from './authz_catalog_prune_orphans.js';
|
|
6
|
+
import AuthzScopesRelay from './authz_scopes_relay.js';
|
|
7
|
+
import AuthzReconcile from './authz_reconcile.js';
|
|
8
|
+
import AuthzRelationsReconcile from './authz_relations_reconcile.js';
|
|
9
|
+
import AuthzFreeze from './authz_freeze.js';
|
|
10
|
+
import AuthzUnfreeze from './authz_unfreeze.js';
|
|
11
|
+
export { OpenFgaProvision, AuthzCatalogDiff, AuthzCatalogSync, AuthzCatalogPruneOrphans, AuthzScopesRelay, AuthzReconcile, AuthzRelationsReconcile, AuthzFreeze, AuthzUnfreeze, };
|
|
12
|
+
/** Contrato de loader de ace (`LoadersContract.getMetaData`). */
|
|
13
|
+
export declare function getMetaData(): Promise<CommandMetaData[]>;
|
|
14
|
+
/** Contrato de loader de ace (`LoadersContract.getCommand`): la clase, o `null` si el nombre no es nuestro. */
|
|
15
|
+
export declare function getCommand(metaData: CommandMetaData): Promise<any>;
|
|
14
16
|
//# sourceMappingURL=main.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,gBAAgB,MAAM,wBAAwB,CAAA;AACrD,OAAO,gBAAgB,MAAM,yBAAyB,CAAA;AACtD,OAAO,gBAAgB,MAAM,yBAAyB,CAAA;AACtD,OAAO,wBAAwB,MAAM,kCAAkC,CAAA;AACvE,OAAO,gBAAgB,MAAM,yBAAyB,CAAA;AACtD,OAAO,cAAc,MAAM,sBAAsB,CAAA;AACjD,OAAO,uBAAuB,MAAM,gCAAgC,CAAA;AACpE,OAAO,WAAW,MAAM,mBAAmB,CAAA;AAC3C,OAAO,aAAa,MAAM,qBAAqB,CAAA;AAE/C,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,WAAW,EACX,aAAa,GACd,CAAA;AAeD,iEAAiE;AACjE,wBAAgB,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAExD;AAED,+GAA+G;AAC/G,wBAAgB,UAAU,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
|
package/build/commands/main.js
CHANGED
|
@@ -1,14 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* **El loader de ace de los comandos del paquete** (2.4.0-alpha.4). Se
|
|
3
|
+
* registra con `node ace configure @jantstack/adonis-authz`, que escribe en el
|
|
4
|
+
* `adonisrc.ts` del consumidor:
|
|
5
|
+
*
|
|
6
|
+
* commands: [() => import('@jantstack/adonis-authz/commands')]
|
|
7
|
+
*
|
|
8
|
+
* y ace trata **el módulo importado como el loader**: `Kernel.boot()` llama
|
|
9
|
+
* `getMetaData()` y `getCommand(meta)` sobre el namespace del módulo, sin
|
|
10
|
+
* desenvolver `default` (mismo contrato que `@adonisjs/core/commands` y
|
|
11
|
+
* `@adonisjs/lucid/commands`, que leen un `commands.json` generado por el
|
|
12
|
+
* assembler; aquí no hay assembler, así que el loader es un `ListLoader`
|
|
13
|
+
* sobre las nueve clases y no hay índice que generar).
|
|
14
|
+
*
|
|
15
|
+
* Hasta alpha.3 este fichero re-exportaba las nueve clases y NADA más, de
|
|
16
|
+
* modo que en un consumidor configurado con `node ace configure` **`node ace`
|
|
17
|
+
* entero reventaba** al arrancar (`TypeError: loaderInstance.getMetaData is
|
|
18
|
+
* not a function`), no solo `openfga:provision`. Ningún caso lo vio porque
|
|
19
|
+
* ninguno registraba el barrel como lo registra `createAceKernel`
|
|
20
|
+
* (`tests/commands.spec.ts` · C-1 lo hace desde alpha.4).
|
|
21
|
+
*
|
|
22
|
+
* Las re-exportaciones con nombre se MANTIENEN (quien importe una clase
|
|
23
|
+
* suelta sigue funcionando). Lo que NO vale desde alpha.4 es envolver el
|
|
24
|
+
* módulo en un `ListLoader(Object.values(m))`: `Object.values` incluye ahora
|
|
25
|
+
* las dos funciones y `ListLoader.getMetaData()` llama `serialize()` sobre
|
|
26
|
+
* cada valor. Se registra el módulo directamente, como escribe `configure`.
|
|
27
|
+
* Y NO se exporta `default`: ace no lo cargaría.
|
|
4
28
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
29
|
+
import { ListLoader } from '@adonisjs/core/ace';
|
|
30
|
+
import OpenFgaProvision from './openfga_provision.js';
|
|
31
|
+
import AuthzCatalogDiff from './authz_catalog_diff.js';
|
|
32
|
+
import AuthzCatalogSync from './authz_catalog_sync.js';
|
|
33
|
+
import AuthzCatalogPruneOrphans from './authz_catalog_prune_orphans.js';
|
|
34
|
+
import AuthzScopesRelay from './authz_scopes_relay.js';
|
|
35
|
+
import AuthzReconcile from './authz_reconcile.js';
|
|
36
|
+
import AuthzRelationsReconcile from './authz_relations_reconcile.js';
|
|
37
|
+
import AuthzFreeze from './authz_freeze.js';
|
|
38
|
+
import AuthzUnfreeze from './authz_unfreeze.js';
|
|
39
|
+
export { OpenFgaProvision, AuthzCatalogDiff, AuthzCatalogSync, AuthzCatalogPruneOrphans, AuthzScopesRelay, AuthzReconcile, AuthzRelationsReconcile, AuthzFreeze, AuthzUnfreeze, };
|
|
40
|
+
/** Los nueve, en el orden en que `node ace list` los enseña. */
|
|
41
|
+
const loader = new ListLoader([
|
|
42
|
+
OpenFgaProvision,
|
|
43
|
+
AuthzCatalogDiff,
|
|
44
|
+
AuthzCatalogSync,
|
|
45
|
+
AuthzCatalogPruneOrphans,
|
|
46
|
+
AuthzScopesRelay,
|
|
47
|
+
AuthzReconcile,
|
|
48
|
+
AuthzRelationsReconcile,
|
|
49
|
+
AuthzFreeze,
|
|
50
|
+
AuthzUnfreeze,
|
|
51
|
+
]);
|
|
52
|
+
/** Contrato de loader de ace (`LoadersContract.getMetaData`). */
|
|
53
|
+
export function getMetaData() {
|
|
54
|
+
return loader.getMetaData();
|
|
55
|
+
}
|
|
56
|
+
/** Contrato de loader de ace (`LoadersContract.getCommand`): la clase, o `null` si el nombre no es nuestro. */
|
|
57
|
+
export function getCommand(metaData) {
|
|
58
|
+
return loader.getCommand(metaData);
|
|
59
|
+
}
|
|
14
60
|
//# sourceMappingURL=main.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAE/C,OAAO,gBAAgB,MAAM,wBAAwB,CAAA;AACrD,OAAO,gBAAgB,MAAM,yBAAyB,CAAA;AACtD,OAAO,gBAAgB,MAAM,yBAAyB,CAAA;AACtD,OAAO,wBAAwB,MAAM,kCAAkC,CAAA;AACvE,OAAO,gBAAgB,MAAM,yBAAyB,CAAA;AACtD,OAAO,cAAc,MAAM,sBAAsB,CAAA;AACjD,OAAO,uBAAuB,MAAM,gCAAgC,CAAA;AACpE,OAAO,WAAW,MAAM,mBAAmB,CAAA;AAC3C,OAAO,aAAa,MAAM,qBAAqB,CAAA;AAE/C,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,WAAW,EACX,aAAa,GACd,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC;IAC5B,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,wBAAwB;IACxB,gBAAgB;IAChB,cAAc;IACd,uBAAuB;IACvB,WAAW;IACX,aAAa;CACP,CAAC,CAAA;AAET,iEAAiE;AACjE,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,WAAW,EAAE,CAAA;AAC7B,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,UAAU,CAAC,QAAyB;IAClD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;AACpC,CAAC"}
|
|
@@ -27,10 +27,54 @@ import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
|
27
27
|
export default class OpenFgaProvision extends BaseCommand {
|
|
28
28
|
static commandName: string;
|
|
29
29
|
static description: string;
|
|
30
|
+
/**
|
|
31
|
+
* **Bootstrap sin base: `startApp: false`, y el comando BOOTEA la app por su
|
|
32
|
+
* cuenta hasta `boot()`** (2.4.0-alpha.4, opción B del panel; el único de
|
|
33
|
+
* los nueve con `startApp: false`, a propósito).
|
|
34
|
+
*
|
|
35
|
+
* `this.app.config` NO existe hasta `app.boot()`: `Application.init()` no
|
|
36
|
+
* llama a `ConfigManager.process()`, `boot()` sí (`@adonisjs/application`
|
|
37
|
+
* 9.0.1; el docblock de `get config()` dice «after init» y es falso). Y el
|
|
38
|
+
* ignitor de ace solo hace `boot()`+`start()` con `startApp: true`. Hasta
|
|
39
|
+
* alpha.3 esto leía `this.app.config.get(...)` sobre `undefined`:
|
|
40
|
+
* `TypeError` en la primera línea de `run()`, en cualquier consumidor.
|
|
41
|
+
*
|
|
42
|
+
* Por qué NO `startApp: true` (opción A, la que recomendaba COGNITIV):
|
|
43
|
+
* `start()` ejecuta el `start()`/`ready()` de TODOS los providers del
|
|
44
|
+
* consumidor y sus preloads (`start/routes.ts`, `start/kernel.ts`…) —
|
|
45
|
+
* medido: un `ready()` que consulta la base mata este comando antes de
|
|
46
|
+
* `run()` con la base ausente, que es exactamente el escenario en el que
|
|
47
|
+
* corre; un preload que importe `services/relations` construye el driver
|
|
48
|
+
* de relaciones, que pide el `OPENFGA_STORE_ID` que este comando existe
|
|
49
|
+
* para producir (circular); y `app_provider.ready()` exige
|
|
50
|
+
* `config/encryption.ts` válido. Lo que este comando necesita es SOLO el
|
|
51
|
+
* config, y eso lo da `boot()` (≈80 ms: `config/*` + `register()`/`boot()`
|
|
52
|
+
* de los providers). Ver `prepare()`.
|
|
53
|
+
*/
|
|
30
54
|
static options: CommandOptions;
|
|
31
55
|
url: string | undefined;
|
|
32
56
|
storeId: string | undefined;
|
|
33
57
|
name: string | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Arranca la app hasta `boot()` — sin `start()` — para que `config/*`
|
|
60
|
+
* exista. Va en `prepare()` y no en `run()` porque `BaseCommand.exec()`
|
|
61
|
+
* encadena `prepare()` → `interact()` → `run()` dentro del mismo `try`:
|
|
62
|
+
* el fallo se reporta igual y `run()` queda limpio de ciclo de vida.
|
|
63
|
+
*
|
|
64
|
+
* Las guardas de estado NO son decorativas: `Application.boot()` es un
|
|
65
|
+
* no-op SILENCIOSO si el estado no es `initiated` (⚪ 9 del auditor), así
|
|
66
|
+
* que desde `created` primero `init()`; desde `booted`/`ready` (comando
|
|
67
|
+
* invocado dentro de una app ya arrancada) no se hace nada: idempotente.
|
|
68
|
+
* Y si tras eso `app.config` sigue sin existir, se dice y se sale con 1
|
|
69
|
+
* en vez de repetir el `TypeError` con otro disfraz.
|
|
70
|
+
*
|
|
71
|
+
* **Lo que sí exige, escrito**: «antes de que haya base» sigue siendo
|
|
72
|
+
* verdad (cero conexiones, cero consultas, ningún `start`/`ready`, ningún
|
|
73
|
+
* preload), pero todo `config/*` tiene que poder IMPORTARSE — un
|
|
74
|
+
* `config/database.ts` que lance por un `.env` incompleto rompe esto igual
|
|
75
|
+
* que a cualquier otro comando.
|
|
76
|
+
*/
|
|
77
|
+
prepare(): Promise<void>;
|
|
34
78
|
run(): Promise<void>;
|
|
35
79
|
/**
|
|
36
80
|
* Los permisos del modelo, o `null` (y exit 1) si no hay ninguno: un modelo
|
|
@@ -59,9 +103,11 @@ export declare function permissionsOfCatalogs(config: any): Promise<string[]>;
|
|
|
59
103
|
* pieza que decide si un store recién aprovisionado acepta tuplas de relación.
|
|
60
104
|
*
|
|
61
105
|
* La fuente es el config (`relations.config`), NO `authz_relations_config` de
|
|
62
|
-
* la base: `openfga:provision` es
|
|
63
|
-
*
|
|
64
|
-
* `
|
|
106
|
+
* la base: `openfga:provision` es bootstrap sin base (`startApp: false`;
|
|
107
|
+
* arranca hasta `boot()` para cargar `config/*`, que tiene que poder
|
|
108
|
+
* importarse; no ejecuta `start`/`ready` ni preloads) y el config es lo único
|
|
109
|
+
* que tiene cargado — igual que los permisos salen de `catalogs` y no de
|
|
110
|
+
* `authz_permissions`.
|
|
65
111
|
*/
|
|
66
112
|
export declare function relationsConfigOf(config: any): {
|
|
67
113
|
objectTypes: readonly any[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openfga_provision.d.ts","sourceRoot":"","sources":["../../commands/openfga_provision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,SAAsB;IACxC,MAAM,CAAC,WAAW,SAAoE;IAEtF,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IAGvB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAG3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"openfga_provision.d.ts","sourceRoot":"","sources":["../../commands/openfga_provision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,SAAsB;IACxC,MAAM,CAAC,WAAW,SAAoE;IAEtF;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IAGvB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAG3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IAEhC;;;;;;;;;;;;;;;;;;OAkBG;IACG,OAAO;IAaP,GAAG;IAkET;;;;OAIG;YACW,kBAAkB;CAajC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ1E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG;IAAE,WAAW,EAAE,SAAS,GAAG,EAAE,CAAA;CAAE,GAAG,SAAS,CAI1F"}
|
|
@@ -35,10 +35,67 @@ import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
|
35
35
|
export default class OpenFgaProvision extends BaseCommand {
|
|
36
36
|
static commandName = 'openfga:provision';
|
|
37
37
|
static description = 'Create an OpenFGA store + write the chassis authorization model';
|
|
38
|
+
/**
|
|
39
|
+
* **Bootstrap sin base: `startApp: false`, y el comando BOOTEA la app por su
|
|
40
|
+
* cuenta hasta `boot()`** (2.4.0-alpha.4, opción B del panel; el único de
|
|
41
|
+
* los nueve con `startApp: false`, a propósito).
|
|
42
|
+
*
|
|
43
|
+
* `this.app.config` NO existe hasta `app.boot()`: `Application.init()` no
|
|
44
|
+
* llama a `ConfigManager.process()`, `boot()` sí (`@adonisjs/application`
|
|
45
|
+
* 9.0.1; el docblock de `get config()` dice «after init» y es falso). Y el
|
|
46
|
+
* ignitor de ace solo hace `boot()`+`start()` con `startApp: true`. Hasta
|
|
47
|
+
* alpha.3 esto leía `this.app.config.get(...)` sobre `undefined`:
|
|
48
|
+
* `TypeError` en la primera línea de `run()`, en cualquier consumidor.
|
|
49
|
+
*
|
|
50
|
+
* Por qué NO `startApp: true` (opción A, la que recomendaba COGNITIV):
|
|
51
|
+
* `start()` ejecuta el `start()`/`ready()` de TODOS los providers del
|
|
52
|
+
* consumidor y sus preloads (`start/routes.ts`, `start/kernel.ts`…) —
|
|
53
|
+
* medido: un `ready()` que consulta la base mata este comando antes de
|
|
54
|
+
* `run()` con la base ausente, que es exactamente el escenario en el que
|
|
55
|
+
* corre; un preload que importe `services/relations` construye el driver
|
|
56
|
+
* de relaciones, que pide el `OPENFGA_STORE_ID` que este comando existe
|
|
57
|
+
* para producir (circular); y `app_provider.ready()` exige
|
|
58
|
+
* `config/encryption.ts` válido. Lo que este comando necesita es SOLO el
|
|
59
|
+
* config, y eso lo da `boot()` (≈80 ms: `config/*` + `register()`/`boot()`
|
|
60
|
+
* de los providers). Ver `prepare()`.
|
|
61
|
+
*/
|
|
38
62
|
static options = {
|
|
39
63
|
startApp: false,
|
|
40
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* Arranca la app hasta `boot()` — sin `start()` — para que `config/*`
|
|
67
|
+
* exista. Va en `prepare()` y no en `run()` porque `BaseCommand.exec()`
|
|
68
|
+
* encadena `prepare()` → `interact()` → `run()` dentro del mismo `try`:
|
|
69
|
+
* el fallo se reporta igual y `run()` queda limpio de ciclo de vida.
|
|
70
|
+
*
|
|
71
|
+
* Las guardas de estado NO son decorativas: `Application.boot()` es un
|
|
72
|
+
* no-op SILENCIOSO si el estado no es `initiated` (⚪ 9 del auditor), así
|
|
73
|
+
* que desde `created` primero `init()`; desde `booted`/`ready` (comando
|
|
74
|
+
* invocado dentro de una app ya arrancada) no se hace nada: idempotente.
|
|
75
|
+
* Y si tras eso `app.config` sigue sin existir, se dice y se sale con 1
|
|
76
|
+
* en vez de repetir el `TypeError` con otro disfraz.
|
|
77
|
+
*
|
|
78
|
+
* **Lo que sí exige, escrito**: «antes de que haya base» sigue siendo
|
|
79
|
+
* verdad (cero conexiones, cero consultas, ningún `start`/`ready`, ningún
|
|
80
|
+
* preload), pero todo `config/*` tiene que poder IMPORTARSE — un
|
|
81
|
+
* `config/database.ts` que lance por un `.env` incompleto rompe esto igual
|
|
82
|
+
* que a cualquier otro comando.
|
|
83
|
+
*/
|
|
84
|
+
async prepare() {
|
|
85
|
+
if (this.app.getState() === 'created')
|
|
86
|
+
await this.app.init();
|
|
87
|
+
if (this.app.getState() === 'initiated')
|
|
88
|
+
await this.app.boot();
|
|
89
|
+
if (!this.app.config) {
|
|
90
|
+
this.logger.error('config/* no está cargado: openfga:provision bootea la app hasta boot() para leer config/authorization.ts ' +
|
|
91
|
+
`y tras hacerlo app.config sigue sin existir (estado de la app: ${this.app.getState()}). ` +
|
|
92
|
+
'Comprueba que la app se puede inicializar y que todo config/* se puede importar.');
|
|
93
|
+
this.exitCode = 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
41
96
|
async run() {
|
|
97
|
+
if (this.exitCode === 1)
|
|
98
|
+
return;
|
|
42
99
|
const config = this.app.config.get('authorization');
|
|
43
100
|
const apiUrl = this.url ?? config?.openfga?.url;
|
|
44
101
|
if (!apiUrl) {
|
|
@@ -48,7 +105,7 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
48
105
|
}
|
|
49
106
|
const { OpenFgaClient } = await import('@openfga/sdk');
|
|
50
107
|
// El subpath `/openfga` es la única puerta al SDK (D9).
|
|
51
|
-
const { openFgaFactsModel, provisionOpenFgaStore } = await import('../src/openfga.js');
|
|
108
|
+
const { openFgaFactsModel, provisionOpenFgaStore, assertFactsModelPublishable } = await import('../src/openfga.js');
|
|
52
109
|
// Los holders del consumidor viven en SU config, no en el paquete.
|
|
53
110
|
const holderTypes = config?.holderTypes ?? {};
|
|
54
111
|
// Los PERMISOS del modelo (c2r) salen de los catálogos del config. Se
|
|
@@ -60,13 +117,19 @@ export default class OpenFgaProvision extends BaseCommand {
|
|
|
60
117
|
return;
|
|
61
118
|
// Los tipos de RELACIÓN (ReBAC) que el modelo lleva FUSIONADOS (Fase 4-8):
|
|
62
119
|
// salen del CONFIG estático (`relations.config`), igual que los permisos
|
|
63
|
-
// salen de `catalogs`. El comando es
|
|
64
|
-
//
|
|
65
|
-
//
|
|
120
|
+
// salen de `catalogs`. El comando es bootstrap sin base —`startApp: false`,
|
|
121
|
+
// arranca hasta `boot()` para cargar `config/*`, que tiene que poder
|
|
122
|
+
// importarse; no ejecuta `start`/`ready` ni preloads— y no puede leer
|
|
123
|
+
// `authz_relations_config` de la BD; el config sí. Sin `relations.config` el modelo es
|
|
66
124
|
// facts-only, como hasta ahora. Con él, un store recién aprovisionado ya
|
|
67
125
|
// acepta tuplas de relación sin un `authz:catalog:sync` previo.
|
|
68
126
|
const relations = relationsConfigOf(config);
|
|
69
127
|
if (this.storeId) {
|
|
128
|
+
// El mismo gate que la rama que CREA el store (L-F): la rama que
|
|
129
|
+
// ACTUALIZA el modelo es justo donde se toca el techo de bytes, y sin
|
|
130
|
+
// esto salía como error crudo del SDK con una request ya hecha (⚪ N1
|
|
131
|
+
// del auditor de alpha.4). Cero requests si no es publicable.
|
|
132
|
+
assertFactsModelPublishable(holderTypes, permissions, undefined, relations);
|
|
70
133
|
const client = new OpenFgaClient({ apiUrl, storeId: this.storeId });
|
|
71
134
|
const model = await client.writeAuthorizationModel(openFgaFactsModel(holderTypes, permissions, relations));
|
|
72
135
|
this.logger.success('Nuevo authorization model escrito en el store existente.');
|
|
@@ -144,9 +207,11 @@ export async function permissionsOfCatalogs(config) {
|
|
|
144
207
|
* pieza que decide si un store recién aprovisionado acepta tuplas de relación.
|
|
145
208
|
*
|
|
146
209
|
* La fuente es el config (`relations.config`), NO `authz_relations_config` de
|
|
147
|
-
* la base: `openfga:provision` es
|
|
148
|
-
*
|
|
149
|
-
* `
|
|
210
|
+
* la base: `openfga:provision` es bootstrap sin base (`startApp: false`;
|
|
211
|
+
* arranca hasta `boot()` para cargar `config/*`, que tiene que poder
|
|
212
|
+
* importarse; no ejecuta `start`/`ready` ni preloads) y el config es lo único
|
|
213
|
+
* que tiene cargado — igual que los permisos salen de `catalogs` y no de
|
|
214
|
+
* `authz_permissions`.
|
|
150
215
|
*/
|
|
151
216
|
export function relationsConfigOf(config) {
|
|
152
217
|
const objectTypes = config?.relations?.config?.objectTypes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openfga_provision.js","sourceRoot":"","sources":["../../commands/openfga_provision.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAGvD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,GAAG,mBAAmB,CAAA;IACxC,MAAM,CAAC,WAAW,GAAG,iEAAiE,CAAA;IAEtF,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,KAAK;KAChB,CAAA;IAWD,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAQ,CAAA;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,MAAM,EAAE,OAAO,EAAE,GAAG,CAAA;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6EAA6E,CAAC,CAAA;YAChG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAA;QACtD,wDAAwD;QACxD,MAAM,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"openfga_provision.js","sourceRoot":"","sources":["../../commands/openfga_provision.ts"],"names":[],"mappings":"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"}
|
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, ActorRequiredError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationTypeUnknownError, RelationUnknownError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, ReconcileTooLargeError, } from './src/errors.js';
|
|
53
|
+
export { AuthorizationBackendError, AuthorizationBackendTimeoutError, ScopeResolverError, InvalidIdentityError, InvalidSlugError, AuthorizationConfigError, AuthorizationInternalError, UnknownScopeError, NoScopeResolverError, UnknownRoleError, UnknownPermissionError, CatalogConflictError, RoleIsNotAccessError, ScopeCycleError, PurgeIncompleteError, WriteConflictError, ActorRequiredError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationTypeUnknownError, RelationUnknownError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, 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
|
|
@@ -84,7 +84,7 @@ export type { DatabaseDriverOptions } from './src/drivers/database_driver.js';
|
|
|
84
84
|
* opcional `@openfga/sdk`. Un consumidor solo-database arranca sin él (D9).
|
|
85
85
|
*/
|
|
86
86
|
export { APP_SCOPE, APP_SCOPE_TYPE, DEFAULT_RECONCILE_MAX_TUPLES } from './src/types.js';
|
|
87
|
-
export type { AuthorizationDriver, AuthorizationDriverFactory, AuthorizedScopes, AuthzWriteEvent, AuthzCatalogWriteEvent, CatalogPermissionSpec, CatalogRole, CatalogRoleSpec, CatalogSpec, ScopedRoleChanges, ScopedRoleSpec, DenyOptions, DenyRef, ExcludedSubtree, GrantOptions, GrantOutcome, HolderTypeMap, ReconcileCounts, ReconcileFact, ReconcileFactPage, ReconcileFactsEnumerator, ReconcileOptions, ReconcileReport, ReconcileSkip, ReconcileSource, RoleQuery, ScopeChainResolver, ScopeDescendantsResolver, ScopeEdge, ScopeEdgePage, ScopeEdgesEnumerator, ScopedWriteOptions, ScopeRef, ScopeType, SubjectRef, WriteOptions, RelationsDriver, RelationsDriverCapabilities, RelObject, RelSubject, RelUserset, RelationRef, RelationWriteEvent, RelationWriteOptions, RelationTransactionOptions, RelationPage, RelationObjectsPage, RelationSubjectsPage, RelationTuple, RelationTuplePage, } from './src/types.js';
|
|
87
|
+
export type { AuthorizationDriver, AuthorizationDriverFactory, AuthorizedScopes, AuthzWriteEvent, AuthzCatalogWriteEvent, CatalogPermissionSpec, CatalogRole, CatalogRoleSpec, CatalogSpec, ScopedRoleChanges, ScopedRoleSpec, DenyOptions, DenyRef, ExcludedSubtree, GrantOptions, GrantOutcome, HolderTypeMap, ReconcileCounts, ReconcileFact, ReconcileFactPage, ReconcileFactsEnumerator, ReconcileOptions, ReconcileReport, ReconcileSkip, ReconcileSource, RoleQuery, ScopeChainResolver, ScopeDescendantsResolver, ScopeEdge, ScopeEdgePage, ScopeEdgesEnumerator, ScopedWriteOptions, ScopeRef, ScopeType, SubjectRef, WriteOptions, AuthorizationDriverCapabilities, RelationsDriver, RelationsDriverCapabilities, RelObject, RelSubject, RelUserset, RelationRef, RelationWriteEvent, RelationWriteOptions, RelationTransactionOptions, RelationPurgeOptions, RelationPage, RelationObjectsPage, RelationSubjectsPage, RelationTuple, RelationTuplePage, } from './src/types.js';
|
|
88
88
|
export { isRelUserset } from './src/types.js';
|
|
89
89
|
/**
|
|
90
90
|
* **¿De quién es esta transacción?** (L-1 · 🟠 9, regla del puerto `{trx}`):
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACtK,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,YAAY,GACb,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAE9G;;;;;;;GAOG;AACH,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,gCAAgC,CAAA;AACvC,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAA;AAEvC;;;GAGG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAE7D;;;;GAIG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAC1E,YAAY,EAAE,2BAA2B,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC1E,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAE3F;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAElE;;;;;GAKG;AACH,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,yBAAyB,EACzB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -43,7 +43,11 @@ 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, ActorRequiredError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationTypeUnknownError, RelationUnknownError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, ReconcileTooLargeError,
|
|
46
|
+
export { AuthorizationBackendError, AuthorizationBackendTimeoutError, ScopeResolverError, InvalidIdentityError, InvalidSlugError, AuthorizationConfigError, AuthorizationInternalError, UnknownScopeError, NoScopeResolverError, UnknownRoleError, UnknownPermissionError, CatalogConflictError, RoleIsNotAccessError, ScopeCycleError, PurgeIncompleteError, WriteConflictError, ActorRequiredError, NotWithinError, WithinRequiredError, WithinRootForbiddenError, TooManyScopesError, TooManyLocalRolesError, MassPurgeRefusedError, PruneInterruptedError, UnsupportedDialectError, ScopeTooDeepError, UnsupportedOperationError, NoDescendantsResolverError, ViewExpiredError, RoleNotVisibleError, AmbiguousRoleError, RoleImmutableError, RoleLevelAboveOwnerError, RoleNotAssignableAtError, PermissionNotDelegableError, RankExceededError, ModelTooLargeError, RelationConfigError, RelationTypeUnknownError, RelationUnknownError, ScopeTreeDriftError, ScopeDriftUnguardedError, AuthorizationFrozenError, FreezeHeldError, MassReconcileRefusedError, ReconcileTooLargeError,
|
|
47
|
+
// Cierre de alpha.3 (invariante 13 en purgas multi-request): un driver de
|
|
48
|
+
// relaciones de terceros marca con `markPartialWrite` el fallo que llega
|
|
49
|
+
// DESPUÉS de haber borrado parte; el manager notifica `indeterminate: true`.
|
|
50
|
+
markPartialWrite, isPartialWrite, } from './src/errors.js';
|
|
47
51
|
/**
|
|
48
52
|
* Validación de identidad y de slugs (422). La aplica el manager en cada
|
|
49
53
|
* llamada y los drivers por defensa en profundidad; se exporta para que un
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAEtK,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAErD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,YAAY,GACb,MAAM,0BAA0B,CAAA;AAGjC;;;;;;;GAOG;AACH,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,gCAAgC,CAAA;AAYvC;;;GAGG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAE7D;;;;GAIG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAE1E,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAG1E;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAGtD;;;;;GAKG;AACH,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,yBAAyB,EACzB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|