@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.
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/assignee.d.ts +57 -0
- package/dist/assignee.d.ts.map +1 -0
- package/dist/assignee.js +46 -0
- package/dist/assignee.js.map +1 -0
- package/dist/commands/permissions-cache-reset.d.ts +20 -0
- package/dist/commands/permissions-cache-reset.d.ts.map +1 -0
- package/dist/commands/permissions-cache-reset.js +24 -0
- package/dist/commands/permissions-cache-reset.js.map +1 -0
- package/dist/commands/permissions-check.d.ts +37 -0
- package/dist/commands/permissions-check.d.ts.map +1 -0
- package/dist/commands/permissions-check.js +92 -0
- package/dist/commands/permissions-check.js.map +1 -0
- package/dist/commands/permissions-show.d.ts +20 -0
- package/dist/commands/permissions-show.d.ts.map +1 -0
- package/dist/commands/permissions-show.js +49 -0
- package/dist/commands/permissions-show.js.map +1 -0
- package/dist/errors.d.ts +77 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +106 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/listeners/invalidate-permission-cache.listener.d.ts +39 -0
- package/dist/listeners/invalidate-permission-cache.listener.d.ts.map +1 -0
- package/dist/listeners/invalidate-permission-cache.listener.js +50 -0
- package/dist/listeners/invalidate-permission-cache.listener.js.map +1 -0
- package/dist/middleware/current-subject.d.ts +23 -0
- package/dist/middleware/current-subject.d.ts.map +1 -0
- package/dist/middleware/current-subject.js +31 -0
- package/dist/middleware/current-subject.js.map +1 -0
- package/dist/middleware/permission.d.ts +19 -0
- package/dist/middleware/permission.d.ts.map +1 -0
- package/dist/middleware/permission.js +34 -0
- package/dist/middleware/permission.js.map +1 -0
- package/dist/middleware/role-or-permission.d.ts +29 -0
- package/dist/middleware/role-or-permission.d.ts.map +1 -0
- package/dist/middleware/role-or-permission.js +45 -0
- package/dist/middleware/role-or-permission.js.map +1 -0
- package/dist/middleware/role.d.ts +21 -0
- package/dist/middleware/role.d.ts.map +1 -0
- package/dist/middleware/role.js +36 -0
- package/dist/middleware/role.js.map +1 -0
- package/dist/migrations/0001_create_permission_tables.d.ts +63 -0
- package/dist/migrations/0001_create_permission_tables.d.ts.map +1 -0
- package/dist/migrations/0001_create_permission_tables.js +113 -0
- package/dist/migrations/0001_create_permission_tables.js.map +1 -0
- package/dist/models/permission.model.d.ts +58 -0
- package/dist/models/permission.model.d.ts.map +1 -0
- package/dist/models/permission.model.js +18 -0
- package/dist/models/permission.model.js.map +1 -0
- package/dist/models/role.model.d.ts +60 -0
- package/dist/models/role.model.d.ts.map +1 -0
- package/dist/models/role.model.js +18 -0
- package/dist/models/role.model.js.map +1 -0
- package/dist/permission-map.d.ts +97 -0
- package/dist/permission-map.d.ts.map +1 -0
- package/dist/permission-map.js +93 -0
- package/dist/permission-map.js.map +1 -0
- package/dist/permission-registrar.d.ts +183 -0
- package/dist/permission-registrar.d.ts.map +1 -0
- package/dist/permission-registrar.js +573 -0
- package/dist/permission-registrar.js.map +1 -0
- package/dist/permissions-config.d.ts +80 -0
- package/dist/permissions-config.d.ts.map +1 -0
- package/dist/permissions-config.js +22 -0
- package/dist/permissions-config.js.map +1 -0
- package/dist/permissions-facade.d.ts +74 -0
- package/dist/permissions-facade.d.ts.map +1 -0
- package/dist/permissions-facade.js +131 -0
- package/dist/permissions-facade.js.map +1 -0
- package/dist/permissions-service-provider.d.ts +91 -0
- package/dist/permissions-service-provider.d.ts.map +1 -0
- package/dist/permissions-service-provider.js +137 -0
- package/dist/permissions-service-provider.js.map +1 -0
- package/dist/relations.d.ts +63 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +62 -0
- package/dist/relations.js.map +1 -0
- package/dist/request-cache.d.ts +50 -0
- package/dist/request-cache.d.ts.map +1 -0
- package/dist/request-cache.js +40 -0
- package/dist/request-cache.js.map +1 -0
- package/dist/tokens.d.ts +15 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +15 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The optional `"permissions"` config namespace.
|
|
3
|
+
*
|
|
4
|
+
* Every field has a default, so an app that never writes
|
|
5
|
+
* `config/permissions.ts` gets a working package: roles scoped to the
|
|
6
|
+
* app's default auth guard, a day-long cache in the default store, and
|
|
7
|
+
* the gate hook registered.
|
|
8
|
+
*
|
|
9
|
+
* NOTHING HERE IMPORTS A MODEL. A `config/*.ts` is loaded before
|
|
10
|
+
* `app.bootstrap()`, so importing a model would pull the ORM into
|
|
11
|
+
* config-load time.
|
|
12
|
+
*/
|
|
13
|
+
export interface PermissionsConfig {
|
|
14
|
+
/**
|
|
15
|
+
* The guard name stamped on new roles and permissions, and used by any
|
|
16
|
+
* check that doesn't name one.
|
|
17
|
+
*
|
|
18
|
+
* Defaults to `auth.default`, which is what a single-guard app wants
|
|
19
|
+
* and never has to think about. A multi-guard app that wants `api`
|
|
20
|
+
* roles passes `{ guard: "api" }` per call; this is only the fallback.
|
|
21
|
+
*
|
|
22
|
+
* There is no wildcard guard. See the migration for why the column
|
|
23
|
+
* cannot be nullable.
|
|
24
|
+
*/
|
|
25
|
+
guard?: string;
|
|
26
|
+
cache?: PermissionsCacheConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Register the `Gate.before()` hook that makes `can("posts.edit")`
|
|
29
|
+
* consult permissions.
|
|
30
|
+
*
|
|
31
|
+
* On by default: it is the headline integration, and an app that
|
|
32
|
+
* installs this package and then finds `can()` ignores it has been
|
|
33
|
+
* surprised in the expensive direction. Set `false` to check only
|
|
34
|
+
* through this package's own API and middleware.
|
|
35
|
+
*/
|
|
36
|
+
gate?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface PermissionsCacheConfig {
|
|
39
|
+
/**
|
|
40
|
+
* The single key holding the whole role/permission map.
|
|
41
|
+
*
|
|
42
|
+
* One key, not one per entity, because `@mahiframework/cache` has no
|
|
43
|
+
* tags: there is no way to flush by pattern, so every key this package
|
|
44
|
+
* writes is a key it must be able to name later. One is nameable.
|
|
45
|
+
*/
|
|
46
|
+
key?: string;
|
|
47
|
+
/** A named cache store, else the default one. */
|
|
48
|
+
store?: string;
|
|
49
|
+
/**
|
|
50
|
+
* How long the map survives, in seconds. Defaults to 24 hours.
|
|
51
|
+
*
|
|
52
|
+
* A TTL rather than no-expiry specifically BECAUSE there are no cache
|
|
53
|
+
* tags. Invalidation here is explicit `forget()` on write plus model-
|
|
54
|
+
* event listeners, and if some path ever escapes both, a `null` TTL
|
|
55
|
+
* would make the stale map permanent. A day is short enough that a
|
|
56
|
+
* missed invalidation is an incident with an end, and long enough that
|
|
57
|
+
* the map is effectively always warm.
|
|
58
|
+
*/
|
|
59
|
+
ttlSeconds?: number;
|
|
60
|
+
}
|
|
61
|
+
/** The config with every default applied, built once at provider boot. */
|
|
62
|
+
export interface ResolvedPermissionsConfig {
|
|
63
|
+
/** Null when neither config nor `auth.default` named one; resolved lazily so boot doesn't fail. */
|
|
64
|
+
guard: string | null;
|
|
65
|
+
cacheKey: string;
|
|
66
|
+
cacheStore: string | undefined;
|
|
67
|
+
cacheTtlSeconds: number;
|
|
68
|
+
gate: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Normalise a config block once, at provider boot.
|
|
72
|
+
*
|
|
73
|
+
* Every default is applied here with `??`, which is both the single place
|
|
74
|
+
* to read them and immune to merge-order surprises: contributing them via
|
|
75
|
+
* `ConfigRepository.merge()` would deep-merge the INCOMING values last
|
|
76
|
+
* and silently overwrite the app's own config rather than layering under
|
|
77
|
+
* it.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveConfig(config?: PermissionsConfig): ResolvedPermissionsConfig;
|
|
80
|
+
//# sourceMappingURL=permissions-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-config.d.ts","sourceRoot":"","sources":["../src/permissions-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAE/B;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,0EAA0E;AAC1E,MAAM,WAAW,yBAAyB;IACxC,mGAAmG;IACnG,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;CACf;AAKD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,GAAE,iBAAsB,GAAG,yBAAyB,CAUvF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const DEFAULT_CACHE_KEY = "mahi.permissions";
|
|
2
|
+
const DEFAULT_TTL_SECONDS = 86_400;
|
|
3
|
+
/**
|
|
4
|
+
* Normalise a config block once, at provider boot.
|
|
5
|
+
*
|
|
6
|
+
* Every default is applied here with `??`, which is both the single place
|
|
7
|
+
* to read them and immune to merge-order surprises: contributing them via
|
|
8
|
+
* `ConfigRepository.merge()` would deep-merge the INCOMING values last
|
|
9
|
+
* and silently overwrite the app's own config rather than layering under
|
|
10
|
+
* it.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveConfig(config = {}) {
|
|
13
|
+
const cache = config.cache ?? {};
|
|
14
|
+
return {
|
|
15
|
+
guard: config.guard ?? null,
|
|
16
|
+
cacheKey: cache.key ?? DEFAULT_CACHE_KEY,
|
|
17
|
+
cacheStore: cache.store,
|
|
18
|
+
cacheTtlSeconds: cache.ttlSeconds ?? DEFAULT_TTL_SECONDS,
|
|
19
|
+
gate: config.gate ?? true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=permissions-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-config.js","sourceRoot":"","sources":["../src/permissions-config.ts"],"names":[],"mappings":"AA4EA,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAC7C,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,SAA4B,EAAE;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IAEjC,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;QAC3B,QAAQ,EAAE,KAAK,CAAC,GAAG,IAAI,iBAAiB;QACxC,UAAU,EAAE,KAAK,CAAC,KAAK;QACvB,eAAe,EAAE,KAAK,CAAC,UAAU,IAAI,mBAAmB;QACxD,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI;KAC1B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Assignee } from "./assignee.js";
|
|
2
|
+
import type { Permission } from "./models/permission.model.js";
|
|
3
|
+
import type { Role } from "./models/role.model.js";
|
|
4
|
+
import type { GuardOption, PermissionRef, PermissionRegistrar, RoleRef } from "./permission-registrar.js";
|
|
5
|
+
declare const Permissions_base: {
|
|
6
|
+
new (): {};
|
|
7
|
+
_swapped?: PermissionRegistrar | undefined;
|
|
8
|
+
instance(): PermissionRegistrar;
|
|
9
|
+
swap<S extends Partial<PermissionRegistrar>>(fake: S): S;
|
|
10
|
+
restore(): void;
|
|
11
|
+
isSwapped(): boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Thin facade over the `PermissionRegistrar` singleton bound at
|
|
15
|
+
* `PERMISSIONS_TOKEN`.
|
|
16
|
+
*
|
|
17
|
+
* await Permissions.assignRole(user, "admin");
|
|
18
|
+
* if (await Permissions.hasPermissionTo(user, "posts.edit")) { ... }
|
|
19
|
+
*
|
|
20
|
+
* Every method takes the subject explicitly. Laravel's spatie package
|
|
21
|
+
* reads `$user->assignRole(...)` off a trait, and this framework has no
|
|
22
|
+
* traits and no package that touches the app's `User` model — behaviour
|
|
23
|
+
* is attached through a container singleton, never by extending a model
|
|
24
|
+
* the app owns. The explicit subject also works where a method could
|
|
25
|
+
* not: on any polymorphic entity uniformly, and in a queue job holding
|
|
26
|
+
* only a `{ type, id }` descriptor.
|
|
27
|
+
*
|
|
28
|
+
* Every call is `async`. There is no synchronous variant, and there
|
|
29
|
+
* cannot be: the first check of a process reads the database.
|
|
30
|
+
*/
|
|
31
|
+
export declare class Permissions extends Permissions_base {
|
|
32
|
+
static createRole(name: string, options?: GuardOption): Promise<Role>;
|
|
33
|
+
static createPermission(name: string, options?: GuardOption): Promise<Permission>;
|
|
34
|
+
static findOrCreateRole(name: string, options?: GuardOption): Promise<Role>;
|
|
35
|
+
static findOrCreatePermission(name: string, options?: GuardOption): Promise<Permission>;
|
|
36
|
+
static findRole(name: string, options?: GuardOption): Promise<Role>;
|
|
37
|
+
static findPermission(name: string, options?: GuardOption): Promise<Permission>;
|
|
38
|
+
static deleteRole(role: RoleRef, options?: GuardOption): Promise<void>;
|
|
39
|
+
static deletePermission(permission: PermissionRef, options?: GuardOption): Promise<void>;
|
|
40
|
+
static givePermissionToRole(role: RoleRef, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
|
|
41
|
+
static revokePermissionFromRole(role: RoleRef, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
|
|
42
|
+
static syncRolePermissions(role: RoleRef, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
|
|
43
|
+
static assignRole(assignee: Assignee, roles: RoleRef | RoleRef[], options?: GuardOption): Promise<void>;
|
|
44
|
+
static removeRole(assignee: Assignee, roles: RoleRef | RoleRef[], options?: GuardOption): Promise<void>;
|
|
45
|
+
/** Makes the subject's roles exactly `roles`. An empty array removes all of them. */
|
|
46
|
+
static syncRoles(assignee: Assignee, roles: RoleRef | RoleRef[], options?: GuardOption): Promise<void>;
|
|
47
|
+
static givePermissionTo(assignee: Assignee, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
|
|
48
|
+
static revokePermissionTo(assignee: Assignee, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
|
|
49
|
+
/** Makes the subject's DIRECT permissions exactly `permissions`. Roles are untouched. */
|
|
50
|
+
static syncPermissions(assignee: Assignee, permissions: PermissionRef | PermissionRef[], options?: GuardOption): Promise<void>;
|
|
51
|
+
static hasRole(assignee: Assignee, role: string | bigint, options?: GuardOption): Promise<boolean>;
|
|
52
|
+
static hasAnyRole(assignee: Assignee, roles: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
|
|
53
|
+
static hasAllRoles(assignee: Assignee, roles: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
|
|
54
|
+
/** True if the subject holds this permission by ANY route: a role, or directly. */
|
|
55
|
+
static hasPermissionTo(assignee: Assignee, permission: string | bigint, options?: GuardOption): Promise<boolean>;
|
|
56
|
+
static hasAnyPermission(assignee: Assignee, permissions: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
|
|
57
|
+
static hasAllPermissions(assignee: Assignee, permissions: Array<string | bigint>, options?: GuardOption): Promise<boolean>;
|
|
58
|
+
/** True only if granted directly. A permission held via a role answers false here. */
|
|
59
|
+
static hasDirectPermission(assignee: Assignee, permission: string | bigint, options?: GuardOption): Promise<boolean>;
|
|
60
|
+
static getRoleNames(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
|
|
61
|
+
static getAllPermissions(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
|
|
62
|
+
static getDirectPermissions(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
|
|
63
|
+
static getPermissionsViaRoles(assignee: Assignee, options?: GuardOption): Promise<Set<string>>;
|
|
64
|
+
/** Drop the cached role/permission map and every memoised assignment. */
|
|
65
|
+
static forgetCache(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Run `callback` with a per-subject assignment memo, for a job or
|
|
68
|
+
* command that makes several checks. HTTP requests already have one,
|
|
69
|
+
* opened by the provider's middleware.
|
|
70
|
+
*/
|
|
71
|
+
static withCache<T>(callback: () => T | Promise<T>): Promise<T>;
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=permissions-facade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-facade.d.ts","sourceRoot":"","sources":["../src/permissions-facade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Facade } from "@mahiframework/facades";
|
|
2
|
+
import { withPermissionCache } from "./request-cache.js";
|
|
3
|
+
import { PERMISSIONS_TOKEN } from "./tokens.js";
|
|
4
|
+
/**
|
|
5
|
+
* Thin facade over the `PermissionRegistrar` singleton bound at
|
|
6
|
+
* `PERMISSIONS_TOKEN`.
|
|
7
|
+
*
|
|
8
|
+
* await Permissions.assignRole(user, "admin");
|
|
9
|
+
* if (await Permissions.hasPermissionTo(user, "posts.edit")) { ... }
|
|
10
|
+
*
|
|
11
|
+
* Every method takes the subject explicitly. Laravel's spatie package
|
|
12
|
+
* reads `$user->assignRole(...)` off a trait, and this framework has no
|
|
13
|
+
* traits and no package that touches the app's `User` model — behaviour
|
|
14
|
+
* is attached through a container singleton, never by extending a model
|
|
15
|
+
* the app owns. The explicit subject also works where a method could
|
|
16
|
+
* not: on any polymorphic entity uniformly, and in a queue job holding
|
|
17
|
+
* only a `{ type, id }` descriptor.
|
|
18
|
+
*
|
|
19
|
+
* Every call is `async`. There is no synchronous variant, and there
|
|
20
|
+
* cannot be: the first check of a process reads the database.
|
|
21
|
+
*/
|
|
22
|
+
export class Permissions extends Facade(() => PERMISSIONS_TOKEN) {
|
|
23
|
+
// --------------------------------------------------------- roles/permissions
|
|
24
|
+
static createRole(name, options) {
|
|
25
|
+
return this.instance().createRole(name, options);
|
|
26
|
+
}
|
|
27
|
+
static createPermission(name, options) {
|
|
28
|
+
return this.instance().createPermission(name, options);
|
|
29
|
+
}
|
|
30
|
+
static findOrCreateRole(name, options) {
|
|
31
|
+
return this.instance().findOrCreateRole(name, options);
|
|
32
|
+
}
|
|
33
|
+
static findOrCreatePermission(name, options) {
|
|
34
|
+
return this.instance().findOrCreatePermission(name, options);
|
|
35
|
+
}
|
|
36
|
+
static findRole(name, options) {
|
|
37
|
+
return this.instance().findRole(name, options);
|
|
38
|
+
}
|
|
39
|
+
static findPermission(name, options) {
|
|
40
|
+
return this.instance().findPermission(name, options);
|
|
41
|
+
}
|
|
42
|
+
static deleteRole(role, options) {
|
|
43
|
+
return this.instance().deleteRole(role, options);
|
|
44
|
+
}
|
|
45
|
+
static deletePermission(permission, options) {
|
|
46
|
+
return this.instance().deletePermission(permission, options);
|
|
47
|
+
}
|
|
48
|
+
// ------------------------------------------------------ role -> permissions
|
|
49
|
+
static givePermissionToRole(role, permissions, options) {
|
|
50
|
+
return this.instance().givePermissionToRole(role, permissions, options);
|
|
51
|
+
}
|
|
52
|
+
static revokePermissionFromRole(role, permissions, options) {
|
|
53
|
+
return this.instance().revokePermissionFromRole(role, permissions, options);
|
|
54
|
+
}
|
|
55
|
+
static syncRolePermissions(role, permissions, options) {
|
|
56
|
+
return this.instance().syncRolePermissions(role, permissions, options);
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------- subject -> roles
|
|
59
|
+
static assignRole(assignee, roles, options) {
|
|
60
|
+
return this.instance().assignRole(assignee, roles, options);
|
|
61
|
+
}
|
|
62
|
+
static removeRole(assignee, roles, options) {
|
|
63
|
+
return this.instance().removeRole(assignee, roles, options);
|
|
64
|
+
}
|
|
65
|
+
/** Makes the subject's roles exactly `roles`. An empty array removes all of them. */
|
|
66
|
+
static syncRoles(assignee, roles, options) {
|
|
67
|
+
return this.instance().syncRoles(assignee, roles, options);
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------- subject -> permissions
|
|
70
|
+
static givePermissionTo(assignee, permissions, options) {
|
|
71
|
+
return this.instance().givePermissionTo(assignee, permissions, options);
|
|
72
|
+
}
|
|
73
|
+
static revokePermissionTo(assignee, permissions, options) {
|
|
74
|
+
return this.instance().revokePermissionTo(assignee, permissions, options);
|
|
75
|
+
}
|
|
76
|
+
/** Makes the subject's DIRECT permissions exactly `permissions`. Roles are untouched. */
|
|
77
|
+
static syncPermissions(assignee, permissions, options) {
|
|
78
|
+
return this.instance().syncPermissions(assignee, permissions, options);
|
|
79
|
+
}
|
|
80
|
+
// --------------------------------------------------------------- the checks
|
|
81
|
+
static hasRole(assignee, role, options) {
|
|
82
|
+
return this.instance().hasRole(assignee, role, options);
|
|
83
|
+
}
|
|
84
|
+
static hasAnyRole(assignee, roles, options) {
|
|
85
|
+
return this.instance().hasAnyRole(assignee, roles, options);
|
|
86
|
+
}
|
|
87
|
+
static hasAllRoles(assignee, roles, options) {
|
|
88
|
+
return this.instance().hasAllRoles(assignee, roles, options);
|
|
89
|
+
}
|
|
90
|
+
/** True if the subject holds this permission by ANY route: a role, or directly. */
|
|
91
|
+
static hasPermissionTo(assignee, permission, options) {
|
|
92
|
+
return this.instance().hasPermissionTo(assignee, permission, options);
|
|
93
|
+
}
|
|
94
|
+
static hasAnyPermission(assignee, permissions, options) {
|
|
95
|
+
return this.instance().hasAnyPermission(assignee, permissions, options);
|
|
96
|
+
}
|
|
97
|
+
static hasAllPermissions(assignee, permissions, options) {
|
|
98
|
+
return this.instance().hasAllPermissions(assignee, permissions, options);
|
|
99
|
+
}
|
|
100
|
+
/** True only if granted directly. A permission held via a role answers false here. */
|
|
101
|
+
static hasDirectPermission(assignee, permission, options) {
|
|
102
|
+
return this.instance().hasDirectPermission(assignee, permission, options);
|
|
103
|
+
}
|
|
104
|
+
// ------------------------------------------------------------ introspection
|
|
105
|
+
static getRoleNames(assignee, options) {
|
|
106
|
+
return this.instance().getRoleNames(assignee, options);
|
|
107
|
+
}
|
|
108
|
+
static getAllPermissions(assignee, options) {
|
|
109
|
+
return this.instance().getAllPermissions(assignee, options);
|
|
110
|
+
}
|
|
111
|
+
static getDirectPermissions(assignee, options) {
|
|
112
|
+
return this.instance().getDirectPermissions(assignee, options);
|
|
113
|
+
}
|
|
114
|
+
static getPermissionsViaRoles(assignee, options) {
|
|
115
|
+
return this.instance().getPermissionsViaRoles(assignee, options);
|
|
116
|
+
}
|
|
117
|
+
// -------------------------------------------------------------------- cache
|
|
118
|
+
/** Drop the cached role/permission map and every memoised assignment. */
|
|
119
|
+
static forgetCache() {
|
|
120
|
+
return this.instance().forgetCache();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Run `callback` with a per-subject assignment memo, for a job or
|
|
124
|
+
* command that makes several checks. HTTP requests already have one,
|
|
125
|
+
* opened by the provider's middleware.
|
|
126
|
+
*/
|
|
127
|
+
static withCache(callback) {
|
|
128
|
+
return withPermissionCache(callback);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=permissions-facade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-facade.js","sourceRoot":"","sources":["../src/permissions-facade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ServiceProvider } from "@mahiframework/core";
|
|
2
|
+
import type { AnyModelClass, RegisteredMigration } from "@mahiframework/database";
|
|
3
|
+
import type { GateRegistry } from "@mahiframework/authorization";
|
|
4
|
+
import type { ListenerRegistration } from "@mahiframework/events";
|
|
5
|
+
import type { HttpPipe } from "@mahiframework/http";
|
|
6
|
+
import { PermissionsShowCommand } from "./commands/permissions-show.js";
|
|
7
|
+
import { PERMISSIONS_TOKEN } from "./tokens.js";
|
|
8
|
+
export { PERMISSIONS_TOKEN };
|
|
9
|
+
/**
|
|
10
|
+
* Registers the `PermissionRegistrar` singleton, the gate hook that makes
|
|
11
|
+
* `can()` consult permissions, the per-request assignment memo, and the
|
|
12
|
+
* listeners that keep the cache honest.
|
|
13
|
+
*
|
|
14
|
+
* ORDERING: list this provider AFTER `DatabaseServiceProvider` (it owns
|
|
15
|
+
* five tables and two models), AFTER `CacheServiceProvider` (the map
|
|
16
|
+
* lives in a cache store), AFTER `AuthServiceProvider` (so its memo pipe
|
|
17
|
+
* runs inside the ambient auth scope and the subject is resolvable), and
|
|
18
|
+
* BEFORE `HttpServiceProvider` (so its pipe is collected before routes
|
|
19
|
+
* are). `AuthorizationServiceProvider` may come on either side:
|
|
20
|
+
* its own `boot()` walks every registered provider, so `gates()` is
|
|
21
|
+
* collected whichever order they appear in. You cannot enforce any of
|
|
22
|
+
* that; the app's `config/app.ts` decides, and this docstring is the
|
|
23
|
+
* whole mechanism.
|
|
24
|
+
*/
|
|
25
|
+
export declare class PermissionsServiceProvider extends ServiceProvider {
|
|
26
|
+
register(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Teach the gate that a bare ability may be a permission name.
|
|
29
|
+
*
|
|
30
|
+
* This is what makes `can("posts.edit")`, `Gate.authorize("posts.edit")`
|
|
31
|
+
* and the `can()` route middleware consult permissions without any of
|
|
32
|
+
* them knowing this package exists. spatie registers the same hook.
|
|
33
|
+
*
|
|
34
|
+
* THREE THINGS HERE ARE LOAD-BEARING:
|
|
35
|
+
*
|
|
36
|
+
* 1. It returns `true` or `null`, NEVER `false`. A `false` from a
|
|
37
|
+
* `before()` hook hard-denies and skips policy resolution entirely,
|
|
38
|
+
* so denying on a permission miss would make every policy in the app
|
|
39
|
+
* unreachable — the single worst bug this package could ship, and
|
|
40
|
+
* the reason there is a test asserting a policy still grants after a
|
|
41
|
+
* miss.
|
|
42
|
+
*
|
|
43
|
+
* 2. It abstains the moment the check carries an argument
|
|
44
|
+
* (`args.length > 0`), which is a DELIBERATE DIVERGENCE FROM SPATIE.
|
|
45
|
+
* There, `Gate::allows("update", $post)` also routes through the
|
|
46
|
+
* permission check, so an app with both a permission named `update`
|
|
47
|
+
* and a `PostPolicy.update` grants update on EVERY post to anyone
|
|
48
|
+
* holding that permission. Abstaining splits the two cleanly: bare
|
|
49
|
+
* abilities are permission names, model-scoped abilities are the
|
|
50
|
+
* policy's business, and a policy that wants a permission check calls
|
|
51
|
+
* `Permissions.hasPermissionTo()` explicitly — which is where that
|
|
52
|
+
* decision belongs anyway, next to the row it concerns.
|
|
53
|
+
*
|
|
54
|
+
* 3. A non-model user abstains too. The pivots need a morph alias and a
|
|
55
|
+
* `bigint` key; a token-guard adapter or a plain object has neither,
|
|
56
|
+
* and `resolveAssignee()` would throw. A hook that throws turns every
|
|
57
|
+
* authorization check in the app into a 500.
|
|
58
|
+
*
|
|
59
|
+
* `gates()` is synchronous, so nothing is loaded here. The closure is
|
|
60
|
+
* async and the first check of the process populates the cache.
|
|
61
|
+
*
|
|
62
|
+
* Hooks run in registration order and the first non-null wins, so a
|
|
63
|
+
* permission grant beats a later hook that would have denied. That
|
|
64
|
+
* ordering is the app's `config/app.ts` choice.
|
|
65
|
+
*/
|
|
66
|
+
gates(gate: GateRegistry): void;
|
|
67
|
+
/**
|
|
68
|
+
* Open a per-request assignment memo.
|
|
69
|
+
*
|
|
70
|
+
* Not an optimisation so much as a correction: the gate hook above runs
|
|
71
|
+
* on EVERY authorization check, so a controller making five `can()`
|
|
72
|
+
* calls would be ten queries without this. See `request-cache.ts`.
|
|
73
|
+
*/
|
|
74
|
+
middleware(): HttpPipe[];
|
|
75
|
+
/**
|
|
76
|
+
* Forget the cached map when a `Role` or `Permission` is written
|
|
77
|
+
* outside the registrar — a seeder, a migration, an admin screen.
|
|
78
|
+
* See the listener for why these three events and not a pattern.
|
|
79
|
+
*/
|
|
80
|
+
listeners(): ReadonlyArray<ListenerRegistration>;
|
|
81
|
+
/**
|
|
82
|
+
* Static rather than a `migrations()` directory path, so it resolves
|
|
83
|
+
* inside a bundled binary. One migration for all five tables; see the
|
|
84
|
+
* file for why they are not five.
|
|
85
|
+
*/
|
|
86
|
+
migrationSources(): RegisteredMigration[];
|
|
87
|
+
/** Registered so a queued job can carry a `Role` or a `Permission`. */
|
|
88
|
+
models(): AnyModelClass[];
|
|
89
|
+
commands(): (typeof PermissionsShowCommand)[];
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=permissions-service-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-service-provider.d.ts","sourceRoot":"","sources":["../src/permissions-service-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAElF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAQpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,0BAA2B,SAAQ,eAAe;IAC7D,QAAQ,IAAI,IAAI;IAiBhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCG;IACH,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAkB/B;;;;;;OAMG;IACH,UAAU,IAAI,QAAQ,EAAE;IAIxB;;;;OAIG;IACH,SAAS,IAAI,aAAa,CAAC,oBAAoB,CAAC;IAQhD;;;;OAIG;IACH,gBAAgB,IAAI,mBAAmB,EAAE;IAIzC,uEAAuE;IACvE,MAAM,IAAI,aAAa,EAAE;IAIzB,QAAQ;CAGT"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { ServiceProvider } from "@mahiframework/core";
|
|
2
|
+
import { BaseModel, ModelCreated, ModelDeleted, ModelUpdated } from "@mahiframework/database";
|
|
3
|
+
import { PermissionRegistrar } from "./permission-registrar.js";
|
|
4
|
+
import { resolveConfig } from "./permissions-config.js";
|
|
5
|
+
import { Permission } from "./models/permission.model.js";
|
|
6
|
+
import { Role } from "./models/role.model.js";
|
|
7
|
+
import { InvalidatePermissionCacheListener } from "./listeners/invalidate-permission-cache.listener.js";
|
|
8
|
+
import { PermissionsCacheResetCommand } from "./commands/permissions-cache-reset.js";
|
|
9
|
+
import { PermissionsCheckCommand } from "./commands/permissions-check.js";
|
|
10
|
+
import { PermissionsShowCommand } from "./commands/permissions-show.js";
|
|
11
|
+
import createPermissionTables from "./migrations/0001_create_permission_tables.js";
|
|
12
|
+
import { runWithPermissionCache } from "./request-cache.js";
|
|
13
|
+
import { PERMISSIONS_TOKEN } from "./tokens.js";
|
|
14
|
+
export { PERMISSIONS_TOKEN };
|
|
15
|
+
/**
|
|
16
|
+
* Registers the `PermissionRegistrar` singleton, the gate hook that makes
|
|
17
|
+
* `can()` consult permissions, the per-request assignment memo, and the
|
|
18
|
+
* listeners that keep the cache honest.
|
|
19
|
+
*
|
|
20
|
+
* ORDERING: list this provider AFTER `DatabaseServiceProvider` (it owns
|
|
21
|
+
* five tables and two models), AFTER `CacheServiceProvider` (the map
|
|
22
|
+
* lives in a cache store), AFTER `AuthServiceProvider` (so its memo pipe
|
|
23
|
+
* runs inside the ambient auth scope and the subject is resolvable), and
|
|
24
|
+
* BEFORE `HttpServiceProvider` (so its pipe is collected before routes
|
|
25
|
+
* are). `AuthorizationServiceProvider` may come on either side:
|
|
26
|
+
* its own `boot()` walks every registered provider, so `gates()` is
|
|
27
|
+
* collected whichever order they appear in. You cannot enforce any of
|
|
28
|
+
* that; the app's `config/app.ts` decides, and this docstring is the
|
|
29
|
+
* whole mechanism.
|
|
30
|
+
*/
|
|
31
|
+
export class PermissionsServiceProvider extends ServiceProvider {
|
|
32
|
+
register() {
|
|
33
|
+
// No `config.merge()` of defaults. Every default is applied in
|
|
34
|
+
// `resolveConfig()` with `??`, which is both the single place to read
|
|
35
|
+
// them and immune to merge-order surprises: `ConfigRepository.merge()`
|
|
36
|
+
// deep-merges the INCOMING values last, so contributing defaults that
|
|
37
|
+
// way would silently overwrite the app's own config rather than
|
|
38
|
+
// layering under it.
|
|
39
|
+
this.app.singleton(PERMISSIONS_TOKEN, (app) => {
|
|
40
|
+
// `get`, not `require`: an app that installs the package and
|
|
41
|
+
// configures nothing gets working defaults (the auth default
|
|
42
|
+
// guard, a day-long cache) rather than a boot failure.
|
|
43
|
+
const config = app.config.get("permissions") ?? {};
|
|
44
|
+
return new PermissionRegistrar(app, resolveConfig(config));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Teach the gate that a bare ability may be a permission name.
|
|
49
|
+
*
|
|
50
|
+
* This is what makes `can("posts.edit")`, `Gate.authorize("posts.edit")`
|
|
51
|
+
* and the `can()` route middleware consult permissions without any of
|
|
52
|
+
* them knowing this package exists. spatie registers the same hook.
|
|
53
|
+
*
|
|
54
|
+
* THREE THINGS HERE ARE LOAD-BEARING:
|
|
55
|
+
*
|
|
56
|
+
* 1. It returns `true` or `null`, NEVER `false`. A `false` from a
|
|
57
|
+
* `before()` hook hard-denies and skips policy resolution entirely,
|
|
58
|
+
* so denying on a permission miss would make every policy in the app
|
|
59
|
+
* unreachable — the single worst bug this package could ship, and
|
|
60
|
+
* the reason there is a test asserting a policy still grants after a
|
|
61
|
+
* miss.
|
|
62
|
+
*
|
|
63
|
+
* 2. It abstains the moment the check carries an argument
|
|
64
|
+
* (`args.length > 0`), which is a DELIBERATE DIVERGENCE FROM SPATIE.
|
|
65
|
+
* There, `Gate::allows("update", $post)` also routes through the
|
|
66
|
+
* permission check, so an app with both a permission named `update`
|
|
67
|
+
* and a `PostPolicy.update` grants update on EVERY post to anyone
|
|
68
|
+
* holding that permission. Abstaining splits the two cleanly: bare
|
|
69
|
+
* abilities are permission names, model-scoped abilities are the
|
|
70
|
+
* policy's business, and a policy that wants a permission check calls
|
|
71
|
+
* `Permissions.hasPermissionTo()` explicitly — which is where that
|
|
72
|
+
* decision belongs anyway, next to the row it concerns.
|
|
73
|
+
*
|
|
74
|
+
* 3. A non-model user abstains too. The pivots need a morph alias and a
|
|
75
|
+
* `bigint` key; a token-guard adapter or a plain object has neither,
|
|
76
|
+
* and `resolveAssignee()` would throw. A hook that throws turns every
|
|
77
|
+
* authorization check in the app into a 500.
|
|
78
|
+
*
|
|
79
|
+
* `gates()` is synchronous, so nothing is loaded here. The closure is
|
|
80
|
+
* async and the first check of the process populates the cache.
|
|
81
|
+
*
|
|
82
|
+
* Hooks run in registration order and the first non-null wins, so a
|
|
83
|
+
* permission grant beats a later hook that would have denied. That
|
|
84
|
+
* ordering is the app's `config/app.ts` choice.
|
|
85
|
+
*/
|
|
86
|
+
gates(gate) {
|
|
87
|
+
const config = resolveConfig(this.app.config.get("permissions") ?? {});
|
|
88
|
+
if (!config.gate) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
gate.before(async (user, ability, ...args) => {
|
|
92
|
+
if (args.length > 0 || !(user instanceof BaseModel)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const registrar = this.app.make(PERMISSIONS_TOKEN);
|
|
96
|
+
return (await registrar.hasPermissionTo(user, ability)) ? true : null;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Open a per-request assignment memo.
|
|
101
|
+
*
|
|
102
|
+
* Not an optimisation so much as a correction: the gate hook above runs
|
|
103
|
+
* on EVERY authorization check, so a controller making five `can()`
|
|
104
|
+
* calls would be ten queries without this. See `request-cache.ts`.
|
|
105
|
+
*/
|
|
106
|
+
middleware() {
|
|
107
|
+
return [(request, next) => runWithPermissionCache(() => next(request))];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Forget the cached map when a `Role` or `Permission` is written
|
|
111
|
+
* outside the registrar — a seeder, a migration, an admin screen.
|
|
112
|
+
* See the listener for why these three events and not a pattern.
|
|
113
|
+
*/
|
|
114
|
+
listeners() {
|
|
115
|
+
return [
|
|
116
|
+
[ModelCreated, InvalidatePermissionCacheListener],
|
|
117
|
+
[ModelUpdated, InvalidatePermissionCacheListener],
|
|
118
|
+
[ModelDeleted, InvalidatePermissionCacheListener],
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Static rather than a `migrations()` directory path, so it resolves
|
|
123
|
+
* inside a bundled binary. One migration for all five tables; see the
|
|
124
|
+
* file for why they are not five.
|
|
125
|
+
*/
|
|
126
|
+
migrationSources() {
|
|
127
|
+
return [{ name: "0001_create_permission_tables", migration: createPermissionTables }];
|
|
128
|
+
}
|
|
129
|
+
/** Registered so a queued job can carry a `Role` or a `Permission`. */
|
|
130
|
+
models() {
|
|
131
|
+
return [Role, Permission];
|
|
132
|
+
}
|
|
133
|
+
commands() {
|
|
134
|
+
return [PermissionsCacheResetCommand, PermissionsCheckCommand, PermissionsShowCommand];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=permissions-service-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-service-provider.js","sourceRoot":"","sources":["../src/permissions-service-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Permission } from "./models/permission.model.js";
|
|
2
|
+
import { Role } from "./models/role.model.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `roles` relation, for an app model that wants to eager-load them.
|
|
5
|
+
*
|
|
6
|
+
* Opt-in per model rather than shipped on anything, because the package
|
|
7
|
+
* owns neither the app's models nor its attribute interfaces:
|
|
8
|
+
*
|
|
9
|
+
* export interface UserAttributes {
|
|
10
|
+
* // ...
|
|
11
|
+
* roles: MorphToMany<Role>;
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* export class User extends Model<UserAttributes>()({ ... }) {
|
|
15
|
+
* static override relationships = {
|
|
16
|
+
* roles: rolesRelation(),
|
|
17
|
+
* };
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* That buys `User.query().with("roles.permissions")` and
|
|
21
|
+
* `whereHas("roles", (q) => q.where("name", "admin"))`.
|
|
22
|
+
*
|
|
23
|
+
* READ-ONLY, in practice. `Permissions.assignRole()` writes the pivot
|
|
24
|
+
* directly (it takes a subject, not a relation handle, so it can also
|
|
25
|
+
* work from a `{ type, id }` descriptor in a job), so a collection
|
|
26
|
+
* loaded by `with("roles")` will not reflect an assignment made later in
|
|
27
|
+
* the same request. Re-`load()` it if that matters.
|
|
28
|
+
*
|
|
29
|
+
* `type` is deliberately omitted so it defaults to the DECLARING model's
|
|
30
|
+
* `morphAlias()` — which is what a `morphToMany` wants, and the opposite
|
|
31
|
+
* of what `morphedByMany` would. Note that `morphAlias()` falls back to
|
|
32
|
+
* the table name, so an app without a `Relation.morphMap()` entry has
|
|
33
|
+
* made its assignment rows depend on its table name.
|
|
34
|
+
*/
|
|
35
|
+
export declare function rolesRelation(): {
|
|
36
|
+
readonly type: "morphToMany";
|
|
37
|
+
readonly related: () => import("@mahiframework/database").ModelLike;
|
|
38
|
+
readonly options: import("@mahiframework/database").MorphToManyOptions<any, any>;
|
|
39
|
+
readonly __brand?: {
|
|
40
|
+
kind: "morphToMany";
|
|
41
|
+
related: Role;
|
|
42
|
+
} | undefined;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The `permissions` relation: permissions granted to this model
|
|
46
|
+
* DIRECTLY, never those inherited from its roles.
|
|
47
|
+
*
|
|
48
|
+
* The inherited ones have no relation to declare — they are two hops
|
|
49
|
+
* through `model_has_roles` and `role_has_permissions`, which is a
|
|
50
|
+
* `hasManyThrough` the ORM cannot express across a polymorphic pivot.
|
|
51
|
+
* `Permissions.getAllPermissions(user)` is the union, and it answers
|
|
52
|
+
* from the cached map rather than a join.
|
|
53
|
+
*/
|
|
54
|
+
export declare function permissionsRelation(): {
|
|
55
|
+
readonly type: "morphToMany";
|
|
56
|
+
readonly related: () => import("@mahiframework/database").ModelLike;
|
|
57
|
+
readonly options: import("@mahiframework/database").MorphToManyOptions<any, any>;
|
|
58
|
+
readonly __brand?: {
|
|
59
|
+
kind: "morphToMany";
|
|
60
|
+
related: Permission;
|
|
61
|
+
} | undefined;
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=relations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,aAAa;;;;;;;;EAO5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB;;;;;;;;EAOlC"}
|