@mahiframework/permissions 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/assignee.d.ts +57 -0
  4. package/dist/assignee.d.ts.map +1 -0
  5. package/dist/assignee.js +46 -0
  6. package/dist/assignee.js.map +1 -0
  7. package/dist/commands/permissions-cache-reset.d.ts +20 -0
  8. package/dist/commands/permissions-cache-reset.d.ts.map +1 -0
  9. package/dist/commands/permissions-cache-reset.js +24 -0
  10. package/dist/commands/permissions-cache-reset.js.map +1 -0
  11. package/dist/commands/permissions-check.d.ts +37 -0
  12. package/dist/commands/permissions-check.d.ts.map +1 -0
  13. package/dist/commands/permissions-check.js +92 -0
  14. package/dist/commands/permissions-check.js.map +1 -0
  15. package/dist/commands/permissions-show.d.ts +20 -0
  16. package/dist/commands/permissions-show.d.ts.map +1 -0
  17. package/dist/commands/permissions-show.js +49 -0
  18. package/dist/commands/permissions-show.js.map +1 -0
  19. package/dist/errors.d.ts +77 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +106 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/index.d.ts +25 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +18 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/listeners/invalidate-permission-cache.listener.d.ts +39 -0
  28. package/dist/listeners/invalidate-permission-cache.listener.d.ts.map +1 -0
  29. package/dist/listeners/invalidate-permission-cache.listener.js +50 -0
  30. package/dist/listeners/invalidate-permission-cache.listener.js.map +1 -0
  31. package/dist/middleware/current-subject.d.ts +23 -0
  32. package/dist/middleware/current-subject.d.ts.map +1 -0
  33. package/dist/middleware/current-subject.js +31 -0
  34. package/dist/middleware/current-subject.js.map +1 -0
  35. package/dist/middleware/permission.d.ts +19 -0
  36. package/dist/middleware/permission.d.ts.map +1 -0
  37. package/dist/middleware/permission.js +34 -0
  38. package/dist/middleware/permission.js.map +1 -0
  39. package/dist/middleware/role-or-permission.d.ts +29 -0
  40. package/dist/middleware/role-or-permission.d.ts.map +1 -0
  41. package/dist/middleware/role-or-permission.js +45 -0
  42. package/dist/middleware/role-or-permission.js.map +1 -0
  43. package/dist/middleware/role.d.ts +21 -0
  44. package/dist/middleware/role.d.ts.map +1 -0
  45. package/dist/middleware/role.js +36 -0
  46. package/dist/middleware/role.js.map +1 -0
  47. package/dist/migrations/0001_create_permission_tables.d.ts +63 -0
  48. package/dist/migrations/0001_create_permission_tables.d.ts.map +1 -0
  49. package/dist/migrations/0001_create_permission_tables.js +113 -0
  50. package/dist/migrations/0001_create_permission_tables.js.map +1 -0
  51. package/dist/models/permission.model.d.ts +58 -0
  52. package/dist/models/permission.model.d.ts.map +1 -0
  53. package/dist/models/permission.model.js +18 -0
  54. package/dist/models/permission.model.js.map +1 -0
  55. package/dist/models/role.model.d.ts +60 -0
  56. package/dist/models/role.model.d.ts.map +1 -0
  57. package/dist/models/role.model.js +18 -0
  58. package/dist/models/role.model.js.map +1 -0
  59. package/dist/permission-map.d.ts +97 -0
  60. package/dist/permission-map.d.ts.map +1 -0
  61. package/dist/permission-map.js +93 -0
  62. package/dist/permission-map.js.map +1 -0
  63. package/dist/permission-registrar.d.ts +183 -0
  64. package/dist/permission-registrar.d.ts.map +1 -0
  65. package/dist/permission-registrar.js +573 -0
  66. package/dist/permission-registrar.js.map +1 -0
  67. package/dist/permissions-config.d.ts +80 -0
  68. package/dist/permissions-config.d.ts.map +1 -0
  69. package/dist/permissions-config.js +22 -0
  70. package/dist/permissions-config.js.map +1 -0
  71. package/dist/permissions-facade.d.ts +74 -0
  72. package/dist/permissions-facade.d.ts.map +1 -0
  73. package/dist/permissions-facade.js +131 -0
  74. package/dist/permissions-facade.js.map +1 -0
  75. package/dist/permissions-service-provider.d.ts +91 -0
  76. package/dist/permissions-service-provider.d.ts.map +1 -0
  77. package/dist/permissions-service-provider.js +137 -0
  78. package/dist/permissions-service-provider.js.map +1 -0
  79. package/dist/relations.d.ts +63 -0
  80. package/dist/relations.d.ts.map +1 -0
  81. package/dist/relations.js +62 -0
  82. package/dist/relations.js.map +1 -0
  83. package/dist/request-cache.d.ts +50 -0
  84. package/dist/request-cache.d.ts.map +1 -0
  85. package/dist/request-cache.js +40 -0
  86. package/dist/request-cache.js.map +1 -0
  87. package/dist/tokens.d.ts +15 -0
  88. package/dist/tokens.d.ts.map +1 -0
  89. package/dist/tokens.js +15 -0
  90. package/dist/tokens.js.map +1 -0
  91. package/package.json +69 -0
