@jantstack/adonis-authz 1.0.2 → 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +417 -46
  3. package/build/commands/authz_catalog_diff.d.ts +28 -0
  4. package/build/commands/authz_catalog_diff.d.ts.map +1 -0
  5. package/build/commands/authz_catalog_diff.js +67 -0
  6. package/build/commands/authz_catalog_diff.js.map +1 -0
  7. package/build/commands/authz_catalog_sync.d.ts +20 -0
  8. package/build/commands/authz_catalog_sync.d.ts.map +1 -0
  9. package/build/commands/authz_catalog_sync.js +58 -0
  10. package/build/commands/authz_catalog_sync.js.map +1 -0
  11. package/build/commands/main.d.ts +2 -0
  12. package/build/commands/main.d.ts.map +1 -1
  13. package/build/commands/main.js +2 -0
  14. package/build/commands/main.js.map +1 -1
  15. package/build/commands/openfga_import.d.ts +8 -2
  16. package/build/commands/openfga_import.d.ts.map +1 -1
  17. package/build/commands/openfga_import.js +29 -6
  18. package/build/commands/openfga_import.js.map +1 -1
  19. package/build/commands/openfga_provision.d.ts.map +1 -1
  20. package/build/commands/openfga_provision.js +2 -1
  21. package/build/commands/openfga_provision.js.map +1 -1
  22. package/build/index.d.ts +51 -5
  23. package/build/index.d.ts.map +1 -1
  24. package/build/index.js +43 -3
  25. package/build/index.js.map +1 -1
  26. package/build/src/catalog.d.ts +249 -2
  27. package/build/src/catalog.d.ts.map +1 -1
  28. package/build/src/catalog.js +709 -59
  29. package/build/src/catalog.js.map +1 -1
  30. package/build/src/catalog_cache.d.ts +300 -0
  31. package/build/src/catalog_cache.d.ts.map +1 -0
  32. package/build/src/catalog_cache.js +656 -0
  33. package/build/src/catalog_cache.js.map +1 -0
  34. package/build/src/clock.d.ts +24 -0
  35. package/build/src/clock.d.ts.map +1 -0
  36. package/build/src/clock.js +7 -0
  37. package/build/src/clock.js.map +1 -0
  38. package/build/src/define_config.d.ts +102 -6
  39. package/build/src/define_config.d.ts.map +1 -1
  40. package/build/src/define_config.js.map +1 -1
  41. package/build/src/drivers/backend_guard.d.ts +92 -0
  42. package/build/src/drivers/backend_guard.d.ts.map +1 -0
  43. package/build/src/drivers/backend_guard.js +221 -0
  44. package/build/src/drivers/backend_guard.js.map +1 -0
  45. package/build/src/drivers/database_driver.d.ts +242 -15
  46. package/build/src/drivers/database_driver.d.ts.map +1 -1
  47. package/build/src/drivers/database_driver.js +692 -127
  48. package/build/src/drivers/database_driver.js.map +1 -1
  49. package/build/src/drivers/openfga_driver.d.ts +341 -27
  50. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  51. package/build/src/drivers/openfga_driver.js +1052 -264
  52. package/build/src/drivers/openfga_driver.js.map +1 -1
  53. package/build/src/drivers/sql_expiry.d.ts +53 -0
  54. package/build/src/drivers/sql_expiry.d.ts.map +1 -0
  55. package/build/src/drivers/sql_expiry.js +66 -0
  56. package/build/src/drivers/sql_expiry.js.map +1 -0
  57. package/build/src/errors.d.ts +366 -0
  58. package/build/src/errors.d.ts.map +1 -0
  59. package/build/src/errors.js +387 -0
  60. package/build/src/errors.js.map +1 -0
  61. package/build/src/expiry.d.ts +27 -0
  62. package/build/src/expiry.d.ts.map +1 -0
  63. package/build/src/expiry.js +50 -0
  64. package/build/src/expiry.js.map +1 -0
  65. package/build/src/hierarchical_resolver.d.ts +56 -0
  66. package/build/src/hierarchical_resolver.d.ts.map +1 -0
  67. package/build/src/hierarchical_resolver.js +87 -0
  68. package/build/src/hierarchical_resolver.js.map +1 -0
  69. package/build/src/identity.d.ts +155 -0
  70. package/build/src/identity.d.ts.map +1 -0
  71. package/build/src/identity.js +359 -0
  72. package/build/src/identity.js.map +1 -0
  73. package/build/src/manager.d.ts +225 -7
  74. package/build/src/manager.d.ts.map +1 -1
  75. package/build/src/manager.js +1670 -23
  76. package/build/src/manager.js.map +1 -1
  77. package/build/src/memoize_ancestors.d.ts +23 -0
  78. package/build/src/memoize_ancestors.d.ts.map +1 -0
  79. package/build/src/memoize_ancestors.js +42 -0
  80. package/build/src/memoize_ancestors.js.map +1 -0
  81. package/build/src/middleware/app_access_middleware.d.ts +8 -6
  82. package/build/src/middleware/app_access_middleware.d.ts.map +1 -1
  83. package/build/src/middleware/app_access_middleware.js +10 -26
  84. package/build/src/middleware/app_access_middleware.js.map +1 -1
  85. package/build/src/models/authz_assignment.d.ts +5 -5
  86. package/build/src/models/authz_assignment.d.ts.map +1 -1
  87. package/build/src/models/authz_deny.d.ts +5 -5
  88. package/build/src/models/authz_deny.d.ts.map +1 -1
  89. package/build/src/models/authz_permission.d.ts +11 -5
  90. package/build/src/models/authz_permission.d.ts.map +1 -1
  91. package/build/src/models/authz_permission.js +4 -0
  92. package/build/src/models/authz_permission.js.map +1 -1
  93. package/build/src/models/authz_role.d.ts +13 -6
  94. package/build/src/models/authz_role.d.ts.map +1 -1
  95. package/build/src/models/authz_role.js +6 -1
  96. package/build/src/models/authz_role.js.map +1 -1
  97. package/build/src/models/authz_role_permission.d.ts +5 -5
  98. package/build/src/models/authz_role_permission.d.ts.map +1 -1
  99. package/build/src/openfga.d.ts +13 -0
  100. package/build/src/openfga.d.ts.map +1 -0
  101. package/build/src/openfga.js +12 -0
  102. package/build/src/openfga.js.map +1 -0
  103. package/build/src/sql_descendants.d.ts +51 -0
  104. package/build/src/sql_descendants.d.ts.map +1 -0
  105. package/build/src/sql_descendants.js +129 -0
  106. package/build/src/sql_descendants.js.map +1 -0
  107. package/build/src/testing/contract.d.ts +138 -7
  108. package/build/src/testing/contract.d.ts.map +1 -1
  109. package/build/src/testing/contract.js +2946 -24
  110. package/build/src/testing/contract.js.map +1 -1
  111. package/build/src/testing/main.d.ts +4 -2
  112. package/build/src/testing/main.d.ts.map +1 -1
  113. package/build/src/testing/main.js +2 -1
  114. package/build/src/testing/main.js.map +1 -1
  115. package/build/src/testing/scope_tree.d.ts +65 -0
  116. package/build/src/testing/scope_tree.d.ts.map +1 -0
  117. package/build/src/testing/scope_tree.js +145 -0
  118. package/build/src/testing/scope_tree.js.map +1 -0
  119. package/build/src/traits/authz_scopes.d.ts +30 -6
  120. package/build/src/traits/authz_scopes.d.ts.map +1 -1
  121. package/build/src/traits/authz_scopes.js +30 -18
  122. package/build/src/traits/authz_scopes.js.map +1 -1
  123. package/build/src/traits/has_uuid.d.ts +5 -5
  124. package/build/src/traits/has_uuid.d.ts.map +1 -1
  125. package/build/src/types.d.ts +530 -28
  126. package/build/src/types.d.ts.map +1 -1
  127. package/build/src/types.js +10 -4
  128. package/build/src/types.js.map +1 -1
  129. package/build/stubs/config/app_acl.stub +4 -2
  130. package/build/stubs/config/authorization.stub +66 -12
  131. package/build/stubs/migration.stub +57 -13
  132. package/package.json +11 -6
