@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
@@ -0,0 +1,155 @@
1
+ import type { NormalizedRoleQuery, RoleQuery, ScopeRef, SubjectRef } from './types.js';
2
+ /**
3
+ * Validación de identidad del motor: holder, scope, rol y permiso.
4
+ *
5
+ * Vive en un solo sitio y la aplican el manager (una vez por llamada) Y los
6
+ * drivers (defensa en profundidad: el juez del contrato habla con el driver
7
+ * directamente, y un driver de terceros puede usarse sin manager). Nadie
8
+ * duplica la regla: se importa.
9
+ *
10
+ * Por qué es 422 y no "se acepta lo que venga":
11
+ * - `uuid: undefined` en el driver openfga escribía `user:undefined`, y a
12
+ * partir de ahí CUALQUIER holder sin uuid heredaba ese permiso (L0.5).
13
+ * - `{ type: 'app', uuid: 'X' }` colapsaba a la raíz global en openfga y no
14
+ * en database: escalada de tenant a plataforma en un driver y `false` en
15
+ * el otro (L0.10).
16
+ * - `#`, `:`, `|`, `~`, `*` y espacios son sintaxis de FGA o separadores de
17
+ * los ids de binding: un componente que los contenga puede fabricar un
18
+ * userset, un comodín o la clave de OTRO scope (L0.5, L0.8). Desde 3A
19
+ * (2.2) el slug ya NO viaja en los ids de FGA (van por uuid de catálogo),
20
+ * pero los tipos y uuids de scope sí, y los slugs serán relaciones del
21
+ * modelo `facts` (3b): la lista blanca se mantiene entera.
22
+ * - Las longitudes son las de las columnas `authz_*`: lo que no cabe se
23
+ * truncaría o fallaría con un error de SQL ilegible.
24
+ */
25
+ /**
26
+ * Longitudes de columna del esquema publicado (`stubs/migration.stub`) que
27
+ * la identidad aplica tal cual. El slug NO está aquí: su límite es
28
+ * `MAX_SLUG_LENGTH` (42), más estricto que la columna (100) para que el
29
+ * catálogo sea publicable en FGA (E2).
30
+ */
31
+ export declare const IDENTITY_LIMITS: Readonly<{
32
+ holderType: 50;
33
+ scopeType: 20;
34
+ uuid: 36;
35
+ }>;
36
+ /**
37
+ * Centinela con el que el driver `database` almacena el uuid del scope `app`
38
+ * (NOT NULL + unique). Fuera de `app` sería una identidad de scope que
39
+ * colisiona con la raíz: se rechaza (L0.15).
40
+ */
41
+ export declare const SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
42
+ /** El uuid de un rol o permiso del catálogo (opcional en el spec): canónico y en minúsculas, o 422. */
43
+ export declare function assertCatalogUuid(kind: string, value: unknown): asserts value is string;
44
+ /** Versión booleana para caminos de LECTURA (ids que vienen del backend): no lanza. */
45
+ export declare function isCatalogUuid(value: unknown): value is string;
46
+ /** `SubjectRef` bien formado: type y uuid presentes, con formato y longitud. */
47
+ export declare function assertSubject(subject: SubjectRef): void;
48
+ /**
49
+ * `ScopeRef` bien formado. `app` es la raíz y NO admite uuid (L0.10): un
50
+ * `{ app, 'X' }` no es "otro app", es una identidad que un driver colapsaría
51
+ * a la global. Fuera de `app` el uuid es obligatorio y no puede ser el
52
+ * centinela con el que `database` representa la raíz (L0.15).
53
+ */
54
+ export declare function assertScope(scope: ScopeRef): void;
55
+ /** Tipo de scope suelto (el de `listRoleScopes`). */
56
+ export declare function assertScopeType(scopeType: string): void;
57
+ /**
58
+ * Clave textual de un scope: `app` para la raíz, `<tipo>|<uuid>` para el
59
+ * resto (≤ 57 caracteres). Es la MISMA en todo el paquete: el id de los
60
+ * bindings de FGA (`role_binding:<scopeKey>|<roleUuid>`) y el owner de un
61
+ * rol local (`authz_roles.owner_scope_key`, 3B · B1). `|` es el separador:
62
+ * como ningún componente lo admite (`assertScope`), dos scopes distintos no
63
+ * pueden producir la misma clave, y la clave reservada `global` (el catálogo
64
+ * del config) no la produce ningún scope — la raíz da `app` y todo lo demás
65
+ * lleva `|`. Un scope mal formado (`{app, uuid}`, centinela, mayúsculas) es
66
+ * 422 aquí, antes de serializarse a nada.
67
+ */
68
+ export declare function scopeKey(scope: ScopeRef): string;
69
+ /**
70
+ * La inversa de `scopeKey` para claves que vienen de la BASE (el owner de un
71
+ * rol): `app` ⇒ la raíz; `<tipo>|<uuid>` ⇒ el scope; cualquier otra forma
72
+ * (incluida `global`, que no es un scope) ⇒ `null`. No lanza: es camino de
73
+ * lectura, y una clave que no es un scope simplemente no resuelve.
74
+ */
75
+ export declare function scopeFromKey(key: string): ScopeRef | null;
76
+ /**
77
+ * Nombres que el modelo FGA del modo `facts` usa como relaciones propias
78
+ * (`scope#parent`, `role_binding#assignee`, `deny_binding#denied`…). Un
79
+ * permiso llamado `parent` invalidaría el modelo entero (S14): se rechaza en
80
+ * el núcleo, para ambos drivers, no el día de la migración.
81
+ */
82
+ export declare const RESERVED_SLUGS: ReadonlySet<string>;
83
+ /**
84
+ * Familias de relaciones derivadas del modelo (`can_<P>`, `denied_<P>`,
85
+ * `permits_<P>`). Un slug que EMPIECE por una de ellas colisiona con la
86
+ * relación derivada de otro permiso y el generador la colapsaría en silencio
87
+ * (S4: `can_docs_write` anulaba el deny de `docs:write`).
88
+ */
89
+ export declare const RESERVED_SLUG_PREFIXES: readonly string[];
90
+ /**
91
+ * Longitud máxima de un slug. Un nombre de relación en FGA admite 50
92
+ * caracteres y el modelo deriva `permits_<slug>` (el prefijo más largo, 8):
93
+ * 42 es lo que cabe con cualquier prefijo. Es más estricto que la columna
94
+ * (100) a propósito: un catálogo legal en `database` tiene que ser publicable
95
+ * en `openfga` (S13), o el cambio de driver no es una migración de hechos.
96
+ */
97
+ export declare const MAX_SLUG_LENGTH: number;
98
+ export type SlugKind = 'rol' | 'permiso';
99
+ /** Slug de rol o permiso válido para AMBOS drivers (formato, longitud, reservados). */
100
+ export declare function assertValidSlug(kind: SlugKind, slug: unknown): asserts slug is string;
101
+ /**
102
+ * Cómo se proyecta un slug a un nombre de relación FGA (`:` → `_`). Dos slugs
103
+ * distintos con la misma proyección (`docs:write` / `docs_write`) serían UNA
104
+ * relación: el catálogo los rechaza juntos (L0.8a).
105
+ */
106
+ export declare function slugAsRelation(slug: string): string;
107
+ /** Ningún par de slugs del conjunto colisiona tras proyectarse a relación. */
108
+ export declare function assertNoSlugCollisions(kind: SlugKind, slugs: Iterable<string>): void;
109
+ /** Versiones booleanas para caminos de LECTURA que no deben lanzar (ids que vienen del backend). */
110
+ export declare function isValidScope(scope: ScopeRef): boolean;
111
+ export declare function isValidScopeType(scopeType: unknown): scopeType is string;
112
+ export declare function isValidSlug(kind: SlugKind, slug: string): boolean;
113
+ /**
114
+ * La pregunta de `hasRole`, validada: string ⇒ `{ slug }`; objeto ⇒
115
+ * `{ slug, scopeType }` (ambos con la gramática del motor).
116
+ */
117
+ export declare function normalizeRoleQuery(role: RoleQuery): NormalizedRoleQuery;
118
+ /**
119
+ * Las claves de la cadena desde cada nivel hacia la raíz (3B · B2; movida
120
+ * aquí en 3D · N5 para que exista UNA sola codificación de clave de scope):
121
+ * `keysFrom[i]` = `chain.slice(i).map(scopeKey)`. Un rol es visible en el
122
+ * nivel `i` si es global o su owner está en `keysFrom[i]` — el owner tiene
123
+ * que ser ancestro-o-igual del scope de la ASIGNACIÓN, no de la pregunta.
124
+ */
125
+ export declare function chainKeysFrom(chain: ScopeRef[]): string[][];
126
+ /**
127
+ * `expiresAt` de un `grant`, en sus tres estados legales: omitido, `null` o
128
+ * una `Date` válida. Una cadena, un número o un `Invalid Date` no son una
129
+ * caducidad: un driver lanzaba un TypeError al serializar y el otro persistía
130
+ * basura (D7). Se rechaza como identidad mal formada (422).
131
+ */
132
+ export declare function assertExpiresAt(value: unknown): asserts value is Date | null | undefined;
133
+ export interface IdentityParts {
134
+ subject?: SubjectRef;
135
+ scope?: ScopeRef;
136
+ scopeType?: string;
137
+ /** Pregunta de `hasRole`: string u objeto `{ slug, scopeType }`. */
138
+ role?: RoleQuery;
139
+ /**
140
+ * Slug de rol donde el contrato pide un string (`grant`, `revoke`,
141
+ * `listSubjects`): un `RoleQuery` objeto aquí es 422, no un 503 ni un
142
+ * TypeError (D11).
143
+ */
144
+ roleSlug?: unknown;
145
+ permission?: string;
146
+ /** Caducidad de un `grant`: `undefined` | `null` | `Date` válida. */
147
+ expiresAt?: unknown;
148
+ }
149
+ /**
150
+ * Valida en un solo paso los componentes que recibe una operación. Se llama
151
+ * ANTES de tocar catálogo, árbol o backend: una pregunta mal formada no
152
+ * cuesta una consulta ni deja rastro.
153
+ */
154
+ export declare function assertIdentity(parts: IdentityParts): void;
155
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,359 @@
1
+ import { InvalidIdentityError, InvalidSlugError } from './errors.js';
2
+ import { APP_SCOPE_TYPE } from './types.js';
3
+ /**
4
+ * Validación de identidad del motor: holder, scope, rol y permiso.
5
+ *
6
+ * Vive en un solo sitio y la aplican el manager (una vez por llamada) Y los
7
+ * drivers (defensa en profundidad: el juez del contrato habla con el driver
8
+ * directamente, y un driver de terceros puede usarse sin manager). Nadie
9
+ * duplica la regla: se importa.
10
+ *
11
+ * Por qué es 422 y no "se acepta lo que venga":
12
+ * - `uuid: undefined` en el driver openfga escribía `user:undefined`, y a
13
+ * partir de ahí CUALQUIER holder sin uuid heredaba ese permiso (L0.5).
14
+ * - `{ type: 'app', uuid: 'X' }` colapsaba a la raíz global en openfga y no
15
+ * en database: escalada de tenant a plataforma en un driver y `false` en
16
+ * el otro (L0.10).
17
+ * - `#`, `:`, `|`, `~`, `*` y espacios son sintaxis de FGA o separadores de
18
+ * los ids de binding: un componente que los contenga puede fabricar un
19
+ * userset, un comodín o la clave de OTRO scope (L0.5, L0.8). Desde 3A
20
+ * (2.2) el slug ya NO viaja en los ids de FGA (van por uuid de catálogo),
21
+ * pero los tipos y uuids de scope sí, y los slugs serán relaciones del
22
+ * modelo `facts` (3b): la lista blanca se mantiene entera.
23
+ * - Las longitudes son las de las columnas `authz_*`: lo que no cabe se
24
+ * truncaría o fallaría con un error de SQL ilegible.
25
+ */
26
+ /**
27
+ * Longitudes de columna del esquema publicado (`stubs/migration.stub`) que
28
+ * la identidad aplica tal cual. El slug NO está aquí: su límite es
29
+ * `MAX_SLUG_LENGTH` (42), más estricto que la columna (100) para que el
30
+ * catálogo sea publicable en FGA (E2).
31
+ */
32
+ export const IDENTITY_LIMITS = Object.freeze({
33
+ holderType: 50,
34
+ scopeType: 20,
35
+ uuid: 36,
36
+ });
37
+ /**
38
+ * Centinela con el que el driver `database` almacena el uuid del scope `app`
39
+ * (NOT NULL + unique). Fuera de `app` sería una identidad de scope que
40
+ * colisiona con la raíz: se rechaza (L0.15).
41
+ */
42
+ export const SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
43
+ /**
44
+ * UUID del CATÁLOGO (rol o permiso): la columna `uuid` de `authz_roles` y
45
+ * `authz_permissions` es `uuid` real, y desde 3A (2.2) es lo que viaja en
46
+ * los ids de binding de FGA (`role_binding:<scopeKey>|<roleUuid>`). Se exige
47
+ * canónico y en MINÚSCULAS (RFC 9562, 8-4-4-4-12 hex): PostgreSQL lo
48
+ * normaliza así y MySQL/SQLite lo guardan tal cual, así que un uuid en
49
+ * mayúsculas del spec produciría dos identidades para el mismo rol según el
50
+ * motor, y un id de binding que el propio driver no leería de vuelta. Es más
51
+ * estricto que la gramática de los uuids de holder/scope (esos son del
52
+ * consumidor, `[a-z0-9._-]{1,36}`): un id 1.x con slug (`app|editor`) no
53
+ * puede parecer un id 2.2 (`app|<uuid>`).
54
+ */
55
+ const CATALOG_UUID_FORMAT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
56
+ /** El uuid de un rol o permiso del catálogo (opcional en el spec): canónico y en minúsculas, o 422. */
57
+ export function assertCatalogUuid(kind, value) {
58
+ if (typeof value !== 'string' || !CATALOG_UUID_FORMAT.test(value)) {
59
+ throw new InvalidIdentityError(`UUID del ${kind} inválido: se esperaba un UUID canónico en minúsculas (8-4-4-4-12 hex) y llegó ${describe(value)}`);
60
+ }
61
+ }
62
+ /** Versión booleana para caminos de LECTURA (ids que vienen del backend): no lanza. */
63
+ export function isCatalogUuid(value) {
64
+ return typeof value === 'string' && CATALOG_UUID_FORMAT.test(value);
65
+ }
66
+ /**
67
+ * Un componente de identidad: MINÚSCULAS, dígitos, `_`, `.` y `-`. Lista
68
+ * blanca y no negra: lo que no está aquí no se serializa a ningún backend.
69
+ * Excluye por construcción `#`, `:`, `|`, `~`, `*`, `@`, espacios y control.
70
+ *
71
+ * Los TIPOS (de holder y de scope) van en minúsculas desde E4 (auditor H14):
72
+ * en un motor SQL con collation `*_ci` `Users` y `users` serían la misma fila
73
+ * y en FGA dos holders distintos. Los UUIDs también desde 2.5-B · K1
74
+ * (auditor 🔴 1): el árbol del consumidor (tipo `uuid` de PG, `char(36)`
75
+ * `*_ci` de MySQL) fundía `BBBB…` con `bbbb…`, la cadena resolvía con el
76
+ * alias y el deny —escrito canónico en `authz_*`, `utf8mb4_bin`— no casaba.
77
+ * La cadena canónica del resolutor cierra el alias por guiones; el alias por
78
+ * mayúsculas muere aquí, en la puerta, antes de tocar nada. Un id en
79
+ * mayúsculas del consumidor se pasa en minúsculas (`uuid.toLowerCase()`) en
80
+ * su borde: un UUID es el mismo id en cualquier caja.
81
+ */
82
+ const COMPONENT_FORMAT = /^[a-z0-9_.-]+$/;
83
+ const TYPE_FORMAT = COMPONENT_FORMAT;
84
+ function assertComponent(kind, value, maxLength, format = COMPONENT_FORMAT) {
85
+ if (typeof value !== 'string' || value.length === 0) {
86
+ throw new InvalidIdentityError(`${kind} inválido: se esperaba una cadena no vacía y llegó ${describe(value)}`);
87
+ }
88
+ if (value.length > maxLength) {
89
+ throw new InvalidIdentityError(`${kind} inválido: '${value}' supera los ${maxLength} caracteres de la columna`);
90
+ }
91
+ if (!format.test(value)) {
92
+ throw new InvalidIdentityError(`${kind} inválido: '${value}'. Solo se admiten minúsculas, dígitos y . _ - ` +
93
+ `(ni mayúsculas, '#', ':', '|', '~', '*', espacios ni caracteres de control)`);
94
+ }
95
+ }
96
+ function describe(value) {
97
+ if (value === undefined)
98
+ return 'undefined';
99
+ if (value === null)
100
+ return 'null';
101
+ if (typeof value === 'string')
102
+ return `'${value}'`;
103
+ return typeof value;
104
+ }
105
+ /** `SubjectRef` bien formado: type y uuid presentes, con formato y longitud. */
106
+ export function assertSubject(subject) {
107
+ if (!subject || typeof subject !== 'object') {
108
+ throw new InvalidIdentityError(`Holder inválido: llegó ${describe(subject)}`);
109
+ }
110
+ assertComponent('Tipo de holder', subject.type, IDENTITY_LIMITS.holderType, TYPE_FORMAT);
111
+ assertComponent(`UUID del holder '${subject.type}'`, subject.uuid, IDENTITY_LIMITS.uuid);
112
+ }
113
+ /**
114
+ * `ScopeRef` bien formado. `app` es la raíz y NO admite uuid (L0.10): un
115
+ * `{ app, 'X' }` no es "otro app", es una identidad que un driver colapsaría
116
+ * a la global. Fuera de `app` el uuid es obligatorio y no puede ser el
117
+ * centinela con el que `database` representa la raíz (L0.15).
118
+ */
119
+ export function assertScope(scope) {
120
+ if (!scope || typeof scope !== 'object') {
121
+ throw new InvalidIdentityError(`Scope inválido: llegó ${describe(scope)}`);
122
+ }
123
+ assertComponent('Tipo de scope', scope.type, IDENTITY_LIMITS.scopeType, TYPE_FORMAT);
124
+ if (scope.type === APP_SCOPE_TYPE) {
125
+ if (scope.uuid !== null) {
126
+ throw new InvalidIdentityError(`Scope inválido: el scope '${APP_SCOPE_TYPE}' es la raíz y no admite uuid ` +
127
+ `(llegó ${describe(scope.uuid)}). Usa APP_SCOPE.`);
128
+ }
129
+ return;
130
+ }
131
+ assertComponent(`UUID del scope '${scope.type}'`, scope.uuid, IDENTITY_LIMITS.uuid);
132
+ if (scope.uuid === SENTINEL_UUID) {
133
+ throw new InvalidIdentityError(`Scope inválido: '${SENTINEL_UUID}' está reservado para la raíz y no puede ` +
134
+ `identificar un scope de tipo '${scope.type}'`);
135
+ }
136
+ }
137
+ /** Tipo de scope suelto (el de `listRoleScopes`). */
138
+ export function assertScopeType(scopeType) {
139
+ assertComponent('Tipo de scope', scopeType, IDENTITY_LIMITS.scopeType, TYPE_FORMAT);
140
+ }
141
+ /* ── Clave de scope ─────────────────────────────────────────────────────── */
142
+ /**
143
+ * Clave textual de un scope: `app` para la raíz, `<tipo>|<uuid>` para el
144
+ * resto (≤ 57 caracteres). Es la MISMA en todo el paquete: el id de los
145
+ * bindings de FGA (`role_binding:<scopeKey>|<roleUuid>`) y el owner de un
146
+ * rol local (`authz_roles.owner_scope_key`, 3B · B1). `|` es el separador:
147
+ * como ningún componente lo admite (`assertScope`), dos scopes distintos no
148
+ * pueden producir la misma clave, y la clave reservada `global` (el catálogo
149
+ * del config) no la produce ningún scope — la raíz da `app` y todo lo demás
150
+ * lleva `|`. Un scope mal formado (`{app, uuid}`, centinela, mayúsculas) es
151
+ * 422 aquí, antes de serializarse a nada.
152
+ */
153
+ export function scopeKey(scope) {
154
+ assertScope(scope);
155
+ if (scope.type === APP_SCOPE_TYPE)
156
+ return APP_SCOPE_TYPE;
157
+ return `${scope.type}|${scope.uuid}`;
158
+ }
159
+ /**
160
+ * La inversa de `scopeKey` para claves que vienen de la BASE (el owner de un
161
+ * rol): `app` ⇒ la raíz; `<tipo>|<uuid>` ⇒ el scope; cualquier otra forma
162
+ * (incluida `global`, que no es un scope) ⇒ `null`. No lanza: es camino de
163
+ * lectura, y una clave que no es un scope simplemente no resuelve.
164
+ */
165
+ export function scopeFromKey(key) {
166
+ if (key === APP_SCOPE_TYPE)
167
+ return { type: APP_SCOPE_TYPE, uuid: null };
168
+ const parts = key.split('|');
169
+ if (parts.length !== 2)
170
+ return null;
171
+ const scope = { type: parts[0], uuid: parts[1] };
172
+ return isValidScope(scope) ? scope : null;
173
+ }
174
+ /* ── Slugs del catálogo ─────────────────────────────────────────────────── */
175
+ /**
176
+ * Gramática de un slug: minúsculas, dígitos y . _ - ; los permisos admiten UN
177
+ * `:` (`recurso:accion`), los roles ninguno.
178
+ */
179
+ const PERMISSION_FORMAT = /^[a-z0-9][a-z0-9_.-]*(:[a-z0-9][a-z0-9_.-]*)?$/;
180
+ const ROLE_FORMAT = /^[a-z0-9][a-z0-9_.-]*$/;
181
+ /**
182
+ * Nombres que el modelo FGA del modo `facts` usa como relaciones propias
183
+ * (`scope#parent`, `role_binding#assignee`, `deny_binding#denied`…). Un
184
+ * permiso llamado `parent` invalidaría el modelo entero (S14): se rechaza en
185
+ * el núcleo, para ambos drivers, no el día de la migración.
186
+ */
187
+ export const RESERVED_SLUGS = new Set([
188
+ 'parent',
189
+ 'binding',
190
+ 'ancestor',
191
+ 'role',
192
+ 'assignee',
193
+ 'denied',
194
+ ]);
195
+ /**
196
+ * Familias de relaciones derivadas del modelo (`can_<P>`, `denied_<P>`,
197
+ * `permits_<P>`). Un slug que EMPIECE por una de ellas colisiona con la
198
+ * relación derivada de otro permiso y el generador la colapsaría en silencio
199
+ * (S4: `can_docs_write` anulaba el deny de `docs:write`).
200
+ */
201
+ export const RESERVED_SLUG_PREFIXES = Object.freeze(['can_', 'denied_', 'permits_']);
202
+ /**
203
+ * Longitud máxima de un slug. Un nombre de relación en FGA admite 50
204
+ * caracteres y el modelo deriva `permits_<slug>` (el prefijo más largo, 8):
205
+ * 42 es lo que cabe con cualquier prefijo. Es más estricto que la columna
206
+ * (100) a propósito: un catálogo legal en `database` tiene que ser publicable
207
+ * en `openfga` (S13), o el cambio de driver no es una migración de hechos.
208
+ */
209
+ export const MAX_SLUG_LENGTH = 50 - Math.max(...RESERVED_SLUG_PREFIXES.map((p) => p.length));
210
+ /** Slug de rol o permiso válido para AMBOS drivers (formato, longitud, reservados). */
211
+ export function assertValidSlug(kind, slug) {
212
+ if (typeof slug !== 'string' || slug.length === 0) {
213
+ throw new InvalidSlugError(`Slug de ${kind} inválido: se esperaba una cadena no vacía y llegó ${describe(slug)}`);
214
+ }
215
+ if (slug.length > MAX_SLUG_LENGTH) {
216
+ throw new InvalidSlugError(`Slug de ${kind} inválido: '${slug}' supera los ${MAX_SLUG_LENGTH} caracteres ` +
217
+ `(50 de una relación FGA menos el prefijo derivado más largo)`);
218
+ }
219
+ const format = kind === 'permiso' ? PERMISSION_FORMAT : ROLE_FORMAT;
220
+ if (!format.test(slug)) {
221
+ throw new InvalidSlugError(`Slug de ${kind} inválido: '${slug}'. Formato: minúsculas/dígitos/._-` +
222
+ (kind === 'permiso' ? " con ':' único opcional (recurso:accion)" : ' (sin ":")') +
223
+ "; '~', '|', '#', '*' y espacios están prohibidos");
224
+ }
225
+ if (RESERVED_SLUGS.has(slug)) {
226
+ throw new InvalidSlugError(`Slug de ${kind} inválido: '${slug}' es un nombre reservado del modelo ` +
227
+ `(${[...RESERVED_SLUGS].join(', ')})`);
228
+ }
229
+ const family = RESERVED_SLUG_PREFIXES.find((prefix) => slug.startsWith(prefix));
230
+ if (family) {
231
+ throw new InvalidSlugError(`Slug de ${kind} inválido: '${slug}' empieza por '${family}', prefijo reservado ` +
232
+ `de las relaciones derivadas (${RESERVED_SLUG_PREFIXES.join(', ')})`);
233
+ }
234
+ }
235
+ /**
236
+ * Cómo se proyecta un slug a un nombre de relación FGA (`:` → `_`). Dos slugs
237
+ * distintos con la misma proyección (`docs:write` / `docs_write`) serían UNA
238
+ * relación: el catálogo los rechaza juntos (L0.8a).
239
+ */
240
+ export function slugAsRelation(slug) {
241
+ return slug.replaceAll(':', '_');
242
+ }
243
+ /** Ningún par de slugs del conjunto colisiona tras proyectarse a relación. */
244
+ export function assertNoSlugCollisions(kind, slugs) {
245
+ const seen = new Map();
246
+ for (const slug of slugs) {
247
+ const relation = slugAsRelation(slug);
248
+ const other = seen.get(relation);
249
+ if (other !== undefined && other !== slug) {
250
+ throw new InvalidSlugError(`Slugs de ${kind} en colisión: '${other}' y '${slug}' se proyectan a la misma ` +
251
+ `relación '${relation}' (':' y '_' son equivalentes en el modelo)`);
252
+ }
253
+ seen.set(relation, slug);
254
+ }
255
+ }
256
+ /** Versiones booleanas para caminos de LECTURA que no deben lanzar (ids que vienen del backend). */
257
+ export function isValidScope(scope) {
258
+ try {
259
+ assertScope(scope);
260
+ return true;
261
+ }
262
+ catch {
263
+ return false;
264
+ }
265
+ }
266
+ export function isValidScopeType(scopeType) {
267
+ try {
268
+ assertScopeType(scopeType);
269
+ return true;
270
+ }
271
+ catch {
272
+ return false;
273
+ }
274
+ }
275
+ export function isValidSlug(kind, slug) {
276
+ try {
277
+ assertValidSlug(kind, slug);
278
+ return true;
279
+ }
280
+ catch {
281
+ return false;
282
+ }
283
+ }
284
+ /**
285
+ * La pregunta de `hasRole`, validada: string ⇒ `{ slug }`; objeto ⇒
286
+ * `{ slug, scopeType }` (ambos con la gramática del motor).
287
+ */
288
+ export function normalizeRoleQuery(role) {
289
+ if (typeof role === 'string') {
290
+ assertValidSlug('rol', role);
291
+ return { slug: role };
292
+ }
293
+ if (!role || typeof role !== 'object') {
294
+ throw new InvalidIdentityError(`Rol inválido: llegó ${describe(role)}`);
295
+ }
296
+ // 3D · M1(c): `{ uuid }` es la forma EXACTA (sin ambigüedad posible). No se
297
+ // mezcla con `{ slug, scopeType }`: la pregunta es por identidad o por
298
+ // nombre, nunca por las dos a la vez (un `{ uuid, slug }` que no casaran
299
+ // sería una pregunta con dos respuestas).
300
+ if ('uuid' in role) {
301
+ const query = role;
302
+ if (query.slug !== undefined || query.scopeType !== undefined) {
303
+ throw new InvalidIdentityError(`Rol inválido: { uuid } es la forma exacta y no se combina con 'slug'/'scopeType' (llegó ${describe(role)})`);
304
+ }
305
+ assertCatalogUuid('rol', query.uuid);
306
+ return { uuid: query.uuid };
307
+ }
308
+ assertValidSlug('rol', role.slug);
309
+ assertScopeType(role.scopeType);
310
+ return { slug: role.slug, scopeType: role.scopeType };
311
+ }
312
+ /**
313
+ * Las claves de la cadena desde cada nivel hacia la raíz (3B · B2; movida
314
+ * aquí en 3D · N5 para que exista UNA sola codificación de clave de scope):
315
+ * `keysFrom[i]` = `chain.slice(i).map(scopeKey)`. Un rol es visible en el
316
+ * nivel `i` si es global o su owner está en `keysFrom[i]` — el owner tiene
317
+ * que ser ancestro-o-igual del scope de la ASIGNACIÓN, no de la pregunta.
318
+ */
319
+ export function chainKeysFrom(chain) {
320
+ const keys = chain.map(scopeKey);
321
+ return keys.map((_, i) => keys.slice(i));
322
+ }
323
+ /* ── Punto de entrada único ─────────────────────────────────────────────── */
324
+ /**
325
+ * `expiresAt` de un `grant`, en sus tres estados legales: omitido, `null` o
326
+ * una `Date` válida. Una cadena, un número o un `Invalid Date` no son una
327
+ * caducidad: un driver lanzaba un TypeError al serializar y el otro persistía
328
+ * basura (D7). Se rechaza como identidad mal formada (422).
329
+ */
330
+ export function assertExpiresAt(value) {
331
+ if (value === undefined || value === null)
332
+ return;
333
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
334
+ throw new InvalidIdentityError(`expiresAt inválido: se esperaba una Date válida, null (sin caducidad) u omitido ` +
335
+ `(preservar la vigente) y llegó ${value instanceof Date ? 'Invalid Date' : describe(value)}`);
336
+ }
337
+ }
338
+ /**
339
+ * Valida en un solo paso los componentes que recibe una operación. Se llama
340
+ * ANTES de tocar catálogo, árbol o backend: una pregunta mal formada no
341
+ * cuesta una consulta ni deja rastro.
342
+ */
343
+ export function assertIdentity(parts) {
344
+ if ('subject' in parts)
345
+ assertSubject(parts.subject);
346
+ if ('scope' in parts)
347
+ assertScope(parts.scope);
348
+ if ('scopeType' in parts)
349
+ assertScopeType(parts.scopeType);
350
+ if ('role' in parts)
351
+ normalizeRoleQuery(parts.role);
352
+ if ('roleSlug' in parts)
353
+ assertValidSlug('rol', parts.roleSlug);
354
+ if ('permission' in parts)
355
+ assertValidSlug('permiso', parts.permission);
356
+ if ('expiresAt' in parts)
357
+ assertExpiresAt(parts.expiresAt);
358
+ }
359
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"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"}