@@ -0,0 +1,58 @@
1
+ import { type BelongsToMany } from "@mahiframework/database";
2
+ import type { DateTime } from "@mahiframework/datetime";
3
+ import { Role } from "./role.model.js";
4
+ /**
5
+ * A single named thing a subject may do: `"posts.edit"`, `"billing.view"`.
6
+ *
7
+ * A permission is a *name*, not a rule. It carries no model class, no
8
+ * row, no callback — holding `"posts.edit"` says nothing about *which*
9
+ * post. That is the Gate's job, and the deliberate reason this package's
10
+ * gate hook abstains the moment a check carries a model argument (see
11
+ * `PermissionsServiceProvider.gates()`).
12
+ *
13
+ * `guard_name` scopes the permission to one of the app's auth guards, so
14
+ * an `api`-guard permission cannot satisfy a `web`-guard check. It is NOT
15
+ * NULL and has no wildcard value: `(name, guard_name)` is unique, and a
16
+ * nullable column cannot be made unique portably (`nullsNotDistinct` is
17
+ * Postgres 15+ only and throws on SQLite/MySQL), so "applies to every
18
+ * guard" would have meant unlimited duplicate rows in dev.
19
+ */
20
+ export interface PermissionAttributes {
21
+ /** A snowflake, hence `bigint`: a 64-bit id does not fit a `number`. */
22
+ id: bigint;
23
+ name: string;
24
+ /** The auth guard this permission belongs to, e.g. `"web"`. Never null. */
25
+ guard_name: string;
26
+ created_at: DateTime;
27
+ updated_at: DateTime;
28
+ /** Roles granting this permission, across `role_has_permissions`. */
29
+ roles: BelongsToMany<Role>;
30
+ }
31
+ declare const Permission_base: import("@mahiframework/database").ModelStatics<PermissionAttributes, {
32
+ readonly table: "permissions";
33
+ readonly primaryKey: "id";
34
+ readonly keyType: import("@mahiframework/database").KeyStrategy<bigint>;
35
+ readonly morphName: "Permission";
36
+ }> & (new (attributes?: Partial<import("@mahiframework/database").WritableAttributes<PermissionAttributes, {
37
+ readonly table: "permissions";
38
+ readonly primaryKey: "id";
39
+ readonly keyType: import("@mahiframework/database").KeyStrategy<bigint>;
40
+ readonly morphName: "Permission";
41
+ }>> | undefined) => import("@mahiframework/database").BaseModel & import("@mahiframework/database").ResolvedAttributes<PermissionAttributes> & {
42
+ relations: import("@mahiframework/database").RelationBuildersFor<PermissionAttributes>;
43
+ } & import("@mahiframework/database").HasAttributes<PermissionAttributes>);
44
+ export declare class Permission extends Permission_base {
45
+ static relationships: {
46
+ roles: {
47
+ readonly type: "belongsToMany";
48
+ readonly related: () => import("@mahiframework/database").ModelLike;
49
+ readonly options: import("@mahiframework/database").BelongsToManyOptions<any, any>;
50
+ readonly __brand?: {
51
+ kind: "belongsToMany";
52
+ related: Role;
53
+ } | undefined;
54
+ };
55
+ };
56
+ }
57
+ export {};
58
+ //# sourceMappingURL=permission.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.model.d.ts","sourceRoot":"","sources":["../../src/models/permission.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAEnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,QAAQ,CAAC;IACrB,UAAU,EAAE,QAAQ,CAAC;IACrB,qEAAqE;IACrE,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;CAC5B;;;;;;;;;;;;;;AAED,qBAAa,UAAW,SAAQ,eAK9B;IACA,OAAgB,aAAa;;;;;;;;;;MAM3B;CACH"}
@@ -0,0 +1,18 @@
1
+ import { Model, belongsToMany } from "@mahiframework/database";
2
+ import { snowflake } from "@mahiframework/snowflake";
3
+ import { Role } from "./role.model.js";
4
+ export class Permission extends Model()({
5
+ table: "permissions",
6
+ primaryKey: "id",
7
+ keyType: snowflake(),
8
+ morphName: "Permission",
9
+ }) {
10
+ static relationships = {
11
+ roles: belongsToMany(() => Role, {
12
+ pivotTable: "role_has_permissions",
13
+ foreignPivotKey: "permission_id",
14
+ relatedPivotKey: "role_id",
15
+ }),
16
+ };
17
+ }
18
+ //# sourceMappingURL=permission.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.model.js","sourceRoot":"","sources":["../../src/models/permission.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAsB,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AA8BvC,MAAM,OAAO,UAAW,SAAQ,KAAK,EAAwB,CAAC;IAC5D,KAAK,EAAE,aAAa;IACpB,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,SAAS,EAAE;IACpB,SAAS,EAAE,YAAY;CACxB,CAAC;IACA,MAAM,CAAU,aAAa,GAAG;QAC9B,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE;YAC/B,UAAU,EAAE,sBAAsB;YAClC,eAAe,EAAE,eAAe;YAChC,eAAe,EAAE,SAAS;SAC3B,CAAC;KACH,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { type BelongsToMany } from "@mahiframework/database";
2
+ import type { DateTime } from "@mahiframework/datetime";
3
+ import { Permission } from "./permission.model.js";
4
+ /**
5
+ * A named bundle of permissions a subject can hold.
6
+ *
7
+ * The indirection is the whole point: a subject holding `"editor"`
8
+ * inherits every permission attached to it, so changing what an editor
9
+ * may do is one write instead of one per user. A permission can also be
10
+ * granted to a subject directly (`model_has_permissions`), which
11
+ * supplements roles rather than replacing them.
12
+ *
13
+ * `guard_name` is NOT NULL and scoped exactly as `Permission`'s is; see
14
+ * that model for why there is no wildcard guard.
15
+ *
16
+ * There is intentionally no `users` relation here. The inverse of the
17
+ * polymorphic assignment is a `morphedByMany`, which needs the *app's*
18
+ * model class, and this package does not know it. The docs show the three
19
+ * lines an app writes to declare it, and flag the `type`-defaulting
20
+ * asymmetry between `morphToMany` and `morphedByMany`.
21
+ */
22
+ export interface RoleAttributes {
23
+ /** A snowflake, hence `bigint`: a 64-bit id does not fit a `number`. */
24
+ id: bigint;
25
+ name: string;
26
+ /** The auth guard this role belongs to, e.g. `"web"`. Never null. */
27
+ guard_name: string;
28
+ created_at: DateTime;
29
+ updated_at: DateTime;
30
+ /** Permissions this role grants, across `role_has_permissions`. */
31
+ permissions: BelongsToMany<Permission>;
32
+ }
33
+ declare const Role_base: import("@mahiframework/database").ModelStatics<RoleAttributes, {
34
+ readonly table: "roles";
35
+ readonly primaryKey: "id";
36
+ readonly keyType: import("@mahiframework/database").KeyStrategy<bigint>;
37
+ readonly morphName: "Role";
38
+ }> & (new (attributes?: Partial<import("@mahiframework/database").WritableAttributes<RoleAttributes, {
39
+ readonly table: "roles";
40
+ readonly primaryKey: "id";
41
+ readonly keyType: import("@mahiframework/database").KeyStrategy<bigint>;
42
+ readonly morphName: "Role";
43
+ }>> | undefined) => import("@mahiframework/database").BaseModel & import("@mahiframework/database").ResolvedAttributes<RoleAttributes> & {
44
+ relations: import("@mahiframework/database").RelationBuildersFor<RoleAttributes>;
45
+ } & import("@mahiframework/database").HasAttributes<RoleAttributes>);
46
+ export declare class Role extends Role_base {
47
+ static relationships: {
48
+ permissions: {
49
+ readonly type: "belongsToMany";
50
+ readonly related: () => import("@mahiframework/database").ModelLike;
51
+ readonly options: import("@mahiframework/database").BelongsToManyOptions<any, any>;
52
+ readonly __brand?: {
53
+ kind: "belongsToMany";
54
+ related: Permission;
55
+ } | undefined;
56
+ };
57
+ };
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=role.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role.model.d.ts","sourceRoot":"","sources":["../../src/models/role.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAEnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,QAAQ,CAAC;IACrB,UAAU,EAAE,QAAQ,CAAC;IACrB,mEAAmE;IACnE,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CACxC;;;;;;;;;;;;;;AAED,qBAAa,IAAK,SAAQ,SAKxB;IACA,OAAgB,aAAa;;;;;;;;;;MAM3B;CACH"}
@@ -0,0 +1,18 @@
1
+ import { Model, belongsToMany } from "@mahiframework/database";
2
+ import { snowflake } from "@mahiframework/snowflake";
3
+ import { Permission } from "./permission.model.js";
4
+ export class Role extends Model()({
5
+ table: "roles",
6
+ primaryKey: "id",
7
+ keyType: snowflake(),
8
+ morphName: "Role",
9
+ }) {
10
+ static relationships = {
11
+ permissions: belongsToMany(() => Permission, {
12
+ pivotTable: "role_has_permissions",
13
+ foreignPivotKey: "role_id",
14
+ relatedPivotKey: "permission_id",
15
+ }),
16
+ };
17
+ }
18
+ //# sourceMappingURL=role.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role.model.js","sourceRoot":"","sources":["../../src/models/role.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAsB,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAgCnD,MAAM,OAAO,IAAK,SAAQ,KAAK,EAAkB,CAAC;IAChD,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,SAAS,EAAE;IACpB,SAAS,EAAE,MAAM;CAClB,CAAC;IACA,MAAM,CAAU,aAAa,GAAG;QAC9B,WAAW,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE;YAC3C,UAAU,EAAE,sBAAsB;YAClC,eAAe,EAAE,SAAS;YAC1B,eAAe,EAAE,eAAe;SACjC,CAAC;KACH,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The cached snapshot of every role, every permission, and which
3
+ * permissions each role grants.
4
+ *
5
+ * ONE cache entry for the whole table set, which is spatie's model and
6
+ * the only one `@mahiframework/cache` can support: it has no tags, so
7
+ * there is no flush-by-pattern, and every key written must be a key
8
+ * nameable later. One key is nameable. It is also bounded by the number
9
+ * of roles and permissions an app defines (tens, not millions), unlike a
10
+ * per-subject cache which grows with the user table.
11
+ *
12
+ * What is NOT in here: who holds what. `model_has_roles` scales with
13
+ * users, so caching it under one key would mean one entry growing without
14
+ * bound and invalidated by every assignment anywhere. Assignments are a
15
+ * query, memoised per request (see `request-cache.ts`).
16
+ *
17
+ * ## The serialisation boundary
18
+ *
19
+ * `RedisCacheStore` and `FileCacheStore` persist with `JSON.stringify`,
20
+ * and `JSON.stringify` THROWS on a `bigint` — deliberately, per
21
+ * `@mahiframework/snowflake`: an id cannot be serialised without a
22
+ * decision being made about it. The decision here is a decimal string,
23
+ * matching `Resource` and `Model.toJSON()`.
24
+ *
25
+ * So the cached form (`SerializedPermissionMap`) carries strings and the
26
+ * in-memory form (`PermissionMap`) carries `bigint`, with
27
+ * `serializeMap`/`deserializeMap` the only crossing. Getting this wrong
28
+ * is a bug that cannot be reproduced on `ArrayCacheStore`, which passes a
29
+ * `bigint` through happily — it would appear only once an app switched to
30
+ * redis or file, i.e. in production. Hence one module, two functions, and
31
+ * a test that round-trips through `JSON.parse(JSON.stringify(...))`.
32
+ */
33
+ /** A role as cached: its identity plus the permissions it grants. */
34
+ export interface MappedRole {
35
+ id: bigint;
36
+ name: string;
37
+ guardName: string;
38
+ /** Permission ids from `role_has_permissions`. */
39
+ permissionIds: bigint[];
40
+ }
41
+ /** A permission as cached. */
42
+ export interface MappedPermission {
43
+ id: bigint;
44
+ name: string;
45
+ guardName: string;
46
+ }
47
+ /**
48
+ * The in-memory map, with lookup indexes derived once at
49
+ * deserialisation rather than scanned per check.
50
+ *
51
+ * Keyed by `name` + `guardName` together, because the same name can exist
52
+ * once per guard and a `web` role must not answer an `api` lookup.
53
+ */
54
+ export interface PermissionMap {
55
+ roles: MappedRole[];
56
+ permissions: MappedPermission[];
57
+ /** `"{guard}\u0000{name}"` -> role. */
58
+ roleByName: Map<string, MappedRole>;
59
+ /** `"{guard}\u0000{name}"` -> permission. */
60
+ permissionByName: Map<string, MappedPermission>;
61
+ /** Role id -> role, for turning an assignment row into permissions. */
62
+ roleById: Map<bigint, MappedRole>;
63
+ /** Permission id -> permission, for naming a directly-assigned permission. */
64
+ permissionById: Map<bigint, MappedPermission>;
65
+ }
66
+ /** The JSON-safe form actually written to the cache store. */
67
+ export interface SerializedPermissionMap {
68
+ roles: Array<{
69
+ id: string;
70
+ name: string;
71
+ guardName: string;
72
+ permissionIds: string[];
73
+ }>;
74
+ permissions: Array<{
75
+ id: string;
76
+ name: string;
77
+ guardName: string;
78
+ }>;
79
+ }
80
+ /**
81
+ * `"{guard}\0{name}"`.
82
+ *
83
+ * A NUL separator rather than `:` because a permission name is
84
+ * app-chosen and may contain anything printable (`"billing:view"` is a
85
+ * perfectly ordinary name), whereas a guard name comes from
86
+ * `config/auth.ts`. Without a separator that cannot appear in either
87
+ * half, `("web", "a:b")` and `("web:a", "b")` would collide — and the
88
+ * collision would grant a permission nobody assigned.
89
+ */
90
+ export declare function mapKey(guardName: string, name: string): string;
91
+ /** Strip the `bigint`s out, for the cache store. */
92
+ export declare function serializeMap(map: PermissionMap): SerializedPermissionMap;
93
+ /** Put the `bigint`s back, and build the lookup indexes. */
94
+ export declare function deserializeMap(serialized: SerializedPermissionMap): PermissionMap;
95
+ /** Assemble a map and its indexes from freshly-read rows. */
96
+ export declare function buildMap(roles: MappedRole[], permissions: MappedPermission[]): PermissionMap;
97
+ //# sourceMappingURL=permission-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-map.d.ts","sourceRoot":"","sources":["../src/permission-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8BAA8B;AAC9B,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,uCAAuC;IACvC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,6CAA6C;IAC7C,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAChD,uEAAuE;IACvE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8EAA8E;IAC9E,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC/C;AAED,8DAA8D;AAC9D,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACvF,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,oDAAoD;AACpD,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,uBAAuB,CAcxE;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,UAAU,EAAE,uBAAuB,GAAG,aAAa,CAejF;AAED,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,aAAa,CAiB5F"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The cached snapshot of every role, every permission, and which
3
+ * permissions each role grants.
4
+ *
5
+ * ONE cache entry for the whole table set, which is spatie's model and
6
+ * the only one `@mahiframework/cache` can support: it has no tags, so
7
+ * there is no flush-by-pattern, and every key written must be a key
8
+ * nameable later. One key is nameable. It is also bounded by the number
9
+ * of roles and permissions an app defines (tens, not millions), unlike a
10
+ * per-subject cache which grows with the user table.
11
+ *
12
+ * What is NOT in here: who holds what. `model_has_roles` scales with
13
+ * users, so caching it under one key would mean one entry growing without
14
+ * bound and invalidated by every assignment anywhere. Assignments are a
15
+ * query, memoised per request (see `request-cache.ts`).
16
+ *
17
+ * ## The serialisation boundary
18
+ *
19
+ * `RedisCacheStore` and `FileCacheStore` persist with `JSON.stringify`,
20
+ * and `JSON.stringify` THROWS on a `bigint` — deliberately, per
21
+ * `@mahiframework/snowflake`: an id cannot be serialised without a
22
+ * decision being made about it. The decision here is a decimal string,
23
+ * matching `Resource` and `Model.toJSON()`.
24
+ *
25
+ * So the cached form (`SerializedPermissionMap`) carries strings and the
26
+ * in-memory form (`PermissionMap`) carries `bigint`, with
27
+ * `serializeMap`/`deserializeMap` the only crossing. Getting this wrong
28
+ * is a bug that cannot be reproduced on `ArrayCacheStore`, which passes a
29
+ * `bigint` through happily — it would appear only once an app switched to
30
+ * redis or file, i.e. in production. Hence one module, two functions, and
31
+ * a test that round-trips through `JSON.parse(JSON.stringify(...))`.
32
+ */
33
+ /**
34
+ * `"{guard}\0{name}"`.
35
+ *
36
+ * A NUL separator rather than `:` because a permission name is
37
+ * app-chosen and may contain anything printable (`"billing:view"` is a
38
+ * perfectly ordinary name), whereas a guard name comes from
39
+ * `config/auth.ts`. Without a separator that cannot appear in either
40
+ * half, `("web", "a:b")` and `("web:a", "b")` would collide — and the
41
+ * collision would grant a permission nobody assigned.
42
+ */
43
+ export function mapKey(guardName, name) {
44
+ return `${guardName}\u0000${name}`;
45
+ }
46
+ /** Strip the `bigint`s out, for the cache store. */
47
+ export function serializeMap(map) {
48
+ return {
49
+ roles: map.roles.map((role) => ({
50
+ id: String(role.id),
51
+ name: role.name,
52
+ guardName: role.guardName,
53
+ permissionIds: role.permissionIds.map(String),
54
+ })),
55
+ permissions: map.permissions.map((permission) => ({
56
+ id: String(permission.id),
57
+ name: permission.name,
58
+ guardName: permission.guardName,
59
+ })),
60
+ };
61
+ }
62
+ /** Put the `bigint`s back, and build the lookup indexes. */
63
+ export function deserializeMap(serialized) {
64
+ const roles = serialized.roles.map((role) => ({
65
+ id: BigInt(role.id),
66
+ name: role.name,
67
+ guardName: role.guardName,
68
+ permissionIds: role.permissionIds.map(BigInt),
69
+ }));
70
+ const permissions = serialized.permissions.map((permission) => ({
71
+ id: BigInt(permission.id),
72
+ name: permission.name,
73
+ guardName: permission.guardName,
74
+ }));
75
+ return buildMap(roles, permissions);
76
+ }
77
+ /** Assemble a map and its indexes from freshly-read rows. */
78
+ export function buildMap(roles, permissions) {
79
+ const roleByName = new Map();
80
+ const roleById = new Map();
81
+ const permissionByName = new Map();
82
+ const permissionById = new Map();
83
+ for (const role of roles) {
84
+ roleByName.set(mapKey(role.guardName, role.name), role);
85
+ roleById.set(role.id, role);
86
+ }
87
+ for (const permission of permissions) {
88
+ permissionByName.set(mapKey(permission.guardName, permission.name), permission);
89
+ permissionById.set(permission.id, permission);
90
+ }
91
+ return { roles, permissions, roleByName, permissionByName, roleById, permissionById };
92
+ }
93
+ //# sourceMappingURL=permission-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-map.js","sourceRoot":"","sources":["../src/permission-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AA4CH;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CAAC,SAAiB,EAAE,IAAY;IACpD,OAAO,GAAG,SAAS,SAAS,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,GAAkB;IAC7C,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9B,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;SAC9C,CAAC,CAAC;QACH,WAAW,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAChD,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YACzB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,SAAS,EAAE,UAAU,CAAC,SAAS;SAChC,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc,CAAC,UAAmC;IAChE,MAAM,KAAK,GAAiB,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1D,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;KAC9C,CAAC,CAAC,CAAC;IAEJ,MAAM,WAAW,GAAuB,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAClF,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QACzB,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,SAAS,EAAE,UAAU,CAAC,SAAS;KAChC,CAAC,CAAC,CAAC;IAEJ,OAAO,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,QAAQ,CAAC,KAAmB,EAAE,WAA+B;IAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC/C,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC7D,MAAM,cAAc,GAAG,IAAI,GAAG,EAA4B,CAAC;IAE3D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;QACxD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;QAChF,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;AACxF,CAAC"}
@@ -0,0 +1,183 @@
1
+ import type { Application } from "@mahiframework/core";
2
+ import { type Assignee } from "./assignee.js";
3
+ import { type PermissionMap } from "./permission-map.js";
4
+ import { Permission } from "./models/permission.model.js";
5
+ import { Role } from "./models/role.model.js";
6
+ import type { ResolvedPermissionsConfig } from "./permissions-config.js";
7
+ /** Options every name-addressed call accepts. */
8
+ export interface GuardOption {
9
+ /** The auth guard to scope to. Defaults to config `guard`, then `auth.default`. */
10
+ guard?: string;
11
+ }
12
+ /** A role named by name, id, or instance. */
13
+ export type RoleRef = string | bigint | Role;
14
+ /** A permission named by name, id, or instance. */
15
+ export type PermissionRef = string | bigint | Permission;
16
+ /** Roles and permissions, their relationships, and who holds them. */
17
+ export declare class PermissionRegistrar {
18
+ private readonly app;
19
+ private readonly config;
20
+ constructor(app: Application, config: ResolvedPermissionsConfig);
21
+ /**
22
+ * The guard a call operates on.
23
+ *
24
+ * `auth.default` is the last resort rather than the ambient
25
+ * `currentGuard()`, which would read the guard that authenticated THIS
26
+ * request. That is tempting and wrong: it throws outside a request
27
+ * scope (`MissingAuthContextError`), so a queue job or a seeder
28
+ * assigning a role would crash, and it would make the same
29
+ * `createRole("admin")` produce a different row depending on who
30
+ * happened to call it.
31
+ *
32
+ * Throws rather than defaulting to `""`: `guard_name` is NOT NULL with
33
+ * no wildcard, so an unresolvable guard has no safe representation, and
34
+ * a role stamped with the empty string is a role no check will ever
35
+ * match.
36
+ */
37
+ guardName(options?: GuardOption): string;
38
+ private defaultGuard;
39
+ /**
40
+ * The role/permission map, from cache or the database.
41
+ *
42
+ * `remember()` treats `undefined` as its miss sentinel, so the loader
43
+ * must never return it — this one always returns an object, even for an
44
+ * app with no roles at all.
45
+ *
46
+ * The map crosses the cache boundary serialised (ids as strings): see
47
+ * `permission-map.ts` for why a `bigint` cannot be cached.
48
+ */
49
+ map(): Promise<PermissionMap>;
50
+ /** Read every role, permission, and role-permission link. Three queries, no joins. */
51
+ private loadMap;
52
+ /**
53
+ * Drop the cached map and every memoised assignment.
54
+ *
55
+ * Called by every write here, and by the model-event listeners for
56
+ * writes that bypass this class (a seeder calling `Role.create()`).
57
+ * Forgets exactly one key — `@mahiframework/cache` has no tags, so
58
+ * there is no flush-by-pattern, and `flush()` would take out the app's
59
+ * entire cache.
60
+ */
61
+ forgetCache(): Promise<void>;
62
+ private cache;
63
+ /** Create a role. Throws if one already exists with this name for this guard. */
64
+ createRole(name: string, options?: GuardOption): Promise<Role>;
65
+ /** Create a permission. Throws if one already exists with this name for this guard. */
66
+ createPermission(name: string, options?: GuardOption): Promise<Permission>;
67
+ /** Create a role only if it doesn't exist, returning either way. */
68
+ findOrCreateRole(name: string, options?: GuardOption): Promise<Role>;
69
+ /** Create a permission only if it doesn't exist, returning either way. */
70
+ findOrCreatePermission(name: string, options?: GuardOption): Promise<Permission>;
71
+ /** The role with this name, or `RoleNotFoundError`. */
72
+ findRole(name: string, options?: GuardOption): Promise<Role>;
73
+ /** The permission with this name, or `PermissionNotFoundError`. */
74
+ findPermission(name: string, options?: GuardOption): Promise<Permission>;
75
+ /**
76
+ * Delete a role. Its assignments and permission links go with it, via
77
+ * the pivots' `cascadeOnDelete` foreign keys.
78
+ */
79
+ deleteRole(role: RoleRef, options?: GuardOption): Promise<void>;
80
+ /** Delete a permission. Its assignments and role links cascade. */
81
+ deletePermission(permission: PermissionRef, options?: GuardOption): Promise<void>;
82
+ /** Grant permissions to a role, ignoring any it already has. */
83
+ givePermissionToRole(role: RoleRef, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
84
+ /** Revoke permissions from a role. */
85
+ revokePermissionFromRole(role: RoleRef, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
86
+ /**
87
+ * Make a role's permissions exactly this list.
88
+ *
89
+ * An empty list revokes everything, which is the whole point of a sync
90
+ * and the one behaviour worth being explicit about: the framework's own
91
+ * `detach([])` is a deliberate no-op, so a caller passing
92
+ * `request.input("permissions")` through that API would silently keep
93
+ * the old set. This writes pivots directly and means what it says.
94
+ */
95
+ syncRolePermissions(role: RoleRef, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
96
+ /** Assign roles to a subject. Already-held roles are skipped, so this is idempotent. */
97
+ assignRole(assignee: Assignee, roles: RoleRef | RoleRef[], options?: GuardOption): Promise<void>;
98
+ /** Remove roles from a subject. */
99
+ removeRole(assignee: Assignee, roles: RoleRef | RoleRef[], options?: GuardOption): Promise<void>;
100
+ /** Make a subject's roles exactly this list. An empty list removes all of them. */
101
+ syncRoles(assignee: Assignee, roles: RoleRef | RoleRef[], options?: GuardOption): Promise<void>;
102
+ /** Grant permissions to a subject directly, alongside whatever its roles grant. */
103
+ givePermissionTo(assignee: Assignee, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
104
+ /**
105
+ * Revoke a directly-granted permission.
106
+ *
107
+ * Only touches `model_has_permissions`. A permission the subject also
108
+ * holds through a role survives this call, and `hasPermissionTo()` will
109
+ * still answer true — which is correct, and the reason
110
+ * `hasDirectPermission()` exists to tell the two apart.
111
+ */
112
+ revokePermissionTo(assignee: Assignee, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
113
+ /** Make a subject's direct permissions exactly this list. An empty list removes all of them. */
114
+ syncPermissions(assignee: Assignee, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
115
+ /** Does this subject hold this role? */
116
+ hasRole(assignee: Assignee, role: string | bigint, options?: GuardOption): Promise<boolean>;
117
+ /** Does this subject hold at least one of these roles? */
118
+ hasAnyRole(assignee: Assignee, roles: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
119
+ /** Does this subject hold every one of these roles? */
120
+ hasAllRoles(assignee: Assignee, roles: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
121
+ /**
122
+ * Does this subject hold this permission, by any route?
123
+ *
124
+ * An unknown permission name returns false rather than throwing. This
125
+ * one is a check, not a write: the gate hook calls it with every
126
+ * ability string in the app, almost none of which are permissions, so
127
+ * throwing would turn `can("view-dashboard")` into a 500. The write
128
+ * methods still throw on an unknown name, which is where a typo
129
+ * actually matters.
130
+ */
131
+ hasPermissionTo(assignee: Assignee, permission: string | bigint, options?: GuardOption): Promise<boolean>;
132
+ /** Does this subject hold at least one of these permissions, by any route? */
133
+ hasAnyPermission(assignee: Assignee, permissions: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
134
+ /** Does this subject hold every one of these permissions, by any route? */
135
+ hasAllPermissions(assignee: Assignee, permissions: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
136
+ /** Does this subject hold this permission DIRECTLY, ignoring its roles? */
137
+ hasDirectPermission(assignee: Assignee, permission: string | bigint, options?: GuardOption): Promise<boolean>;
138
+ /** This subject's role names, for the resolved guard. */
139
+ getRoleNames(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
140
+ /** Permission names this subject holds via its roles only. */
141
+ getPermissionsViaRoles(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
142
+ /** Permission names granted to this subject directly, ignoring its roles. */
143
+ getDirectPermissions(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
144
+ /** Every permission name this subject holds, by either route. */
145
+ getAllPermissions(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
146
+ /**
147
+ * One subject's raw assignment ids, memoised per request.
148
+ *
149
+ * Two queries, in parallel, and at most once per subject per request.
150
+ * The memo is what makes the gate hook affordable: it fires on every
151
+ * authorization check, so a controller with five `can()` calls would
152
+ * otherwise be ten queries against rows that cannot have changed.
153
+ */
154
+ private assignmentsFor;
155
+ /** The role's name, whether it was named by name or by id. Unknown ids yield a non-matching sentinel. */
156
+ private roleNameOf;
157
+ private permissionNameOf;
158
+ /** Resolve a role reference to its id, throwing on an unknown name. */
159
+ private roleId;
160
+ private permissionId;
161
+ private roleIds;
162
+ private permissionIds;
163
+ /**
164
+ * Insert pivot rows, in chunks, as one multi-row statement each.
165
+ *
166
+ * Goes to raw Kysely rather than `QueryBuilder`, whose `insert()` takes
167
+ * a single row: a per-row loop would turn a 500-permission sync into
168
+ * 500 round trips. `Role.resolveConnection()` is the handle rather
169
+ * than a `DatabaseManager` lookup because it swaps in the active
170
+ * transaction when there is one, which is what makes the `sync*`
171
+ * methods' delete-then-insert atomic.
172
+ *
173
+ * Chunked because pivot writes in this framework do not bound their
174
+ * parameter count, and a multi-row insert binds one parameter per
175
+ * column per row — three columns here, so a sync of 12,000 would
176
+ * exceed SQLite's 32,766-binding ceiling (Postgres' is 65,535) and
177
+ * fail at the driver rather than anywhere meaningful. 1,000 rows is
178
+ * 3,000 bindings, well inside every engine. Sequential, since the
179
+ * chunks all target one table inside one transaction.
180
+ */
181
+ private insertChunked;
182
+ }
183
+ //# sourceMappingURL=permission-registrar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-registrar.d.ts","sourceRoot":"","sources":["../src/permission-registrar.ts"],"names":[],"mappings":"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"}