@@ -1,5 +1,50 @@
1
1
  import type { AuthorizationConfig } from './define_config.js';
2
- import type { AuthorizationDriver, GrantOptions, ScopeRef, ScopeType, SubjectRef } from './types.js';
2
+ import type { AuthorizationDriver, AuthorizedScopes, CatalogRole, DenyOptions, DenyRef, ExcludedSubtree, GrantOptions, GrantOutcome, RoleQuery, ScopeDetachOutcome, ScopedRoleChanges, ScopedRoleSpec, ScopedWriteOptions, ScopeRef, ScopeType, SubjectRef } from './types.js';
3
+ /**
4
+ * Lo que devuelve `AuthorizationManager.forRequest()`: la misma API que el
5
+ * manager (lecturas, escrituras, `scopes.*`), con los ancestros memoizados
6
+ * SOLO en las lecturas. Es un tipo, no otra clase: la vista es un manager
7
+ * hijo que comparte config, driver y hooks con su padre.
8
+ */
9
+ export type AuthorizationView = Pick<AuthorizationManager, 'authorize' | 'hasRole' | 'listSubjects' | 'listScopes' | 'listRoles' | 'listRoleScopes' | 'grant' | 'revoke' | 'deny' | 'removeDeny' | 'scopes' | 'driver' | 'forRequest' | 'isWithin' | 'listDenies' | 'effectivePermissions' | 'authorizeMany' | 'authorizedScopes' | 'expandExcludedSubtrees' | 'defineScopedRole' | 'updateScopedRole' | 'deleteScopedRole'>;
10
+ /**
11
+ * Expande los `excludedSubtrees` de un `all` (2D · F10) a la lista plana de
12
+ * scopes que hay que restar: cada scope denegado y TODOS sus descendientes,
13
+ * con el `descendantsOf` del config. Es la forma correcta del `NOT IN`;
14
+ * restar solo los scopes con deny dejaría dentro sus subárboles.
15
+ */
16
+ export declare function expandExcludedSubtrees(view: AuthorizationView, excluded: ExcludedSubtree[]): Promise<ScopeRef[]>;
17
+ /** Cotas por defecto de `authorizedScopes` (config `scopes.maxScopes` / `scopes.maxDescendants`). */
18
+ export declare const DEFAULT_MAX_SCOPES = 1000;
19
+ export declare const DEFAULT_MAX_DESCENDANTS = 10000;
20
+ /**
21
+ * Tope sano de `maxScopes`/`maxDescendants` (2.5-B, auditor ⚪6): por encima
22
+ * de ~4,29e9 el hint `SET_VAR(cte_max_recursion_depth)` de MySQL sale de
23
+ * rango y un ciclo en la tabla deja de ser el 422 «posible ciclo» del
24
+ * contrato (503); y ya con 1e6 un ciclo cuesta segundos de CPU por llamada.
25
+ * Una cota mayor es config rota (500), nunca una pregunta.
26
+ */
27
+ export declare const MAX_SCOPE_BOUND = 10000000;
28
+ /** Vida por defecto de una vista de `forRequest()` para LEER (F9): un request, no un módulo. */
29
+ export declare const DEFAULT_VIEW_MAX_AGE_MS = 30000;
30
+ export interface ForRequestOptions {
31
+ /**
32
+ * Milisegundos durante los que la vista puede LEER (default 30 000).
33
+ * Después, cualquier lectura es 500 `E_AUTHZ_VIEW_EXPIRED`: el memo de
34
+ * ancestros solo es correcto mientras dura el request y una vista guardada
35
+ * por error serviría la cadena vieja para siempre. `0` = sin límite, a
36
+ * sabiendas. Las escrituras e `isWithin` no caducan (resuelven en fresco).
37
+ * Se mide con un reloj MONÓTONO (`performance.now()`, 2E · H3): un salto
38
+ * del reloj de pared hacia atrás no resucita una vista caducada.
39
+ */
40
+ maxAgeMs?: number;
41
+ /**
42
+ * SOLO TESTS: fuente del reloj monótono, en ms (default `performance.now`).
43
+ * Sirve para fijar la frontera exacta de `maxAgeMs` sin dormir; en
44
+ * producción no se toca.
45
+ */
46
+ now?: () => number;
47
+ }
3
48
  /**
4
49
  * Manager de autorización — la fachada que usan middleware, services y
5
50
  * seeders. Resuelve el driver activo del config y notifica cada escritura
@@ -12,18 +57,191 @@ import type { AuthorizationDriver, GrantOptions, ScopeRef, ScopeType, SubjectRef
12
57
  export declare class AuthorizationManager {
13
58
  #private;
14
59
  constructor(config: AuthorizationConfig);
60
+ /**
61
+ * Vista por request (2A/A3): las LECTURAS (`authorize`, `hasRole`, `list*`)
62
+ * resuelven ancestros con `memoizeAncestors(config.scopes.resolveChain)`
63
+ * —una llamada al árbol por scope durante la vida de la vista—; las
64
+ * ESCRITURAS (`grant`, `revoke`, `deny`, `removeDeny`, `scopes.*`) siguen
65
+ * resolviendo en fresco, porque una lectura obsoleta caduca sola y un
66
+ * grant sobre una cadena que ya cambió queda escrito para siempre (auditor
67
+ * C3/E3). El memo es de ANCESTROS, nunca de decisiones: un deny escrito
68
+ * entre dos `authorize` de la misma vista cambia la segunda respuesta.
69
+ *
70
+ * Patrón en Adonis: un middleware hace `ctx.authz = authorization.forRequest()`
71
+ * y controladores y policies leen de `ctx.authz`. Sin `AsyncLocalStorage`:
72
+ * la vista es un objeto explícito con la vida que le des. Sin
73
+ * `config.scopes.resolveChain`, o con un driver de terceros sin
74
+ * `withChainResolver`, la vista lee con el driver tal cual (sin memo)
75
+ * y sigue siendo correcta.
76
+ */
77
+ forRequest(options?: ForRequestOptions): AuthorizationView;
78
+ /**
79
+ * El driver activo, TAL CUAL. Es la salida explícita de las barreras del
80
+ * manager (2D · G4, auditor 8): lo que escribas por aquí no pasa por
81
+ * `actor`/`requireActor`, `within`/`requireWithin` ni `onWrite`, y lo que
82
+ * leas no pasa por el memo de ancestros. Está pensado para el código de
83
+ * PLATAFORMA (seeders, comandos, la escritura en la raíz con
84
+ * `requireWithin: 'non-root'`) y para tests; un call-site de tenant nunca
85
+ * debería llamarlo. No se ofrece nada más por aquí a propósito.
86
+ *
87
+ * Lo único que el manager le aplica al resolverlo es el reloj del config
88
+ * (`clock`, 2.5 · J1) vía `withClock`: no es una barrera, es la hora con
89
+ * la que el driver decide, y vale igual para la plataforma, los tests y
90
+ * cada vista de `forRequest()` (todas leen el driver del padre). Un driver
91
+ * sin `withClock` con `clock` declarado es 500 `E_AUTHZ_CONFIG`.
92
+ */
15
93
  driver(): Promise<AuthorizationDriver>;
