@jantstack/adonis-authz 1.0.0

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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +138 -0
  3. package/build/commands/main.d.ts +7 -0
  4. package/build/commands/main.d.ts.map +1 -0
  5. package/build/commands/main.js +7 -0
  6. package/build/commands/main.js.map +1 -0
  7. package/build/commands/openfga_import.d.ts +28 -0
  8. package/build/commands/openfga_import.d.ts.map +1 -0
  9. package/build/commands/openfga_import.js +74 -0
  10. package/build/commands/openfga_import.js.map +1 -0
  11. package/build/commands/openfga_provision.d.ts +27 -0
  12. package/build/commands/openfga_provision.d.ts.map +1 -0
  13. package/build/commands/openfga_provision.js +73 -0
  14. package/build/commands/openfga_provision.js.map +1 -0
  15. package/build/configure.d.ts +10 -0
  16. package/build/configure.d.ts.map +1 -0
  17. package/build/configure.js +43 -0
  18. package/build/configure.js.map +1 -0
  19. package/build/index.d.ts +21 -0
  20. package/build/index.d.ts.map +1 -0
  21. package/build/index.js +18 -0
  22. package/build/index.js.map +1 -0
  23. package/build/providers/authz_provider.d.ts +17 -0
  24. package/build/providers/authz_provider.d.ts.map +1 -0
  25. package/build/providers/authz_provider.js +21 -0
  26. package/build/providers/authz_provider.js.map +1 -0
  27. package/build/services/main.d.ts +10 -0
  28. package/build/services/main.d.ts.map +1 -0
  29. package/build/services/main.js +13 -0
  30. package/build/services/main.js.map +1 -0
  31. package/build/src/catalog.d.ts +3 -0
  32. package/build/src/catalog.d.ts.map +1 -0
  33. package/build/src/catalog.js +97 -0
  34. package/build/src/catalog.js.map +1 -0
  35. package/build/src/define_config.d.ts +53 -0
  36. package/build/src/define_config.d.ts.map +1 -0
  37. package/build/src/define_config.js +4 -0
  38. package/build/src/define_config.js.map +1 -0
  39. package/build/src/drivers/database_driver.d.ts +48 -0
  40. package/build/src/drivers/database_driver.d.ts.map +1 -0
  41. package/build/src/drivers/database_driver.js +260 -0
  42. package/build/src/drivers/database_driver.js.map +1 -0
  43. package/build/src/drivers/openfga_driver.d.ts +107 -0
  44. package/build/src/drivers/openfga_driver.d.ts.map +1 -0
  45. package/build/src/drivers/openfga_driver.js +450 -0
  46. package/build/src/drivers/openfga_driver.js.map +1 -0
  47. package/build/src/manager.d.ts +29 -0
  48. package/build/src/manager.d.ts.map +1 -0
  49. package/build/src/manager.js +79 -0
  50. package/build/src/manager.js.map +1 -0
  51. package/build/src/middleware/app_access_middleware.d.ts +23 -0
  52. package/build/src/middleware/app_access_middleware.d.ts.map +1 -0
  53. package/build/src/middleware/app_access_middleware.js +53 -0
  54. package/build/src/middleware/app_access_middleware.js.map +1 -0
  55. package/build/src/models/authz_assignment.d.ts +166 -0
  56. package/build/src/models/authz_assignment.d.ts.map +1 -0
  57. package/build/src/models/authz_assignment.js +60 -0
  58. package/build/src/models/authz_assignment.js.map +1 -0
  59. package/build/src/models/authz_deny.d.ts +161 -0
  60. package/build/src/models/authz_deny.d.ts.map +1 -0
  61. package/build/src/models/authz_deny.js +50 -0
  62. package/build/src/models/authz_deny.js.map +1 -0
  63. package/build/src/models/authz_permission.d.ts +159 -0
  64. package/build/src/models/authz_permission.d.ts.map +1 -0
  65. package/build/src/models/authz_permission.js +42 -0
  66. package/build/src/models/authz_permission.js.map +1 -0
  67. package/build/src/models/authz_role.d.ts +166 -0
  68. package/build/src/models/authz_role.d.ts.map +1 -0
  69. package/build/src/models/authz_role.js +66 -0
  70. package/build/src/models/authz_role.js.map +1 -0
  71. package/build/src/models/authz_role_permission.d.ts +155 -0
  72. package/build/src/models/authz_role_permission.d.ts.map +1 -0
  73. package/build/src/models/authz_role_permission.js +35 -0
  74. package/build/src/models/authz_role_permission.js.map +1 -0
  75. package/build/src/testing/contract.d.ts +27 -0
  76. package/build/src/testing/contract.d.ts.map +1 -0
  77. package/build/src/testing/contract.js +211 -0
  78. package/build/src/testing/contract.js.map +1 -0
  79. package/build/src/testing/main.d.ts +11 -0
  80. package/build/src/testing/main.d.ts.map +1 -0
  81. package/build/src/testing/main.js +10 -0
  82. package/build/src/testing/main.js.map +1 -0
  83. package/build/src/traits/authz_scopes.d.ts +21 -0
  84. package/build/src/traits/authz_scopes.d.ts.map +1 -0
  85. package/build/src/traits/authz_scopes.js +45 -0
  86. package/build/src/traits/authz_scopes.js.map +1 -0
  87. package/build/src/traits/has_uuid.d.ts +151 -0
  88. package/build/src/traits/has_uuid.d.ts.map +1 -0
  89. package/build/src/traits/has_uuid.js +38 -0
  90. package/build/src/traits/has_uuid.js.map +1 -0
  91. package/build/src/types.d.ts +153 -0
  92. package/build/src/types.d.ts.map +1 -0
  93. package/build/src/types.js +38 -0
  94. package/build/src/types.js.map +1 -0
  95. package/build/stubs/config/app_acl.stub +81 -0
  96. package/build/stubs/config/authorization.stub +74 -0
  97. package/build/stubs/main.d.ts +3 -0
  98. package/build/stubs/main.d.ts.map +1 -0
  99. package/build/stubs/main.js +3 -0
  100. package/build/stubs/main.js.map +1 -0
  101. package/build/stubs/migration.stub +124 -0
  102. package/package.json +94 -0
