@cedarjs/permissions 0.0.0-stage → 8.0.0-canary.3571
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/README.md +106 -2
- package/dist/api.d.ts +64 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +55 -0
- package/dist/definePermissions.d.ts +67 -0
- package/dist/definePermissions.d.ts.map +1 -0
- package/dist/definePermissions.js +54 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/package.json +56 -4
package/README.md
CHANGED
|
@@ -1,3 +1,107 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @cedarjs/permissions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Permissions for a Cedar app: who may do what, defined in one place. A role is a
|
|
4
|
+
named bundle of permissions.
|
|
5
|
+
|
|
6
|
+
Fields, services and components check a permission, never a role. So a role can
|
|
7
|
+
do exactly what its list says. A field added later is closed to a role until its
|
|
8
|
+
permission is on that role's list.
|
|
9
|
+
|
|
10
|
+
`yarn cedar setup permissions` sets this up. In an app with tenancy
|
|
11
|
+
(`yarn cedar setup tenancy`), `@cedarjs/tenancy` checks the same definitions
|
|
12
|
+
against the user's role in the current organization.
|
|
13
|
+
|
|
14
|
+
## Defining permissions
|
|
15
|
+
|
|
16
|
+
The definitions live in the app's `packages/permissions`, shared by the api and
|
|
17
|
+
web sides:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// packages/permissions/src/index.ts
|
|
21
|
+
import { definePermissions } from '@cedarjs/permissions'
|
|
22
|
+
import type { PermissionName } from '@cedarjs/permissions'
|
|
23
|
+
|
|
24
|
+
const reader = ['posts.read'] as const
|
|
25
|
+
|
|
26
|
+
export const permissions = definePermissions({
|
|
27
|
+
permissions: ['posts.read', 'posts.edit', 'users.manage'],
|
|
28
|
+
roles: {
|
|
29
|
+
admin: [...reader, 'posts.edit', 'users.manage'],
|
|
30
|
+
editor: [...reader, 'posts.edit'],
|
|
31
|
+
reader,
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export type Permission = PermissionName<typeof permissions>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The explicit `permissions` list makes a typo in a role's list a type error (and
|
|
39
|
+
an error at runtime) instead of a new permission. A role the definitions don't
|
|
40
|
+
list has no permissions.
|
|
41
|
+
|
|
42
|
+
`@cedarjs/permissions` is plain data and functions with no api code in it, so
|
|
43
|
+
the web can import it too. It also exports:
|
|
44
|
+
|
|
45
|
+
- `permissionsOf(definitions, roles)`: the permissions of a role, or of a list
|
|
46
|
+
of roles (their union). The one place a role becomes permissions.
|
|
47
|
+
- `coversRole(definitions, held, role)`: whether the permissions in `held`
|
|
48
|
+
include every permission of `role`.
|
|
49
|
+
- `isPermission(definitions, name)` and `assertPermission(definitions, name)`:
|
|
50
|
+
whether `name` is defined, as a type guard or an assertion.
|
|
51
|
+
|
|
52
|
+
## Checking permissions on the api
|
|
53
|
+
|
|
54
|
+
`createPermissions` from `@cedarjs/permissions/api` checks the signed-in user's
|
|
55
|
+
permissions: those of their roles, in `currentUser.roles` (one role or a list of
|
|
56
|
+
them).
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// api/src/lib/permissions.ts
|
|
60
|
+
import { createPermissions } from '@cedarjs/permissions/api'
|
|
61
|
+
|
|
62
|
+
import { permissions } from '@my-app/permissions'
|
|
63
|
+
|
|
64
|
+
export const {
|
|
65
|
+
hasPermission,
|
|
66
|
+
requirePermission,
|
|
67
|
+
canGrantRole,
|
|
68
|
+
permissionsOf,
|
|
69
|
+
withPermissions,
|
|
70
|
+
} = createPermissions(permissions)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- `hasPermission('posts.edit')`: whether one of the user's roles has it. False
|
|
74
|
+
when no one is signed in.
|
|
75
|
+
- `requirePermission('posts.edit')`: throws `AuthenticationError` when no one is
|
|
76
|
+
signed in, and `ForbiddenError` when they may not.
|
|
77
|
+
- `canGrantRole(role)`: whether the user may give someone `role`, or take it
|
|
78
|
+
away: they hold every permission it has, so no one can hand out more than they
|
|
79
|
+
may do themselves. False for a role the definitions don't list.
|
|
80
|
+
- `permissionsOf(user)`: the permissions of `user.roles`.
|
|
81
|
+
- `withPermissions(user)`: `user` with a `permissions` list added, for
|
|
82
|
+
`getCurrentUser` to return.
|
|
83
|
+
|
|
84
|
+
Permission names are typed from the definitions, so a typo in a service is a
|
|
85
|
+
type error. A name that isn't defined (a typo in an SDL directive) throws.
|
|
86
|
+
|
|
87
|
+
Fields use the `@requirePermission` directive that setup adds:
|
|
88
|
+
|
|
89
|
+
```graphql
|
|
90
|
+
type Mutation {
|
|
91
|
+
updatePost(id: Int!, input: UpdatePostInput!): Post!
|
|
92
|
+
@requirePermission(permission: "posts.edit")
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## On the web
|
|
97
|
+
|
|
98
|
+
`getCurrentUser` returns the user through `withPermissions`, so
|
|
99
|
+
`currentUser.permissions` has the user's permissions, and
|
|
100
|
+
`useAuth().hasPermission('posts.edit')` and
|
|
101
|
+
`<PrivateSet permission="posts.edit">` check them. The web only decides what to
|
|
102
|
+
show; the api's checks are the guard.
|
|
103
|
+
|
|
104
|
+
## In tests
|
|
105
|
+
|
|
106
|
+
`mockCurrentUser({ roles: ['editor'] })` gives the user the permissions the
|
|
107
|
+
app's definitions give `editor`, on the api and on the web.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { PermissionDefinitions } from './definePermissions.js';
|
|
2
|
+
/** A user as `getCurrentUser` returns it, with its permissions */
|
|
3
|
+
export type WithPermissions<User, Permission extends string> = User & {
|
|
4
|
+
permissions: Permission[];
|
|
5
|
+
};
|
|
6
|
+
export interface Permissions<Permission extends string> {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the signed-in user may do `permission`: one of their roles,
|
|
9
|
+
* in `currentUser.roles`, has it. False when no one is signed in.
|
|
10
|
+
*/
|
|
11
|
+
hasPermission(permission: Permission): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Guards a service or directive on `hasPermission(permission)`. Throws
|
|
14
|
+
* `AuthenticationError` when no one is signed in, and `ForbiddenError`
|
|
15
|
+
* when they may not.
|
|
16
|
+
*/
|
|
17
|
+
requirePermission(permission: Permission): void;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the signed-in user may give `role` to someone, or take it
|
|
20
|
+
* away: they hold every permission `role` has, so no one can hand out
|
|
21
|
+
* more than they may do themselves. False for a role the definitions
|
|
22
|
+
* don't list.
|
|
23
|
+
*
|
|
24
|
+
* This is only the role half: who may manage users at all is a
|
|
25
|
+
* permission of its own (`users.manage`, say) on the field.
|
|
26
|
+
*/
|
|
27
|
+
canGrantRole(role: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* What `user` may do: the permissions of each of its roles, from
|
|
30
|
+
* `user.roles` (one role, or a list of them). The one place a user's
|
|
31
|
+
* permissions are worked out, which `hasPermission`, `requirePermission`
|
|
32
|
+
* and `withPermissions` all use.
|
|
33
|
+
*/
|
|
34
|
+
permissionsOf(user: unknown): Permission[];
|
|
35
|
+
/**
|
|
36
|
+
* `user` with its permissions added, for `getCurrentUser` to return, so
|
|
37
|
+
* the web gets them (`useAuth().hasPermission`)
|
|
38
|
+
*/
|
|
39
|
+
withPermissions<User extends object>(user: User): WithPermissions<User, Permission>;
|
|
40
|
+
withPermissions<User extends object>(user: User | null): WithPermissions<User, Permission> | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Permissions on top of the user's roles: roles map to permissions in one
|
|
44
|
+
* place, and fields and services ask for a permission instead of listing
|
|
45
|
+
* roles, so adding a role is one entry in the definitions instead of an
|
|
46
|
+
* edit to every field it should reach.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* // api/src/lib/permissions.ts
|
|
50
|
+
* import { permissions } from '@my-app/permissions'
|
|
51
|
+
*
|
|
52
|
+
* export const { hasPermission, requirePermission, canGrantRole, withPermissions } =
|
|
53
|
+
* createPermissions(permissions)
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* `permissions` comes from `definePermissions`. A user's permissions are
|
|
57
|
+
* those of their roles, in `currentUser.roles`. Fails closed: a user whose
|
|
58
|
+
* roles don't have the permission is refused, as is a role the definitions
|
|
59
|
+
* don't list. Permission names are typed, so a typo in a service is a type
|
|
60
|
+
* error; a name that isn't defined (a typo in an SDL directive) throws
|
|
61
|
+
* instead of quietly refusing everyone.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createPermissions<const Permission extends string>(definitions: PermissionDefinitions<Permission>): Permissions<Permission>;
|
|
64
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAEnE,kEAAkE;AAClE,MAAM,MAAM,eAAe,CAAC,IAAI,EAAE,UAAU,SAAS,MAAM,IAAI,IAAI,GAAG;IACpE,WAAW,EAAE,UAAU,EAAE,CAAA;CAC1B,CAAA;AAED,MAAM,WAAW,WAAW,CAAC,UAAU,SAAS,MAAM;IACpD;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAA;IAC9C;;;;OAIG;IACH,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;IAC/C;;;;;;;;OAQG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACnC;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;IAC1C;;;OAGG;IACH,eAAe,CAAC,IAAI,SAAS,MAAM,EACjC,IAAI,EAAE,IAAI,GACT,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IACpC,eAAe,CAAC,IAAI,SAAS,MAAM,EACjC,IAAI,EAAE,IAAI,GAAG,IAAI,GAChB,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,IAAI,CAAA;CAC5C;AAmBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,UAAU,SAAS,MAAM,EAC/D,WAAW,EAAE,qBAAqB,CAAC,UAAU,CAAC,GAC7C,WAAW,CAAC,UAAU,CAAC,CA6CzB"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { context } from "@cedarjs/context";
|
|
2
|
+
import { AuthenticationError, ForbiddenError } from "@cedarjs/graphql-server";
|
|
3
|
+
import {
|
|
4
|
+
assertPermission,
|
|
5
|
+
assertPermissionDefinitions,
|
|
6
|
+
coversRole,
|
|
7
|
+
permissionsOf
|
|
8
|
+
} from "./definePermissions.js";
|
|
9
|
+
function rolesOf(user) {
|
|
10
|
+
if (typeof user !== "object" || user === null || !("roles" in user)) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
const { roles } = user;
|
|
14
|
+
if (typeof roles === "string") {
|
|
15
|
+
return [roles];
|
|
16
|
+
}
|
|
17
|
+
return Array.isArray(roles) ? roles.filter((role) => typeof role === "string") : [];
|
|
18
|
+
}
|
|
19
|
+
function createPermissions(definitions) {
|
|
20
|
+
assertPermissionDefinitions(definitions);
|
|
21
|
+
const userPermissions = (user) => permissionsOf(definitions, rolesOf(user));
|
|
22
|
+
const hasPermission = (permission) => {
|
|
23
|
+
assertPermission(definitions, permission);
|
|
24
|
+
return userPermissions(context.currentUser).includes(permission);
|
|
25
|
+
};
|
|
26
|
+
function withPermissions(user) {
|
|
27
|
+
if (!user) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return { ...user, permissions: userPermissions(user) };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
hasPermission,
|
|
34
|
+
permissionsOf: userPermissions,
|
|
35
|
+
withPermissions,
|
|
36
|
+
canGrantRole(role) {
|
|
37
|
+
if (!context.currentUser) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return coversRole(definitions, userPermissions(context.currentUser), role);
|
|
41
|
+
},
|
|
42
|
+
requirePermission(permission) {
|
|
43
|
+
assertPermission(definitions, permission);
|
|
44
|
+
if (!context.currentUser) {
|
|
45
|
+
throw new AuthenticationError("You don't have permission to do that.");
|
|
46
|
+
}
|
|
47
|
+
if (!hasPermission(permission)) {
|
|
48
|
+
throw new ForbiddenError("You don't have access to do that.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
createPermissions
|
|
55
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every permission an app checks, and what each role may do.
|
|
3
|
+
*
|
|
4
|
+
* Plain data with no api code in it, so both sides can import it: the api to
|
|
5
|
+
* check permissions (`createPermissions`), and the web for the names.
|
|
6
|
+
*/
|
|
7
|
+
export interface PermissionDefinitions<Name extends string = string> {
|
|
8
|
+
/**
|
|
9
|
+
* Every permission the app checks, by name. Fields, services and the web
|
|
10
|
+
* ask for these names, so they're defined in code. A name is a stable id,
|
|
11
|
+
* so it can also key what an app shows for it, such as translations.
|
|
12
|
+
*/
|
|
13
|
+
permissions: readonly Name[];
|
|
14
|
+
/**
|
|
15
|
+
* What each role may do: a role is a named bundle of permissions. A role
|
|
16
|
+
* that isn't listed has no permissions.
|
|
17
|
+
*/
|
|
18
|
+
roles: Readonly<Record<string, readonly Name[]>>;
|
|
19
|
+
}
|
|
20
|
+
/** The permission names in `definitions` */
|
|
21
|
+
export type PermissionName<Definitions> = Definitions extends PermissionDefinitions<infer Name> ? Name : never;
|
|
22
|
+
/**
|
|
23
|
+
* Throws when a role lists a permission that `permissions` doesn't define.
|
|
24
|
+
* The types catch this in TypeScript; this is for JavaScript, and for
|
|
25
|
+
* definitions built at runtime.
|
|
26
|
+
*/
|
|
27
|
+
export declare function assertPermissionDefinitions(definitions: PermissionDefinitions): void;
|
|
28
|
+
/**
|
|
29
|
+
* Defines an app's permissions, and what each role may do. Permission names
|
|
30
|
+
* are typed from `permissions`, so a role listing a name that isn't defined
|
|
31
|
+
* is a type error.
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* export const permissions = definePermissions({
|
|
35
|
+
* permissions: ['projects.read', 'members.manage'],
|
|
36
|
+
* roles: {
|
|
37
|
+
* owner: ['projects.read', 'members.manage'],
|
|
38
|
+
* member: ['projects.read'],
|
|
39
|
+
* },
|
|
40
|
+
* })
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Pass the result to `createPermissions` on the api.
|
|
44
|
+
*/
|
|
45
|
+
export declare function definePermissions<const Name extends string>(definitions: {
|
|
46
|
+
permissions: readonly Name[];
|
|
47
|
+
roles: Readonly<Record<string, readonly NoInfer<Name>[]>>;
|
|
48
|
+
}): PermissionDefinitions<Name>;
|
|
49
|
+
/** Whether `name` is one of the permissions `definitions` defines */
|
|
50
|
+
export declare function isPermission<Name extends string>(definitions: PermissionDefinitions<Name>, name: unknown): name is Name;
|
|
51
|
+
/**
|
|
52
|
+
* Throws when `definitions` doesn't define `name`, so a typo (in an SDL
|
|
53
|
+
* directive, say) is an error instead of a check that refuses everyone
|
|
54
|
+
*/
|
|
55
|
+
export declare function assertPermission<Name extends string>(definitions: PermissionDefinitions<Name>, name: string): asserts name is Name;
|
|
56
|
+
/**
|
|
57
|
+
* What `roles` may do: every permission any of them has. The one place a
|
|
58
|
+
* role becomes permissions. A role the definitions don't list has none.
|
|
59
|
+
*/
|
|
60
|
+
export declare function permissionsOf<Name extends string>(definitions: PermissionDefinitions<Name>, roles: string | readonly string[] | null | undefined): Name[];
|
|
61
|
+
/**
|
|
62
|
+
* Whether `held` covers `role`: it has every permission `role` has. This is
|
|
63
|
+
* who may give someone a role, so no one can hand out more than they may do
|
|
64
|
+
* themselves. False for a role the definitions don't list.
|
|
65
|
+
*/
|
|
66
|
+
export declare function coversRole<Name extends string>(definitions: PermissionDefinitions<Name>, held: readonly string[], role: string): boolean;
|
|
67
|
+
//# sourceMappingURL=definePermissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definePermissions.d.ts","sourceRoot":"","sources":["../src/definePermissions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACjE;;;;OAIG;IACH,WAAW,EAAE,SAAS,IAAI,EAAE,CAAA;IAC5B;;;OAGG;IACH,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,CAAA;CACjD;AAED,4CAA4C;AAC5C,MAAM,MAAM,cAAc,CAAC,WAAW,IACpC,WAAW,SAAS,qBAAqB,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,CAAA;AAEtE;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,qBAAqB,QAanC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,EAAE,WAAW,EAAE;IACxE,WAAW,EAAE,SAAS,IAAI,EAAE,CAAA;IAC5B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;CAC1D,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAI9B;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,IAAI,SAAS,MAAM,EAC9C,WAAW,EAAE,qBAAqB,CAAC,IAAI,CAAC,EACxC,IAAI,EAAE,OAAO,GACZ,IAAI,IAAI,IAAI,CAEd;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAClD,WAAW,EAAE,qBAAqB,CAAC,IAAI,CAAC,EACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,IAAI,IAAI,CAOtB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,MAAM,EAC/C,WAAW,EAAE,qBAAqB,CAAC,IAAI,CAAC,EACxC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GACnD,IAAI,EAAE,CAcR;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,SAAS,MAAM,EAC5C,WAAW,EAAE,qBAAqB,CAAC,IAAI,CAAC,EACxC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,MAAM,GACX,OAAO,CAQT"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
function assertPermissionDefinitions(definitions) {
|
|
2
|
+
const defined = new Set(definitions.permissions);
|
|
3
|
+
for (const [role, names] of Object.entries(definitions.roles)) {
|
|
4
|
+
for (const name of names) {
|
|
5
|
+
if (!defined.has(name)) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
`Unknown permission "${name}" in role "${role}". Add it to \`permissions\` in your permission definitions first.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function definePermissions(definitions) {
|
|
14
|
+
assertPermissionDefinitions(definitions);
|
|
15
|
+
return definitions;
|
|
16
|
+
}
|
|
17
|
+
function isPermission(definitions, name) {
|
|
18
|
+
return definitions.permissions.some((permission) => permission === name);
|
|
19
|
+
}
|
|
20
|
+
function assertPermission(definitions, name) {
|
|
21
|
+
if (!isPermission(definitions, name)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Unknown permission "${name}". Add it to \`permissions\` in your permission definitions before using it.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function permissionsOf(definitions, roles) {
|
|
28
|
+
const list = typeof roles === "string" ? [roles] : roles ?? [];
|
|
29
|
+
const permissions = /* @__PURE__ */ new Set();
|
|
30
|
+
for (const role of list) {
|
|
31
|
+
if (Object.hasOwn(definitions.roles, role)) {
|
|
32
|
+
for (const permission of definitions.roles[role]) {
|
|
33
|
+
permissions.add(permission);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return [...permissions];
|
|
38
|
+
}
|
|
39
|
+
function coversRole(definitions, held, role) {
|
|
40
|
+
if (!Object.hasOwn(definitions.roles, role)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return definitions.roles[role].every(
|
|
44
|
+
(permission) => held.includes(permission)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
assertPermission,
|
|
49
|
+
assertPermissionDefinitions,
|
|
50
|
+
coversRole,
|
|
51
|
+
definePermissions,
|
|
52
|
+
isPermission,
|
|
53
|
+
permissionsOf
|
|
54
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,UAAU,EACV,iBAAiB,EACjB,YAAY,EACZ,aAAa,GACd,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EACV,qBAAqB,EACrB,cAAc,GACf,MAAM,wBAAwB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertPermission,
|
|
3
|
+
assertPermissionDefinitions,
|
|
4
|
+
coversRole,
|
|
5
|
+
definePermissions,
|
|
6
|
+
isPermission,
|
|
7
|
+
permissionsOf
|
|
8
|
+
} from "./definePermissions.js";
|
|
9
|
+
export {
|
|
10
|
+
assertPermission,
|
|
11
|
+
assertPermissionDefinitions,
|
|
12
|
+
coversRole,
|
|
13
|
+
definePermissions,
|
|
14
|
+
isPermission,
|
|
15
|
+
permissionsOf
|
|
16
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarjs/permissions",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "8.0.0-canary.3571",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/cedarjs/cedar.git",
|
|
7
|
+
"directory": "packages/permissions"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"default": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"./api": {
|
|
19
|
+
"default": {
|
|
20
|
+
"types": "./dist/api.d.ts",
|
|
21
|
+
"default": "./dist/api.js"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"!dist/**/*.test.d.*"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "node ./build.mts",
|
|
31
|
+
"build:pack": "yarn pack -o cedarjs-permissions.tgz",
|
|
32
|
+
"build:types": "tsc --build --verbose ./tsconfig.build.json",
|
|
33
|
+
"check:attw": "yarn cedar-fwtools-attw",
|
|
34
|
+
"check:package": "concurrently npm:check:attw yarn:publint",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:types": "tstyche",
|
|
37
|
+
"test:watch": "vitest watch"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@cedarjs/context": "8.0.0-canary.3571",
|
|
41
|
+
"@cedarjs/graphql-server": "8.0.0-canary.3571"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
45
|
+
"@cedarjs/framework-tools": "8.0.0-canary.3571",
|
|
46
|
+
"concurrently": "9.2.5",
|
|
47
|
+
"publint": "0.3.25",
|
|
48
|
+
"tstyche": "5.0.2",
|
|
49
|
+
"typescript": "5.9.3",
|
|
50
|
+
"vitest": "4.1.12"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=24"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|