16
94
  /** Solo tests: fuerza re-resolución del driver. */
17
95
  clearCachedDriver(): void;
96
+ /**
97
+ * El árbol de scopes es un hecho del contrato: el consumidor notifica sus
98
+ * cambios aquí, en TODOS los drivers, y el PAQUETE valida antes de tocar
99
+ * el driver — la raíz no cuelga de nada, el padre tiene que existir y no
100
+ * puede haber ciclos. FGA acepta un ciclo de `parent` y lo evalúa (un grant
101
+ * en cualquier nodo concede en la raíz, S2), así que la barrera es esta.
102
+ * Espía: si la validación falla, cero llamadas al driver.
103
+ */
104
+ readonly scopes: {
105
+ attached: (child: ScopeRef, parent: ScopeRef, options?: ScopedWriteOptions) => Promise<void>;
106
+ moved: (child: ScopeRef, newParent: ScopeRef, options?: ScopedWriteOptions) => Promise<void>;
107
+ /**
108
+ * Hechos primero (el driver demuestra cero o lanza), arista después
109
+ * (S6): si la purga muere a medias, el subárbol sigue colgado y los
110
+ * denies heredados siguen valiendo. Sin `within` no comprueba que el
111
+ * scope exista (el consumidor puede haber borrado ya su fila); con
112
+ * `within` (2D · F2) el hijo tiene que seguir en el árbol para
113
+ * contrastar su cadena: purga ANTES de borrar la fila.
114
+ */
115
+ detached: (child: ScopeRef, options?: ScopedWriteOptions) => Promise<ScopeDetachOutcome>;
116
+ };
117
+ /**
118
+ * ¿`outer` contiene a `inner`? = `outer ∈ chain(inner)`, inclusive: un scope
119
+ * se contiene a sí mismo y `APP_SCOPE` contiene todo. Un `inner` que el
120
+ * árbol no conoce no está dentro de nada (`false`). Siempre con el resolutor
121
+ * fresco (nunca el memo por request): la contención decide escrituras.
122
+ */
123
+ isWithin(inner: ScopeRef, outer: ScopeRef): Promise<boolean>;
18
124
  authorize(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<boolean>;
19
- hasRole(subject: SubjectRef, role: string, scope: ScopeRef): Promise<boolean>;
20
- listSubjects(role: string, scope: ScopeRef): Promise<SubjectRef[]>;
125
+ hasRole(subject: SubjectRef, role: RoleQuery, scope: ScopeRef): Promise<boolean>;
126
+ /**
127
+ * `authorize` sobre varios scopes, un booleano por posición (2.1, B6).
128
+ * Delegado al driver si trae `authorizeMany` (openfga: un batchCheck);
129
+ * si no, `Promise.all` de `authorize` sobre una vista con los ancestros
130
+ * memoizados (una llamada al árbol por scope distinto, aunque se repita).
131
+ * Idéntico a N `authorize`: duplicados por posición, desconocido ⇒ false,
132
+ * y si una posición no se puede responder, lanza entero. Vacío ⇒ `[]`
133
+ * sin tocar backend ni árbol.
134
+ */
135
+ authorizeMany(subject: SubjectRef, permission: string, scopes: ScopeRef[]): Promise<boolean[]>;
136
+ /** Holders con asignación vigente del rol en ese scope exacto. `{ uuid }` es la forma exacta (3D · M1). */
137
+ listSubjects(role: RoleQuery, scope: ScopeRef): Promise<SubjectRef[]>;
21
138
  listScopes(subject: SubjectRef, permission: string): Promise<ScopeRef[]>;
22
139
  listRoles(subject: SubjectRef, scope: ScopeRef): Promise<string[]>;
23
140
  listRoleScopes(subject: SubjectRef, scopeType: ScopeType): Promise<ScopeRef[]>;
24
- grant(subject: SubjectRef, role: string, scope: ScopeRef, options?: GrantOptions): Promise<void>;
25
- revoke(subject: SubjectRef, role: string, scope: ScopeRef): Promise<void>;
26
- deny(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<void>;
27
- removeDeny(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<void>;
141
+ /** Denies directos del holder (scope exacto, o todos). 500 `E_AUTHZ_UNSUPPORTED` si el driver no lo implementa. */
142
+ listDenies(subject: SubjectRef, scope?: ScopeRef): Promise<DenyRef[]>;
143
+ /**
144
+ * Permisos efectivos del holder en un scope (2.1, B5): la unión de lo que
145
+ * conceden sus roles vigentes en toda la cadena (`listRoles` por nivel +
146
+ * catálogo) MENOS lo denegado en cualquier nivel de la cadena
147
+ * (`listDenies` por nivel). Es exactamente el conjunto `{ p | authorize(p) }`,
148
+ * calculado sin preguntar permiso a permiso. Scope desconocido ⇒ `[]`.
149
+ * Prerrequisito de `catalog/` (Fase 3).
150
+ */
151
+ effectivePermissions(subject: SubjectRef, scope: ScopeRef): Promise<string[]>;
152
+ /**
153
+ * Define un rol LOCAL a `ownerScope`. Policy, en este orden y antes de
154
+ * escribir nada: `actor` obligatorio y bien formado; `ownerScope` válido,
155
+ * no la raíz (los roles de la raíz son globales: config + sync) y conocido
156
+ * por el árbol (fresco); `spec` bien formado (slug, nivel ≠ `app`, rank
157
+ * entero, permisos); cada permiso en `config.delegablePermissions`, en el
158
+ * catálogo, componible en ese nivel (`assignableAt`, B5) y EFECTIVO para
159
+ * el actor en el owner (lo concede un rol suyo de la cadena y no lo tiene
160
+ * denegado en ella — C2); `0 < rank < min(rank del actor, rank máximo
161
+ * global)`; y ningún rol `(slug, scopeType)` visible en el owner (global,
162
+ * o local a un ancestro) ni local a un descendiente (colisión, 422
163
+ * `E_AUTHZ_CATALOG_CONFLICT`, re-comprobada dentro de la transacción
164
+ * serializada — 3D · M2). `options.within` contiene la escritura contra el
165
+ * OWNER y `requireWithin` la exige, como en las otras ocho (3D · M3).
166
+ * Devuelve el rol y notifica `role_defined`.
167
+ */
168
+ defineScopedRole(actor: SubjectRef, ownerScope: ScopeRef, spec: ScopedRoleSpec, options?: ScopedWriteOptions): Promise<CatalogRole>;
169
+ /**
170
+ * Cambia `name`/`description`/`rank`/`permissions` de un rol LOCAL (nunca
171
+ * su slug, nivel ni owner). Un global es 422 `E_AUTHZ_ROLE_IMMUTABLE`. El
172
+ * actor tiene que tener, en el owner del rol, rank MAYOR que el del rol
173
+ * (no se toca un rol de rango ≥ al propio) y la misma policy que al
174
+ * definir para lo que cambia: los permisos nuevos delegables/efectivos y
175
+ * componibles, el rank nuevo por debajo del suyo. Sin cambios reales no
176
+ * escribe ni notifica (idempotente). Notifica `role_updated`.
177
+ */
178
+ updateScopedRole(actor: SubjectRef, roleUuid: string, changes: ScopedRoleChanges, options?: ScopedWriteOptions): Promise<CatalogRole>;
179
+ /**
180
+ * Purga un rol LOCAL: sus asignaciones en todos los scopes, sus vínculos y
181
+ * el rol (`driver.purgeRole`, B4; 500 `E_AUTHZ_UNSUPPORTED` en un driver
182
+ * que no lo trae, sin tocar nada). Un global es 422
183
+ * `E_AUTHZ_ROLE_IMMUTABLE`; el actor necesita rank MAYOR que el del rol en
184
+ * su owner. Notifica `role_purged`. No necesita `listDenies`.
185
+ */
186
+ deleteScopedRole(actor: SubjectRef, roleUuid: string, options?: ScopedWriteOptions): Promise<void>;
187
+ /**
188
+ * Asigna un rol al holder en un scope. `role` es un `RoleQuery` (3D · M1):
189
+ * un slug, `{ slug, scopeType }` o `{ uuid }` — esta última es la forma
190
+ * exacta, la única que responde cuando dos roles locales homónimos son
191
+ * visibles en la misma cadena (las otras dos son 422
192
+ * `E_AUTHZ_AMBIGUOUS_ROLE`, nunca «el más cercano gana»).
193
+ */
194
+ grant(subject: SubjectRef, role: RoleQuery, scope: ScopeRef, options?: GrantOptions): Promise<GrantOutcome>;
195
+ /**
196
+ * Quita la asignación del rol en ese scope exacto. Por slug se quitan las
197
+ * de TODOS los homónimos `(slug, nivel)` (3B; quitar nunca concede, y el
198
+ * scope puede no existir ya para el árbol); por `{ uuid }`, solo la de ese
199
+ * rol.
200
+ */
201
+ revoke(subject: SubjectRef, role: RoleQuery, scope: ScopeRef, options?: ScopedWriteOptions): Promise<void>;
202
+ deny(subject: SubjectRef, permission: string, scope: ScopeRef, options?: DenyOptions): Promise<void>;
203
+ removeDeny(subject: SubjectRef, permission: string, scope: ScopeRef, options?: ScopedWriteOptions): Promise<void>;
204
+ /**
205
+ * Scopes de un tipo donde el holder tiene el permiso (2.1, B3). La ÚNICA
206
+ * API del paquete que enumera descendientes — excepción explícita al
207
+ * invariante 7 (`list*` siguen siendo directos) — y lo hace con el
208
+ * `descendantsOf` del consumidor, nunca con N+1 `resolveChain` a ciegas.
209
+ *
210
+ * Regla:
211
+ * 1. `listScopes(subject, permission)`: los scopes DIRECTOS que conceden,
212
+ * ya sin los bloqueados por un deny en su cadena y sin los que el árbol
213
+ * no conoce. Vacío ⇒ `none`.
214
+ * 2. Si la raíz está entre ellos ⇒ `all`, con `excludedSubtrees` = todos
215
+ * los scopes con deny vivo del permiso (`listDenies`), como subárboles
216
+ * (F10). Nunca `all` sin esa lista (juez cruce 5): un deny vivo tiene
217
+ * que verse.
218
+ * 3. Si no: candidatos = directos ∪ sus descendientes (`descendantsOf`).
219
+ * Cada candidato se contrasta con `resolveChain` (memoizado por
220
+ * request, F3): su cadena tiene que contener el scope concedente —si
221
+ * no, los dos resolutores del consumidor describen árboles distintos y
222
+ * se lanza 503 `E_AUTHZ_RESOLVER_FAILED`, nunca una lista con cruces—
223
+ * y no puede contener un scope denegado (es EXACTAMENTE la regla de
224
+ * `authorize`: deny en la cadena ⇒ false). Se filtran por `scopeType`.
225
+ * 4. Más de `maxScopes` ⇒ 422 `E_AUTHZ_TOO_MANY_SCOPES`, nunca parcial, y
226
+ * se corta en cuanto se sabe (F8): los directos del tipo antes de bajar
227
+ * y el conteo del tipo dentro del bucle. `options.maxScopes` solo puede
228
+ * BAJAR la cota del config.
229
+ * Sin `scopes.descendantsOf` ⇒ 500 `E_AUTHZ_NO_DESCENDANTS_RESOLVER`
230
+ * (antes de mirar nada: un `none` sin árbol sería mentira).
231
+ */
232
+ authorizedScopes(subject: SubjectRef, permission: string, scopeType: ScopeType, options?: {
233
+ maxScopes?: number;
234
+ }): Promise<AuthorizedScopes>;
235
+ /**
236
+ * Los `excludedSubtrees` de un `all` (F10) expandidos: cada scope denegado
237
+ * y todos sus descendientes, con el `descendantsOf` del config. Un scope
238
+ * que `descendantsOf` no conoce (`null`) es 503: restarlo a medias
239
+ * dejaría su subárbol dentro (fail-open). Cotas: `maxDescendants` por
240
+ * subárbol y `maxScopes` (config o por llamada, nunca por encima del
241
+ * config) sobre el total ⇒ 422, nunca parcial.
242
+ */
243
+ expandExcludedSubtrees(excluded: ExcludedSubtree[], options?: {
244
+ maxScopes?: number;
245
+ }): Promise<ScopeRef[]>;
28
246
  }
29
247
  //# sourceMappingURL=manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,KAAK,EACV,mBAAmB,EAGnB,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,UAAU,EACX,MAAM,YAAY,CAAA;AAEnB;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAInB,MAAM,EAAE,mBAAmB;IAIjC,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAgB5C,mDAAmD;IACnD,iBAAiB,IAAI,IAAI;IAInB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrF,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAI7E,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAIlE,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAIxE,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIlE,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAI9E,KAAK,CACT,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC;IAWV,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzE,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7E,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CA+B1F"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}