@@ -0,0 +1,97 @@
1
+ import db from '@adonisjs/lucid/services/db';
2
+ import { Exception } from '@adonisjs/core/exceptions';
3
+ import { v7 as uuidv7 } from 'uuid';
4
+ /**
5
+ * Sincroniza un catálogo (roles + permisos + vínculos) a las tablas `authz_*`.
6
+ *
7
+ * Idempotente y aditivo (mismo contrato que el organization_acl_seeder): lo
8
+ * existente se respeta, solo se insertan los diffs nuevos. Quitar algo del
9
+ * catálogo NO lo borra de DB (limpieza explícita vía migración).
10
+ *
11
+ * Lo usan el seeder del nivel app (`app_acl_seeder`) y el harness de la
12
+ * suite de contrato. El catálogo es metadata compartida entre drivers:
13
+ * un driver externo (openfga) materializa aparte solo los hechos.
14
+ */
15
+ /**
16
+ * Slugs válidos del catálogo: minúsculas, dígitos y . _ - con `:` opcional
17
+ * (gramática recurso:accion). Prohibidos `~` y `|`: son los caracteres de
18
+ * encoding/separador del driver openfga — un slug que los contenga
19
+ * corrompería el mapeo de bindings.
20
+ */
21
+ const CATALOG_SLUG_FORMAT = /^[a-z0-9][a-z0-9_.-]*(:[a-z0-9][a-z0-9_.-]*)?$/;
22
+ function assertValidSlug(kind, slug) {
23
+ if (!CATALOG_SLUG_FORMAT.test(slug)) {
24
+ throw new Exception(`Slug de ${kind} inválido: '${slug}'. Formato: minúsculas/dígitos/._- ` +
25
+ `con ':' único opcional (recurso:accion); '~' y '|' están prohibidos.`, { status: 500 });
26
+ }
27
+ }
28
+ export async function syncAuthzCatalog(catalog) {
29
+ for (const perm of catalog.permissions)
30
+ assertValidSlug('permiso', perm.slug);
31
+ for (const role of catalog.roles)
32
+ assertValidSlug('rol', role.slug);
33
+ // 1. Permisos: upsert por slug.
34
+ for (const perm of catalog.permissions) {
35
+ const existing = await db.from('authz_permissions').where('slug', perm.slug).first();
36
+ if (existing)
37
+ continue;
38
+ await db.table('authz_permissions').insert({
39
+ uuid: uuidv7(),
40
+ slug: perm.slug,
41
+ description: perm.description ?? null,
42
+ created_at: new Date(),
43
+ updated_at: new Date(),
44
+ });
45
+ }
46
+ const dbPerms = await db
47
+ .from('authz_permissions')
48
+ .whereIn('slug', catalog.permissions.map((p) => p.slug))
49
+ .select('uuid', 'slug');
50
+ const permUuidBySlug = new Map(dbPerms.map((p) => [p.slug, p.uuid]));
51
+ // 2. Roles: upsert por (slug, scope_type).
52
+ for (const role of catalog.roles) {
53
+ const existing = await db
54
+ .from('authz_roles')
55
+ .where('slug', role.slug)
56
+ .where('scope_type', role.scopeType)
57
+ .first();
58
+ const roleUuid = existing?.uuid ?? role.uuid ?? uuidv7();
59
+ if (!existing) {
60
+ await db.table('authz_roles').insert({
61
+ uuid: roleUuid,
62
+ slug: role.slug,
63
+ name: role.name ?? role.slug,
64
+ description: role.description ?? null,
65
+ scope_type: role.scopeType,
66
+ rank: role.rank ?? 0,
67
+ created_at: new Date(),
68
+ updated_at: new Date(),
69
+ });
70
+ }
71
+ else if (role.rank !== undefined && existing.rank !== role.rank) {
72
+ // El rank es metadata de policy: el config manda (a diferencia de los
73
+ // vínculos, que son aditivos).
74
+ await db.from('authz_roles').where('uuid', roleUuid).update({ rank: role.rank });
75
+ }
76
+ // 3. Vínculos rol→permiso: upsert por par.
77
+ for (const permSlug of role.permissions) {
78
+ const permUuid = permUuidBySlug.get(permSlug);
79
+ if (!permUuid)
80
+ continue;
81
+ const linked = await db
82
+ .from('authz_role_permissions')
83
+ .where('role_uuid', roleUuid)
84
+ .where('permission_uuid', permUuid)
85
+ .first();
86
+ if (linked)
87
+ continue;
88
+ await db.table('authz_role_permissions').insert({
89
+ uuid: uuidv7(),
90
+ role_uuid: roleUuid,
91
+ permission_uuid: permUuid,
92
+ created_at: new Date(),
93
+ });
94
+ }
95
+ }
96
+ }
97
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { AuthorizationDriverFactory, AuthzWriteEvent } from './types.js';
2
+ import type { HolderTypeMap } from './drivers/openfga_driver.js';
3
+ /**
4
+ * Config del sistema de autorización. Todo lo específico del consumidor
5
+ * entra por aquí: qué drivers existen, cuál está activo, cómo se llaman sus
6
+ * holders y qué hacer cuando el motor escribe.
7
+ *
8
+ * export default defineConfig({
9
+ * default: env.get('AUTHZ_DRIVER', 'database'),
10
+ * holderTypes: { users: 'user', admins: 'admin' },
11
+ * drivers: {
12
+ * database: () => new DatabaseAuthorizationDriver({ resolveAncestors }),
13
+ * 'mi-driver': () => new MiDriver(),
14
+ * },
15
+ * hooks: { onWrite: auditarEscritura },
16
+ * })
17
+ *
18
+ * Todo driver debe pasar la suite de contrato que publica el paquete
19
+ * (`@jantstack/adonis-authz/testing`).
20
+ */
21
+ export interface AuthorizationConfig {
22
+ /** Nombre del driver activo (debe existir como key en `drivers`). */
23
+ default: string;
24
+ drivers: Record<string, AuthorizationDriverFactory>;
25
+ /**
26
+ * Holders del consumidor: morph name (`@MorphMap` del modelo) → tipo del
27
+ * modelo FGA. Lo necesitan el driver `openfga` y los comandos
28
+ * `openfga:provision` / `openfga:import`; el driver `database` lo ignora.
29
+ */
30
+ holderTypes?: HolderTypeMap;
31
+ /**
32
+ * Conexión al servidor OpenFGA para las herramientas de línea de comandos
33
+ * (el driver recibe estos mismos valores en su factory).
34
+ */
35
+ openfga?: {
36
+ url?: string;
37
+ storeId?: string;
38
+ modelId?: string;
39
+ /** Nombre del store al provisionar. */
40
+ storeName?: string;
41
+ };
42
+ /**
43
+ * Hooks del consumidor. `onWrite` se llama tras cada escritura del motor
44
+ * (grant/revoke/deny/removeDeny) — el sitio natural para auditar o emitir
45
+ * eventos. No debe lanzar: una escritura ya aplicada no se revierte por un
46
+ * side-effect fallido.
47
+ */
48
+ hooks?: {
49
+ onWrite?: (event: AuthzWriteEvent) => Promise<void>;
50
+ };
51
+ }
52
+ export declare function defineConfig<T extends AuthorizationConfig>(config: T): T;
53
+ //# sourceMappingURL=define_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAEhE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAA;IAEnD;;;;OAIG;IACH,WAAW,CAAC,EAAE,aAAa,CAAA;IAE3B;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,uCAAuC;QACvC,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;IAED;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACpD,CAAA;CACF;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,mBAAmB,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAExE"}
@@ -0,0 +1,4 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
4
+ //# sourceMappingURL=define_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"AAwDA,MAAM,UAAU,YAAY,CAAgC,MAAS;IACnE,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { AuthorizationDriver, GrantOptions, ScopeRef, ScopeType, SubjectRef } from '../types.js';
2
+ import type { ScopeAncestorsResolver } from '../types.js';
3
+ /**
4
+ * UUID centinela para el scope 'app' en las columnas scope_uuid (NOT NULL):
5
+ * permite que el unique de assignments/denies cubra también el nivel app
6
+ * (los NULL serían "distintos" para el motor SQL). Detalle de almacenamiento
7
+ * de ESTE driver — la API pública sigue usando uuid null para app.
8
+ */
9
+ export declare const APP_SCOPE_DB_UUID = "00000000-0000-0000-0000-000000000000";
10
+ /**
11
+ * Driver `database`: motor de autorización propio sobre las tablas `authz_*`.
12
+ * Default autosuficiente del chasis (cero piezas extra en el appliance).
13
+ *
14
+ * Resolución de `authorize`: NO hay deny en la cadena de scopes Y alguna
15
+ * asignación vigente (expires_at null o futuro) en la cadena concede el
16
+ * permiso vía su rol. La cadena es `[scope, ...ancestros]` — herencia solo
17
+ * hacia abajo. Solo SQL estándar vía query builder (regla M6: nada
18
+ * específico de un motor).
19
+ */
20
+ export declare class DatabaseAuthorizationDriver implements AuthorizationDriver {
21
+ /**
22
+ * Resolutor de jerarquía inyectado por el consumidor (el chasis pasa el
23
+ * suyo, que conoce organizations/units, en `config/authorization.ts`).
24
+ * Default puro sin dominio: todo cuelga directamente de `app`.
25
+ */
26
+ private resolveAncestors;
27
+ constructor(options?: {
28
+ resolveAncestors?: ScopeAncestorsResolver;
29
+ });
30
+ private chain;
31
+ /** Filtro (scope_type, scope_uuid) ∈ scopes (centinela para el nivel app). */
32
+ private whereScopeIn;
33
+ /** Asignación vigente: sin expiración o con expiración futura. */
34
+ private whereActive;
35
+ private findPermission;
36
+ private findRoleOrFail;
37
+ authorize(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<boolean>;
38
+ grant(subject: SubjectRef, role: string, scope: ScopeRef, options?: GrantOptions): Promise<void>;
39
+ revoke(subject: SubjectRef, role: string, scope: ScopeRef): Promise<void>;
40
+ hasRole(subject: SubjectRef, role: string, scope: ScopeRef): Promise<boolean>;
41
+ deny(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<void>;
42
+ removeDeny(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<void>;
43
+ listSubjects(role: string, scope: ScopeRef): Promise<SubjectRef[]>;
44
+ listRoles(subject: SubjectRef, scope: ScopeRef): Promise<string[]>;
45
+ listRoleScopes(subject: SubjectRef, scopeType: ScopeType): Promise<ScopeRef[]>;
46
+ listScopes(subject: SubjectRef, permission: string): Promise<ScopeRef[]>;
47
+ }
48
+ //# sourceMappingURL=database_driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database_driver.d.ts","sourceRoot":"","sources":["../../../src/drivers/database_driver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,UAAU,EACX,MAAM,aAAa,CAAA;AAEpB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAIzD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,yCAAyC,CAAA;AAUvE;;;;;;;;;GASG;AACH,qBAAa,2BAA4B,YAAW,mBAAmB;IACrE;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAwB;gBAEpC,OAAO,GAAE;QAAE,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;KAAO;YAKzD,KAAK;IAInB,8EAA8E;IAC9E,OAAO,CAAC,YAAY;IAUpB,kEAAkE;IAClE,OAAO,CAAC,WAAW;YAML,cAAc;YAId,cAAc;IAetB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAkCrF,KAAK,CACT,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,QAAQ,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC;IAqDV,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzE,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAkB7E,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C7E,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAenF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAiBlE,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBlE,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAY9E,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAkC/E"}
@@ -0,0 +1,260 @@
1
+ import db from '@adonisjs/lucid/services/db';
2
+ import { Exception } from '@adonisjs/core/exceptions';
3
+ import { v7 as uuidv7 } from 'uuid';
4
+ import { APP_SCOPE } from '../types.js';
5
+ /**
6
+ * UUID centinela para el scope 'app' en las columnas scope_uuid (NOT NULL):
7
+ * permite que el unique de assignments/denies cubra también el nivel app
8
+ * (los NULL serían "distintos" para el motor SQL). Detalle de almacenamiento
9
+ * de ESTE driver — la API pública sigue usando uuid null para app.
10
+ */
11
+ export const APP_SCOPE_DB_UUID = '00000000-0000-0000-0000-000000000000';
12
+ function toDbScopeUuid(scope) {
13
+ return scope.uuid ?? APP_SCOPE_DB_UUID;
14
+ }
15
+ function fromDbScopeUuid(uuid) {
16
+ return uuid === APP_SCOPE_DB_UUID ? null : uuid;
17
+ }
18
+ /**
19
+ * Driver `database`: motor de autorización propio sobre las tablas `authz_*`.
20
+ * Default autosuficiente del chasis (cero piezas extra en el appliance).
21
+ *
22
+ * Resolución de `authorize`: NO hay deny en la cadena de scopes Y alguna
23
+ * asignación vigente (expires_at null o futuro) en la cadena concede el
24
+ * permiso vía su rol. La cadena es `[scope, ...ancestros]` — herencia solo
25
+ * hacia abajo. Solo SQL estándar vía query builder (regla M6: nada
26
+ * específico de un motor).
27
+ */
28
+ export class DatabaseAuthorizationDriver {
29
+ /**
30
+ * Resolutor de jerarquía inyectado por el consumidor (el chasis pasa el
31
+ * suyo, que conoce organizations/units, en `config/authorization.ts`).
32
+ * Default puro sin dominio: todo cuelga directamente de `app`.
33
+ */
34
+ resolveAncestors;
35
+ constructor(options = {}) {
36
+ this.resolveAncestors =
37
+ options.resolveAncestors ?? (async (scope) => (scope.type === 'app' ? [] : [APP_SCOPE]));
38
+ }
39
+ async chain(scope) {
40
+ return [scope, ...(await this.resolveAncestors(scope))];
41
+ }
42
+ /** Filtro (scope_type, scope_uuid) ∈ scopes (centinela para el nivel app). */
43
+ whereScopeIn(query, column, scopes) {
44
+ return query.where((outer) => {
45
+ for (const s of scopes) {
46
+ outer.orWhere((inner) => {
47
+ inner.where(`${column}_type`, s.type).where(`${column}_uuid`, toDbScopeUuid(s));
48
+ });
49
+ }
50
+ });
51
+ }
52
+ /** Asignación vigente: sin expiración o con expiración futura. */
53
+ whereActive(query, column = 'expires_at') {
54
+ return query.where((builder) => {
55
+ builder.whereNull(column).orWhere(column, '>', new Date());
56
+ });
57
+ }
58
+ async findPermission(slug) {
59
+ return db.from('authz_permissions').where('slug', slug).select('uuid').first();
60
+ }
61
+ async findRoleOrFail(slug, scopeType) {
62
+ const role = await db
63
+ .from('authz_roles')
64
+ .where('slug', slug)
65
+ .where('scope_type', scopeType)
66
+ .select('uuid')
67
+ .first();
68
+ if (!role) {
69
+ throw new Exception(`Rol '${slug}' no existe en el catálogo para el nivel '${scopeType}'`, {
70
+ status: 422,
71
+ });
72
+ }
73
+ return role;
74
+ }
75
+ async authorize(subject, permission, scope) {
76
+ const perm = await this.findPermission(permission);
77
+ if (!perm)
78
+ return false;
79
+ const chain = await this.chain(scope);
80
+ const denied = await this.whereScopeIn(db
81
+ .from('authz_denies')
82
+ .where('holder_type', subject.type)
83
+ .where('holder_uuid', subject.uuid)
84
+ .where('permission_uuid', perm.uuid), 'scope', chain).first();
85
+ if (denied)
86
+ return false;
87
+ const granted = await this.whereActive(this.whereScopeIn(db
88
+ .from('authz_assignments as a')
89
+ .join('authz_role_permissions as rp', 'rp.role_uuid', 'a.role_uuid')
90
+ .where('rp.permission_uuid', perm.uuid)
91
+ .where('a.holder_type', subject.type)
92
+ .where('a.holder_uuid', subject.uuid), 'a.scope', chain), 'a.expires_at').first();
93
+ return Boolean(granted);
94
+ }
95
+ async grant(subject, role, scope, options = {}) {
96
+ const { uuid: roleUuid } = await this.findRoleOrFail(role, scope.type);
97
+ const expiresAt = options.expiresAt ?? null;
98
+ const existing = await this.whereScopeIn(db
99
+ .from('authz_assignments')
100
+ .where('holder_type', subject.type)
101
+ .where('holder_uuid', subject.uuid)
102
+ .where('role_uuid', roleUuid), 'scope', [scope]).first();
103
+ if (existing) {
104
+ // Idempotente: re-grant refresca la expiración (revivir una asignación
105
+ // expirada es un grant nuevo a todos los efectos).
106
+ await db.from('authz_assignments').where('uuid', existing.uuid).update({
107
+ expires_at: expiresAt,
108
+ });
109
+ return;
110
+ }
111
+ try {
112
+ await db.table('authz_assignments').insert({
113
+ uuid: uuidv7(),
114
+ holder_type: subject.type,
115
+ holder_uuid: subject.uuid,
116
+ role_uuid: roleUuid,
117
+ scope_type: scope.type,
118
+ scope_uuid: toDbScopeUuid(scope),
119
+ expires_at: expiresAt,
120
+ created_at: new Date(),
121
+ });
122
+ }
123
+ catch (error) {
124
+ // Carrera check-then-insert: el unique (que cubre también el nivel app
125
+ // vía centinela) la detecta — el perdedor degrada a update (idempotente).
126
+ const raced = await this.whereScopeIn(db
127
+ .from('authz_assignments')
128
+ .where('holder_type', subject.type)
129
+ .where('holder_uuid', subject.uuid)
130
+ .where('role_uuid', roleUuid), 'scope', [scope]).first();
131
+ if (!raced)
132
+ throw error;
133
+ await db.from('authz_assignments').where('uuid', raced.uuid).update({
134
+ expires_at: expiresAt,
135
+ });
136
+ }
137
+ }
138
+ async revoke(subject, role, scope) {
139
+ const roleRow = await db
140
+ .from('authz_roles')
141
+ .where('slug', role)
142
+ .where('scope_type', scope.type)
143
+ .select('uuid')
144
+ .first();
145
+ if (!roleRow)
146
+ return;
147
+ await this.whereScopeIn(db
148
+ .from('authz_assignments')
149
+ .where('holder_type', subject.type)
150
+ .where('holder_uuid', subject.uuid)
151
+ .where('role_uuid', roleRow.uuid), 'scope', [scope]).delete();
152
+ }
153
+ async hasRole(subject, role, scope) {
154
+ const chain = await this.chain(scope);
155
+ const found = await this.whereActive(this.whereScopeIn(db
156
+ .from('authz_assignments as a')
157
+ .join('authz_roles as r', 'r.uuid', 'a.role_uuid')
158
+ .where('r.slug', role)
159
+ .where('a.holder_type', subject.type)
160
+ .where('a.holder_uuid', subject.uuid), 'a.scope', chain), 'a.expires_at').first();
161
+ return Boolean(found);
162
+ }
163
+ async deny(subject, permission, scope) {
164
+ const perm = await this.findPermission(permission);
165
+ if (!perm) {
166
+ throw new Exception(`Permiso '${permission}' no existe en el catálogo`, { status: 422 });
167
+ }
168
+ const existing = await this.whereScopeIn(db
169
+ .from('authz_denies')
170
+ .where('holder_type', subject.type)
171
+ .where('holder_uuid', subject.uuid)
172
+ .where('permission_uuid', perm.uuid), 'scope', [scope]).first();
173
+ if (existing)
174
+ return;
175
+ try {
176
+ await db.table('authz_denies').insert({
177
+ uuid: uuidv7(),
178
+ holder_type: subject.type,
179
+ holder_uuid: subject.uuid,
180
+ permission_uuid: perm.uuid,
181
+ scope_type: scope.type,
182
+ scope_uuid: toDbScopeUuid(scope),
183
+ created_at: new Date(),
184
+ });
185
+ }
186
+ catch (error) {
187
+ // Carrera: si otro proceso insertó el mismo deny, el unique lo detecta
188
+ // y el resultado deseado ya existe.
189
+ const raced = await this.whereScopeIn(db
190
+ .from('authz_denies')
191
+ .where('holder_type', subject.type)
192
+ .where('holder_uuid', subject.uuid)
193
+ .where('permission_uuid', perm.uuid), 'scope', [scope]).first();
194
+ if (!raced)
195
+ throw error;
196
+ }
197
+ }
198
+ async removeDeny(subject, permission, scope) {
199
+ const perm = await this.findPermission(permission);
200
+ if (!perm)
201
+ return;
202
+ await this.whereScopeIn(db
203
+ .from('authz_denies')
204
+ .where('holder_type', subject.type)
205
+ .where('holder_uuid', subject.uuid)
206
+ .where('permission_uuid', perm.uuid), 'scope', [scope]).delete();
207
+ }
208
+ async listSubjects(role, scope) {
209
+ const rows = await this.whereActive(this.whereScopeIn(db
210
+ .from('authz_assignments as a')
211
+ .join('authz_roles as r', 'r.uuid', 'a.role_uuid')
212
+ .where('r.slug', role)
213
+ .where('r.scope_type', scope.type), 'a.scope', [scope]), 'a.expires_at').distinct('a.holder_type', 'a.holder_uuid');
214
+ return rows.map((row) => ({ type: row.holder_type, uuid: row.holder_uuid }));
215
+ }
216
+ async listRoles(subject, scope) {
217
+ const rows = await this.whereActive(this.whereScopeIn(db
218
+ .from('authz_assignments as a')
219
+ .join('authz_roles as r', 'r.uuid', 'a.role_uuid')
220
+ .where('a.holder_type', subject.type)
221
+ .where('a.holder_uuid', subject.uuid), 'a.scope', [scope]), 'a.expires_at').distinct('r.slug');
222
+ return rows.map((row) => row.slug);
223
+ }
224
+ async listRoleScopes(subject, scopeType) {
225
+ const rows = await this.whereActive(db
226
+ .from('authz_assignments as a')
227
+ .where('a.holder_type', subject.type)
228
+ .where('a.holder_uuid', subject.uuid)
229
+ .where('a.scope_type', scopeType), 'a.expires_at').distinct('a.scope_type', 'a.scope_uuid');
230
+ return rows.map((row) => ({ type: row.scope_type, uuid: fromDbScopeUuid(row.scope_uuid) }));
231
+ }
232
+ async listScopes(subject, permission) {
233
+ const perm = await this.findPermission(permission);
234
+ if (!perm)
235
+ return [];
236
+ const rows = await this.whereActive(db
237
+ .from('authz_assignments as a')
238
+ .join('authz_role_permissions as rp', 'rp.role_uuid', 'a.role_uuid')
239
+ .where('rp.permission_uuid', perm.uuid)
240
+ .where('a.holder_type', subject.type)
241
+ .where('a.holder_uuid', subject.uuid), 'a.expires_at').distinct('a.scope_type', 'a.scope_uuid');
242
+ const denies = await db
243
+ .from('authz_denies')
244
+ .where('holder_type', subject.type)
245
+ .where('holder_uuid', subject.uuid)
246
+ .where('permission_uuid', perm.uuid)
247
+ .select('scope_type', 'scope_uuid');
248
+ const deniedKeys = new Set(denies.map((d) => `${d.scope_type}:${fromDbScopeUuid(d.scope_uuid) ?? ''}`));
249
+ const result = [];
250
+ for (const row of rows) {
251
+ const candidate = { type: row.scope_type, uuid: fromDbScopeUuid(row.scope_uuid) };
252
+ const chain = await this.chain(candidate);
253
+ const blocked = chain.some((s) => deniedKeys.has(`${s.type}:${s.uuid ?? ''}`));
254
+ if (!blocked)
255
+ result.push(candidate);
256
+ }
257
+ return result;
258
+ }
259
+ }
260
+ //# sourceMappingURL=database_driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database_driver.js","sourceRoot":"","sources":["../../../src/drivers/database_driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ import type { AuthorizationDriver, GrantOptions, ScopeAncestorsResolver, ScopeRef, ScopeType, SubjectRef } from '../types.js';
2
+ /**
3
+ * Driver `openfga` — los HECHOS (asignaciones y denies) viven en un servidor
4
+ * OpenFGA; el CATÁLOGO (roles/permisos/vínculos) y la JERARQUÍA (orgs/units)
5
+ * siguen siendo metadata local en las tablas del chasis (split de propiedad
6
+ * de datos del análisis: cambiar de driver = migrar una tabla de hechos).
7
+ *
8
+ * Modelo FGA genérico (ver `openFgaAuthorizationModel()`):
9
+ * - `role_binding:<scopeKey>|<rol>` #assignee → asignación de rol
10
+ * - `deny_binding:<scopeKey>|<perm>` #denied → deny explícito
11
+ * - Expiración vía condition `not_expired` (valid_until en la tupla,
12
+ * current_time en cada check) — ni scheduler necesita.
13
+ *
14
+ * La herencia se resuelve igual que en el driver database: la cadena de
15
+ * scopes se calcula localmente (el árbol lo declara el consumidor vía
16
+ * `resolveAncestors`) y se consulta FGA por batchCheck sobre la cadena.
17
+ *
18
+ * NADA del dominio está cableado: los holders llegan como `holderTypes`
19
+ * (morph name → tipo FGA) y los niveles de scope se derivan del propio
20
+ * `ScopeRef`, así que un consumidor con otros guards u otra jerarquía solo
21
+ * cambia config.
22
+ */
23
+ /**
24
+ * Mapa morph name → tipo del modelo FGA (`users` → `user`). Debe ser el
25
+ * MISMO con el que se generó el authorization model del store.
26
+ */
27
+ export type HolderTypeMap = Record<string, string>;
28
+ /**
29
+ * El authorization model en formato JSON del API de FGA, generado a partir
30
+ * de los holders del consumidor. El mismo mapa debe usarse al construir el
31
+ * driver: si difieren, los checks no encuentran las tuplas.
32
+ */
33
+ export declare function openFgaAuthorizationModel(holderTypeMap: HolderTypeMap): any;
34
+ /**
35
+ * Crea un store nuevo + escribe el authorization model derivado de los
36
+ * holders del consumidor. Para bootstrap de un appliance o del harness de
37
+ * tests. El `name` lo decide el caller (el comando openfga:provision
38
+ * resuelve APP_NAME del entorno — el motor no lee env).
39
+ */
40
+ export declare function provisionOpenFgaStore(apiUrl: string, name: string, holderTypeMap: HolderTypeMap): Promise<{
41
+ storeId: string;
42
+ modelId: string;
43
+ }>;
44
+ export interface OpenFgaDriverOptions {
45
+ apiUrl: string;
46
+ storeId: string;
47
+ /** Pin del model; si se omite, FGA usa el último. */
48
+ modelId?: string;
49
+ /** Jerarquía del consumidor (ver ScopeAncestorsResolver). */
50
+ resolveAncestors?: ScopeAncestorsResolver;
51
+ /**
52
+ * Holders del consumidor: morph name → tipo FGA. Debe coincidir con el
53
+ * mapa usado al escribir el authorization model del store.
54
+ */
55
+ holderTypes: HolderTypeMap;
56
+ }
57
+ export interface ImportFactsResult {
58
+ assignments: number;
59
+ denies: number;
60
+ skippedExpired: number;
61
+ dryRun: boolean;
62
+ }
63
+ /**
64
+ * Migración de hechos database → openfga: copia las asignaciones vigentes y
65
+ * los denies de las tablas `authz_*` como tuples del store FGA.
66
+ *
67
+ * - COPIA, no mueve: las tablas locales quedan intactas → el rollback es
68
+ * volver a AUTHZ_DRIVER=database (solo se pierde lo escrito mientras se
69
+ * operó con openfga). El catálogo y la jerarquía nunca migran: son
70
+ * metadata local para ambos drivers.
71
+ * - Idempotente: re-ejecutar no duplica (onDuplicateWrites: Ignore).
72
+ * - Las asignaciones ya expiradas se saltan (no tiene sentido copiarlas);
73
+ * las de expiración futura viajan con la condition `not_expired`.
74
+ */
75
+ export declare function importAuthzFactsToOpenFga(options: OpenFgaDriverOptions & {
76
+ dryRun?: boolean;
77
+ }): Promise<ImportFactsResult>;
78
+ export declare class OpenFgaAuthorizationDriver implements AuthorizationDriver {
79
+ private client;
80
+ private resolveAncestors;
81
+ private holderTypes;
82
+ constructor(options: OpenFgaDriverOptions);
83
+ private chain;
84
+ private fgaSubject;
85
+ /**
86
+ * batchCheck troceado al límite del servidor FGA (50 checks/request) y con
87
+ * verificación de completitud: TODOS los checks deben volver respondidos.
88
+ */
89
+ private batchCheckAll;
90
+ private findPermission;
91
+ private findRoleOrFail;
92
+ /** Roles del catálogo que conceden el permiso, agrupados por scope_type. */
93
+ private rolesGranting;
94
+ authorize(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<boolean>;
95
+ grant(subject: SubjectRef, role: string, scope: ScopeRef, options?: GrantOptions): Promise<void>;
96
+ revoke(subject: SubjectRef, role: string, scope: ScopeRef): Promise<void>;
97
+ hasRole(subject: SubjectRef, role: string, scope: ScopeRef): Promise<boolean>;
98
+ deny(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<void>;
99
+ removeDeny(subject: SubjectRef, permission: string, scope: ScopeRef): Promise<void>;
100
+ listSubjects(role: string, scope: ScopeRef): Promise<SubjectRef[]>;
101
+ /** role_binding ids del subject (asignaciones directas vigentes). */
102
+ private listBindings;
103
+ listRoles(subject: SubjectRef, scope: ScopeRef): Promise<string[]>;
104
+ listRoleScopes(subject: SubjectRef, scopeType: ScopeType): Promise<ScopeRef[]>;
105
+ listScopes(subject: SubjectRef, permission: string): Promise<ScopeRef[]>;
106
+ }
107
+ //# sourceMappingURL=openfga_driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfga_driver.d.ts","sourceRoot":"","sources":["../../../src/drivers/openfga_driver.ts"],"names":[],"mappings":"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"}