@jantstack/adonis-authz 2.4.0-alpha.1 → 2.4.0-alpha.3
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 +299 -19
- package/build/commands/authz_relations_reconcile.d.ts +25 -1
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -1
- package/build/commands/authz_relations_reconcile.js +94 -6
- package/build/commands/authz_relations_reconcile.js.map +1 -1
- package/build/index.d.ts +13 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +15 -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 +18 -2
- package/build/providers/authz_provider.js.map +1 -1
- package/build/src/define_config.d.ts +73 -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_driver.d.ts +53 -14
- package/build/src/drivers/database_driver.d.ts.map +1 -1
- package/build/src/drivers/database_driver.js +104 -32
- package/build/src/drivers/database_driver.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +57 -9
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -1
- package/build/src/drivers/database_relations_driver.js +344 -89
- package/build/src/drivers/database_relations_driver.js.map +1 -1
- package/build/src/drivers/openfga_driver.d.ts +17 -4
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +38 -3
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_facts.d.ts +15 -0
- package/build/src/drivers/openfga_facts.d.ts.map +1 -1
- package/build/src/drivers/openfga_facts.js +25 -2
- package/build/src/drivers/openfga_facts.js.map +1 -1
- package/build/src/drivers/openfga_relations_driver.d.ts +40 -6
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_relations_driver.js +244 -44
- package/build/src/drivers/openfga_relations_driver.js.map +1 -1
- package/build/src/errors.d.ts +83 -4
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +139 -3
- package/build/src/errors.js.map +1 -1
- package/build/src/freeze.d.ts +37 -16
- package/build/src/freeze.d.ts.map +1 -1
- package/build/src/freeze.js +53 -8
- package/build/src/freeze.js.map +1 -1
- package/build/src/manager.d.ts +16 -1
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +116 -25
- package/build/src/manager.js.map +1 -1
- package/build/src/models/authz_assignment.d.ts +2 -2
- package/build/src/models/authz_assignment.d.ts.map +1 -1
- package/build/src/models/authz_deny.d.ts +2 -2
- package/build/src/models/authz_deny.d.ts.map +1 -1
- package/build/src/models/authz_permission.d.ts +2 -2
- package/build/src/models/authz_permission.d.ts.map +1 -1
- package/build/src/models/authz_role.d.ts +2 -2
- package/build/src/models/authz_role.d.ts.map +1 -1
- package/build/src/models/authz_role_permission.d.ts +2 -2
- package/build/src/models/authz_role_permission.d.ts.map +1 -1
- package/build/src/relation_partition_trigger.d.ts.map +1 -1
- package/build/src/relation_partition_trigger.js +11 -3
- package/build/src/relation_partition_trigger.js.map +1 -1
- package/build/src/relations/define_relations_config.d.ts +51 -0
- package/build/src/relations/define_relations_config.d.ts.map +1 -1
- package/build/src/relations/define_relations_config.js +68 -1
- package/build/src/relations/define_relations_config.js.map +1 -1
- package/build/src/relations/manager.d.ts +71 -6
- package/build/src/relations/manager.d.ts.map +1 -1
- package/build/src/relations/manager.js +301 -29
- package/build/src/relations/manager.js.map +1 -1
- package/build/src/relations/reconcile.d.ts +53 -4
- package/build/src/relations/reconcile.d.ts.map +1 -1
- package/build/src/relations/reconcile.js +92 -30
- package/build/src/relations/reconcile.js.map +1 -1
- package/build/src/scope_outbox.d.ts.map +1 -1
- package/build/src/scope_outbox.js +17 -10
- package/build/src/scope_outbox.js.map +1 -1
- package/build/src/shared/backend_guard.d.ts +28 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -1
- package/build/src/shared/backend_guard.js +52 -0
- package/build/src/shared/backend_guard.js.map +1 -1
- package/build/src/shared/transaction_guard.d.ts +50 -0
- package/build/src/shared/transaction_guard.d.ts.map +1 -0
- package/build/src/shared/transaction_guard.js +60 -0
- package/build/src/shared/transaction_guard.js.map +1 -0
- package/build/src/testing/contract.d.ts +55 -4
- package/build/src/testing/contract.d.ts.map +1 -1
- package/build/src/testing/contract.js +240 -8
- package/build/src/testing/contract.js.map +1 -1
- package/build/src/testing/main.d.ts +4 -4
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +2 -2
- package/build/src/testing/main.js.map +1 -1
- package/build/src/testing/relations_contract.d.ts +23 -1
- package/build/src/testing/relations_contract.d.ts.map +1 -1
- package/build/src/testing/relations_contract.js +603 -19
- package/build/src/testing/relations_contract.js.map +1 -1
- package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -1
- package/build/src/testing/relations_reconcile_contract.js +49 -1
- package/build/src/testing/relations_reconcile_contract.js.map +1 -1
- package/build/src/traits/has_uuid.d.ts +2 -2
- package/build/src/traits/has_uuid.d.ts.map +1 -1
- package/build/src/types.d.ts +284 -16
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +4 -0
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/authorization.stub +45 -1
- package/build/stubs/migration.stub +20 -10
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}
|
package/build/src/manager.js
CHANGED
|
@@ -4,7 +4,7 @@ import { v7 as uuidv7 } from 'uuid';
|
|
|
4
4
|
import { assertCatalogUuid, assertIdentity, assertScope, assertScopeType, assertSubject, assertValidSlug, chainKeysFrom, normalizeRoleQuery, scopeFromKey, scopeKey, scopeSpellings, } from './identity.js';
|
|
5
5
|
import { expiryChanged } from './expiry.js';
|
|
6
6
|
import { randomBytes } from 'node:crypto';
|
|
7
|
-
import { DEFAULT_FREEZE_LEASE_MS, acquireFreeze, freezeIsLive, freezeKindOf, readFreezeRow, releaseFreeze, renewFreeze, } from './freeze.js';
|
|
7
|
+
import { DEFAULT_FREEZE_LEASE_MS, acquireFreeze, freezeIsLive, freezeKindOf, readFreezeRow, assertNotFrozenRow, releaseFreeze, renewFreeze, } from './freeze.js';
|
|
8
8
|
import { assertKnownScope, isAuthzError, resolveChain, rootOnlyResolver } from './shared/backend_guard.js';
|
|
9
9
|
import { CatalogCache, GLOBAL_OWNER_KEY, invalidateAuthzCatalog, isRoleVisibleWith, readLocalRoles, withAuthzCatalogWrite } from './catalog/catalog_cache.js';
|
|
10
10
|
import { assertAssignableAt } from './catalog/catalog.js';
|
|
@@ -196,10 +196,65 @@ export class AuthorizationManager {
|
|
|
196
196
|
}
|
|
197
197
|
driver = driver.withClock(clock);
|
|
198
198
|
}
|
|
199
|
+
this.#assertTransactionalWritesRequired(driver);
|
|
199
200
|
this.#assertScopeDriftGuarded(driver);
|
|
200
201
|
this.#driver = driver;
|
|
201
202
|
return driver;
|
|
202
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* **Puerta 2 de `{ transaction }`** (L-2, opt-in): con
|
|
206
|
+
* `requireTransactionalWrites: true` el driver activo tiene que declarar
|
|
207
|
+
* `transactionalWrites: true` o el manager falla AL RESOLVERLO — antes de
|
|
208
|
+
* cachearlo, así que toda lectura, toda escritura y `driver()` fallan igual:
|
|
209
|
+
* el despliegue no arranca. Es la forma honesta de «fallar al construirse»:
|
|
210
|
+
* el roadmap lo pedía incondicional y eso haría `openfga` inconstruible en
|
|
211
|
+
* cualquier app que solo lo tenga registrado (y el driver se resuelve
|
|
212
|
+
* perezosamente y por nombre, así que al construir no se sabe si alguien
|
|
213
|
+
* pasará `{ transaction }`). Quien quiera fallar al arrancar, lo pide.
|
|
214
|
+
*/
|
|
215
|
+
#assertTransactionalWritesRequired(driver) {
|
|
216
|
+
if (this.#config.requireTransactionalWrites !== true)
|
|
217
|
+
return;
|
|
218
|
+
if (driver.capabilities?.transactionalWrites === true)
|
|
219
|
+
return;
|
|
220
|
+
throw new AuthorizationConfigError(`config.requireTransactionalWrites está en true y el driver '${this.#config.default}' declara ` +
|
|
221
|
+
`transactionalWrites: ${driver.capabilities ? String(driver.capabilities.transactionalWrites) : 'nada (sin capabilities)'}: ` +
|
|
222
|
+
`no puede inscribir grant/revoke/deny/removeDeny en la transacción del consumidor («los dos o ninguno»), así ` +
|
|
223
|
+
`que el manager no se resuelve. Usa un driver que la declare (database) o quita requireTransactionalWrites.`);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* **Puerta 1 de `{ transaction }`** (L-2, siempre activa): una escritura de
|
|
227
|
+
* HECHOS con `{ transaction }` a un driver que no declara
|
|
228
|
+
* `transactionalWrites: true` es 500 `E_AUTHZ_UNSUPPORTED` nombrando driver
|
|
229
|
+
* y operación, ANTES de la barrera, de la identidad y del driver (cero
|
|
230
|
+
* llamadas). Nunca se ignora, nunca un `logger.warn`: aceptarla y no
|
|
231
|
+
* cumplirla sería publicar con nuestra firma la fuga del cruce 4 · S5.
|
|
232
|
+
* `scopes.*` NO pasa por aquí: su `transaction` ENCOLA (encolar ≠ escribir).
|
|
233
|
+
*/
|
|
234
|
+
async #assertTransactionalWrite(options, operation) {
|
|
235
|
+
if (options?.transaction === undefined || options.transaction === null)
|
|
236
|
+
return;
|
|
237
|
+
const driver = await this.driver();
|
|
238
|
+
if (driver.capabilities?.transactionalWrites === true)
|
|
239
|
+
return;
|
|
240
|
+
throw UnsupportedOperationError.transactional(operation, this.#config.default, 'roles');
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* La marca del evento de una escritura inscrita en la transacción del
|
|
244
|
+
* llamante (L-3): `onWrite` se dispara cuando el DRIVER vuelve, y en ese
|
|
245
|
+
* momento la fila existe solo dentro de esa transacción — es un hecho si y
|
|
246
|
+
* solo si el llamante confirma, cosa que el paquete nunca ve. Quien audita
|
|
247
|
+
* lo necesita para no registrar como firme lo que un rollback deshace.
|
|
248
|
+
*/
|
|
249
|
+
#transactional(options) {
|
|
250
|
+
return options?.transaction === undefined || options.transaction === null ? {} : { transactional: true };
|
|
251
|
+
}
|
|
252
|
+
/** La API de delegación no admite `{ transaction }` (§1.4 del veredicto `{trx}`): 500 antes de tocar nada. */
|
|
253
|
+
#assertNoTransaction(options, operation) {
|
|
254
|
+
if (options?.transaction === undefined || options.transaction === null)
|
|
255
|
+
return;
|
|
256
|
+
throw UnsupportedOperationError.transactionalCatalog(operation);
|
|
257
|
+
}
|
|
203
258
|
/**
|
|
204
259
|
* **El gate de deriva del árbol, en el MANAGER** (3b-2e · E3; cierra el
|
|
205
260
|
* agujero que declaró el 3b-2d).
|
|
@@ -366,10 +421,16 @@ export class AuthorizationManager {
|
|
|
366
421
|
* A1.1/A1.3): si este manager ya sostiene el freeze, la ventana interior
|
|
367
422
|
* no toma otro ni lo levanta al salir — la exterior sigue en pie.
|
|
368
423
|
*/
|
|
369
|
-
async withFrozenWrites(reason, fn) {
|
|
370
|
-
|
|
424
|
+
async withFrozenWrites(reason, fn, options = {}) {
|
|
425
|
+
// L-1 · J1: una pasada que quiera correr DENTRO de la ventana del
|
|
426
|
+
// operador (el cutover) y publicar su `lapsed` —`authz:relations:reconcile`,
|
|
427
|
+
// que vive fuera de este manager— pide `kind: 'reconcile'` y
|
|
428
|
+
// `operatorAsContext: true`, lo mismo que hace `reconcile` de roles.
|
|
429
|
+
const context = await this.#durableFreezeContext(reason, options.kind ?? 'platform', {
|
|
430
|
+
operatorAsContext: options.operatorAsContext === true,
|
|
431
|
+
});
|
|
371
432
|
try {
|
|
372
|
-
return await fn();
|
|
433
|
+
return await fn({ fence: context.fence, leaseMs: context.leaseMs, lapsed: context.lapsed });
|
|
373
434
|
}
|
|
374
435
|
finally {
|
|
375
436
|
await context.release();
|
|
@@ -454,25 +515,23 @@ export class AuthorizationManager {
|
|
|
454
515
|
* freeze, y con `catalogRevalidate: { everyMs }` la fila del memo ni se
|
|
455
516
|
* lee.
|
|
456
517
|
*/
|
|
457
|
-
async #assertNotFrozen(operation
|
|
518
|
+
async #assertNotFrozen(operation) {
|
|
458
519
|
const root = this.#root();
|
|
459
520
|
const held = root.#heldFreeze;
|
|
460
521
|
if (held) {
|
|
461
522
|
throw new AuthorizationFrozenError(`${operation}: el motor de autorización está congelado (${held.reason}) y no acepta escrituras. ` +
|
|
462
523
|
`Las lecturas siguen funcionando; reintenta esta escritura cuando la operación termine.`);
|
|
463
524
|
}
|
|
464
|
-
//
|
|
465
|
-
// por
|
|
466
|
-
// un pool de 1
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
throw new AuthorizationFrozenError(`${operation}: el motor de autorización está congelado (${row.reason})${lift} y no acepta escrituras. ` +
|
|
475
|
-
`Las lecturas siguen funcionando; reintenta esta escritura cuando la ventana termine.`);
|
|
525
|
+
// La fila se lee SIEMPRE por la conexión del motor (L-1 · 🟠 8). Hasta
|
|
526
|
+
// L-1 se leía por la transacción del consumidor si la escritura llegaba
|
|
527
|
+
// con una —«para no interbloquear un pool de 1»—, y eso era el agujero:
|
|
528
|
+
// la barrera la decidía el snapshot del llamante (medido en SQLite, MySQL
|
|
529
|
+
// y PG). Con pool 1 el precio es un 503 con deadline, no un bypass.
|
|
530
|
+
await assertNotFrozenRow(operation, {
|
|
531
|
+
driver: this.#config.default,
|
|
532
|
+
nowMs: root.#wallMs(),
|
|
533
|
+
timeoutMs: this.#config.freezeTimeoutMs,
|
|
534
|
+
});
|
|
476
535
|
}
|
|
477
536
|
/** Solo tests: fuerza re-resolución del driver. */
|
|
478
537
|
clearCachedDriver() {
|
|
@@ -1163,7 +1222,10 @@ export class AuthorizationManager {
|
|
|
1163
1222
|
* `{}` si no hay actor: el evento no inventa autores).
|
|
1164
1223
|
*/
|
|
1165
1224
|
async #writeOptions(options, operation) {
|
|
1166
|
-
|
|
1225
|
+
// Sin cast sobre `transaction` (L-1 · 🟠 8): la barrera no lee nada del
|
|
1226
|
+
// llamante, y `transaction` solo lo consumen los tipos que lo declaran
|
|
1227
|
+
// (`ScopeTreeWriteOptions`, para ENCOLAR — nunca para decidir).
|
|
1228
|
+
await this.#assertNotFrozen(operation);
|
|
1167
1229
|
if (options?.actor !== undefined)
|
|
1168
1230
|
assertSubject(options.actor);
|
|
1169
1231
|
if (this.#config.requireActor === true && !options?.actor) {
|
|
@@ -1594,6 +1656,7 @@ export class AuthorizationManager {
|
|
|
1594
1656
|
* Devuelve el rol y notifica `role_defined`.
|
|
1595
1657
|
*/
|
|
1596
1658
|
async defineScopedRole(actor, ownerScope, spec, options) {
|
|
1659
|
+
this.#assertNoTransaction(options, 'defineScopedRole');
|
|
1597
1660
|
const who = await this.#requireActor(actor, 'defineScopedRole');
|
|
1598
1661
|
this.#assertOwnerScope(ownerScope, 'defineScopedRole');
|
|
1599
1662
|
const parsed = this.#parseScopedRoleSpec(spec);
|
|
@@ -1693,6 +1756,7 @@ export class AuthorizationManager {
|
|
|
1693
1756
|
* escribe ni notifica (idempotente). Notifica `role_updated`.
|
|
1694
1757
|
*/
|
|
1695
1758
|
async updateScopedRole(actor, roleUuid, changes, options) {
|
|
1759
|
+
this.#assertNoTransaction(options, 'updateScopedRole');
|
|
1696
1760
|
const who = await this.#requireActor(actor, 'updateScopedRole');
|
|
1697
1761
|
assertCatalogUuid('rol', roleUuid);
|
|
1698
1762
|
const parsed = this.#parseScopedRoleChanges(changes);
|
|
@@ -1786,6 +1850,7 @@ export class AuthorizationManager {
|
|
|
1786
1850
|
* su owner. Notifica `role_purged`. No necesita `listDenies`.
|
|
1787
1851
|
*/
|
|
1788
1852
|
async deleteScopedRole(actor, roleUuid, options) {
|
|
1853
|
+
this.#assertNoTransaction(options, 'deleteScopedRole');
|
|
1789
1854
|
const who = await this.#requireActor(actor, 'deleteScopedRole');
|
|
1790
1855
|
assertCatalogUuid('rol', roleUuid);
|
|
1791
1856
|
const driver = await this.driver();
|
|
@@ -2397,13 +2462,15 @@ export class AuthorizationManager {
|
|
|
2397
2462
|
* `E_AUTHZ_AMBIGUOUS_ROLE`, nunca «el más cercano gana»).
|
|
2398
2463
|
*/
|
|
2399
2464
|
async grant(subject, role, scope, options) {
|
|
2465
|
+
await this.#assertTransactionalWrite(options, 'grant');
|
|
2400
2466
|
const actor = await this.#writeOptions(options, 'grant');
|
|
2401
2467
|
assertIdentity({ subject, role, scope, expiresAt: options?.expiresAt });
|
|
2402
2468
|
await this.#assertWithin(scope, options, 'grant');
|
|
2403
2469
|
// 3E · Q7: el evento lleva el rol RESUELTO (uuid + slug + nivel + owner),
|
|
2404
2470
|
// no la pregunta cruda. Solo se resuelve si hay hook que lo vaya a leer.
|
|
2405
2471
|
const roles = await this.#resolvedRoles(role, scope, 'grant');
|
|
2406
|
-
const
|
|
2472
|
+
const transactional = this.#transactional(options);
|
|
2473
|
+
const outcome = (await this.#write({ action: 'granted', subject, scope, roles, expiresAt: options?.expiresAt ?? null, ...actor, ...transactional }, async () => (await this.driver()).grant(subject, role, scope, options))) ??
|
|
2407
2474
|
// Un driver de terceros que aún devuelva `void`: la firma promete un
|
|
2408
2475
|
// `GrantOutcome` y no miente (E1). Sin lectura previa no hay caducidad
|
|
2409
2476
|
// anterior que contar: es lo que pidió el llamante, y `existed: false`.
|
|
@@ -2419,6 +2486,7 @@ export class AuthorizationManager {
|
|
|
2419
2486
|
expiresAt: outcome.expiresAt,
|
|
2420
2487
|
previousExpiresAt: outcome.previousExpiresAt,
|
|
2421
2488
|
...actor,
|
|
2489
|
+
...transactional,
|
|
2422
2490
|
});
|
|
2423
2491
|
}
|
|
2424
2492
|
else {
|
|
@@ -2429,6 +2497,7 @@ export class AuthorizationManager {
|
|
|
2429
2497
|
roles,
|
|
2430
2498
|
expiresAt: outcome.expiresAt,
|
|
2431
2499
|
...actor,
|
|
2500
|
+
...transactional,
|
|
2432
2501
|
});
|
|
2433
2502
|
}
|
|
2434
2503
|
return outcome;
|
|
@@ -2440,27 +2509,38 @@ export class AuthorizationManager {
|
|
|
2440
2509
|
* rol.
|
|
2441
2510
|
*/
|
|
2442
2511
|
async revoke(subject, role, scope, options) {
|
|
2512
|
+
await this.#assertTransactionalWrite(options, 'revoke');
|
|
2443
2513
|
const actor = await this.#writeOptions(options, 'revoke');
|
|
2444
2514
|
assertIdentity({ subject, role, scope });
|
|
2445
2515
|
await this.#assertWithin(scope, options, 'revoke');
|
|
2446
|
-
const event = {
|
|
2447
|
-
|
|
2516
|
+
const event = {
|
|
2517
|
+
action: 'revoked',
|
|
2518
|
+
subject,
|
|
2519
|
+
scope,
|
|
2520
|
+
roles: await this.#resolvedRoles(role, scope, 'revoke'),
|
|
2521
|
+
...actor,
|
|
2522
|
+
...this.#transactional(options),
|
|
2523
|
+
};
|
|
2524
|
+
// L-2: las opciones viajan al driver (`transaction`, si la puerta la dejó pasar) — como en `grant`.
|
|
2525
|
+
await this.#write(event, async () => (await this.driver()).revoke(subject, role, scope, options));
|
|
2448
2526
|
await this.#notify(event);
|
|
2449
2527
|
}
|
|
2450
2528
|
async deny(subject, permission, scope, options) {
|
|
2529
|
+
await this.#assertTransactionalWrite(options, 'deny');
|
|
2451
2530
|
const actor = await this.#writeOptions(options, 'deny');
|
|
2452
2531
|
assertIdentity({ subject, permission, scope });
|
|
2453
2532
|
await this.#assertWithin(scope, options, 'deny');
|
|
2454
|
-
const event = { action: 'denied', subject, scope, permission, ...actor };
|
|
2455
|
-
await this.#write(event, async () => (await this.driver()).deny(subject, permission, scope));
|
|
2533
|
+
const event = { action: 'denied', subject, scope, permission, ...actor, ...this.#transactional(options) };
|
|
2534
|
+
await this.#write(event, async () => (await this.driver()).deny(subject, permission, scope, options));
|
|
2456
2535
|
await this.#notify(event);
|
|
2457
2536
|
}
|
|
2458
2537
|
async removeDeny(subject, permission, scope, options) {
|
|
2538
|
+
await this.#assertTransactionalWrite(options, 'removeDeny');
|
|
2459
2539
|
const actor = await this.#writeOptions(options, 'removeDeny');
|
|
2460
2540
|
assertIdentity({ subject, permission, scope });
|
|
2461
2541
|
await this.#assertWithin(scope, options, 'removeDeny');
|
|
2462
|
-
const event = { action: 'deny_removed', subject, scope, permission, ...actor };
|
|
2463
|
-
await this.#write(event, async () => (await this.driver()).removeDeny(subject, permission, scope));
|
|
2542
|
+
const event = { action: 'deny_removed', subject, scope, permission, ...actor, ...this.#transactional(options) };
|
|
2543
|
+
await this.#write(event, async () => (await this.driver()).removeDeny(subject, permission, scope, options));
|
|
2464
2544
|
await this.#notify(event);
|
|
2465
2545
|
}
|
|
2466
2546
|
/**
|
|
@@ -2711,6 +2791,17 @@ export class AuthorizationManager {
|
|
|
2711
2791
|
* `indeterminate: true`, para que quien audita registre "puede haber
|
|
2712
2792
|
* ocurrido" en vez de nada. Cualquier otro fallo (422, conexión rechazada)
|
|
2713
2793
|
* significa que la escritura no ocurrió y se propaga sin evento.
|
|
2794
|
+
*
|
|
2795
|
+
* **Dentro de la transacción del llamante (L-3) el deadline sigue siendo
|
|
2796
|
+
* `indeterminate: true`**, y el evento lleva además `transactional: true`.
|
|
2797
|
+
* El auditor del panel `{trx}` (🟡 12) objetó que ahí «el rollback la
|
|
2798
|
+
* determina»; pero lo que determina el rollback lo determina el LLAMANTE
|
|
2799
|
+
* y el paquete no lo ve: en el instante del 503 la sentencia puede haber
|
|
2800
|
+
* aterrizado en la transacción (SQLite no cancela; en PG la transacción
|
|
2801
|
+
* queda abortada y ya no puede confirmar; en MySQL la sentencia se mata y
|
|
2802
|
+
* la transacción sigue viva) y el llamante puede confirmar o no. Lo
|
|
2803
|
+
* honesto es el mismo «no lo sé» de siempre más la marca de que la última
|
|
2804
|
+
* palabra es su commit. Medido por motor en `database_transaction.spec.ts`.
|
|
2714
2805
|
*/
|
|
2715
2806
|
async #write(event, fn) {
|
|
2716
2807
|
try {
|