@cedarjs/tenancy 8.0.0-canary.3565 → 8.0.0-canary.3567
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 +48 -33
- package/dist/auth.d.ts +5 -33
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -42
- package/dist/permissions.d.ts +12 -4
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +11 -1
- package/dist/web/createTenancy.d.ts +1 -2
- package/dist/web/createTenancy.d.ts.map +1 -1
- package/dist/web/createTenancy.js +0 -2
- package/dist/web/index.d.ts +0 -1
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +0 -2
- package/dist/web/types.d.ts +0 -1
- package/dist/web/types.d.ts.map +1 -1
- package/package.json +11 -11
- package/dist/web/hasOrgRole.d.ts +0 -12
- package/dist/web/hasOrgRole.d.ts.map +0 -1
- package/dist/web/hasOrgRole.js +0 -16
package/README.md
CHANGED
|
@@ -79,38 +79,44 @@ import {
|
|
|
79
79
|
// api/src/lib/auth.ts, in place of the single-tenant hasRole/requireAuth
|
|
80
80
|
import { createTenancyAuth } from '@cedarjs/tenancy'
|
|
81
81
|
|
|
82
|
-
export const { hasUserRole,
|
|
83
|
-
createTenancyAuth()
|
|
82
|
+
export const { hasUserRole, requireUserAuth } = createTenancyAuth()
|
|
84
83
|
|
|
85
84
|
// In a service:
|
|
86
|
-
requireOrgAuth({ roles: ['owner', 'admin'] }) // in the current organization
|
|
87
85
|
requireUserAuth() // signed in, no organization needed
|
|
88
86
|
|
|
89
87
|
// Anywhere:
|
|
90
|
-
if (hasOrgRole('owner')) { ... } // in the current organization
|
|
91
88
|
if (hasUserRole('superuser')) { ... } // currentUser.roles
|
|
92
89
|
```
|
|
93
90
|
|
|
94
|
-
`
|
|
95
|
-
about the
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
against `currentUser.roles`. `hasOrgRole` and `hasUserRole` are the boolean
|
|
101
|
-
checks to match. There's no `requireAuth` or `hasRole`.
|
|
91
|
+
`requireUserAuth` (and the `@requireUserAuth` directive `setup tenancy` adds)
|
|
92
|
+
is for fields about the user rather than an organization: their account,
|
|
93
|
+
their memberships, accepting an invitation. It needs a signed-in user, and
|
|
94
|
+
checks `roles` against `currentUser.roles`. There's no `requireAuth` or
|
|
95
|
+
`hasRole`, and no role check on the current organization: fields about an
|
|
96
|
+
organization ask for a permission (below).
|
|
102
97
|
|
|
103
98
|
## Permissions
|
|
104
99
|
|
|
100
|
+
Roles are named bundles of permissions, defined in one place. Fields,
|
|
101
|
+
services and components check permissions, never roles, so a role can do
|
|
102
|
+
exactly what its list says.
|
|
103
|
+
|
|
105
104
|
```ts
|
|
106
105
|
// packages/permissions/src/index.ts, shared by the api and web sides
|
|
107
106
|
import { definePermissions } from '@cedarjs/tenancy/permissions'
|
|
108
107
|
|
|
108
|
+
const member = ['organization.read', 'projects.read'] as const
|
|
109
|
+
|
|
109
110
|
export const permissions = definePermissions({
|
|
110
|
-
permissions: [
|
|
111
|
+
permissions: [
|
|
112
|
+
'organization.read',
|
|
113
|
+
'projects.read',
|
|
114
|
+
'projects.edit',
|
|
115
|
+
'members.manage',
|
|
116
|
+
],
|
|
111
117
|
roles: {
|
|
112
|
-
owner: [
|
|
113
|
-
member
|
|
118
|
+
owner: [...member, 'projects.edit', 'members.manage'],
|
|
119
|
+
member,
|
|
114
120
|
},
|
|
115
121
|
})
|
|
116
122
|
```
|
|
@@ -121,28 +127,40 @@ import { createPermissions } from '@cedarjs/tenancy'
|
|
|
121
127
|
|
|
122
128
|
import { permissions } from '@my-app/permissions'
|
|
123
129
|
|
|
124
|
-
export const {
|
|
125
|
-
|
|
130
|
+
export const {
|
|
131
|
+
hasPermission,
|
|
132
|
+
requirePermission,
|
|
133
|
+
canGrantRole,
|
|
134
|
+
withPermissions,
|
|
135
|
+
} = createPermissions(permissions)
|
|
126
136
|
```
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
Fields ask for a permission with `@requirePermission(permission:
|
|
139
|
+
"projects.edit")`, and services with `requirePermission('projects.edit')`.
|
|
140
|
+
"Any member" is a permission every role lists, such as `organization.read`,
|
|
141
|
+
so a new role starts with nothing until its entry says otherwise. Permission
|
|
142
|
+
names are typed from the definitions, and an unknown name throws.
|
|
143
|
+
|
|
144
|
+
`canGrantRole(role)` is whether the signed-in user may give someone `role`,
|
|
145
|
+
or take it away: they hold every permission it has. Nobody can hand out more
|
|
146
|
+
than they may do themselves, and a new role needs no grant table. It
|
|
147
|
+
doesn't check `members.manage`: whoever calls it checks that first, as the
|
|
148
|
+
generated `inviteMember` and `removeMember` do with
|
|
149
|
+
`@requirePermission(permission: "members.manage")`.
|
|
150
|
+
|
|
151
|
+
`withPermissions` in `getCurrentUser` gives each membership its permissions,
|
|
152
|
+
which `useAuth().hasPermission()` checks inside an organization route.
|
|
135
153
|
|
|
136
154
|
## Web
|
|
137
155
|
|
|
138
156
|
`@cedarjs/tenancy/web` exports `createTenancy`, a ready-made `tenancy`,
|
|
139
|
-
`useCurrentOrg`, `useOrgBySlug` and `getMemberships
|
|
157
|
+
`useCurrentOrg`, `useOrgBySlug` and `getMemberships` for the client
|
|
140
158
|
side. Pass the tenancy to the router and put organization routes in a
|
|
141
159
|
`PrivateSet`: the router renders a per-organization Apollo client, keyed by the
|
|
142
160
|
immutable organization id so switching organizations never mixes caches, around
|
|
143
|
-
the set's layouts and page. `
|
|
144
|
-
in the organization. To guard routes by
|
|
145
|
-
`orgPermission
|
|
161
|
+
the set's layouts and page. `useAuth().hasPermission()` checks what the user
|
|
162
|
+
may do in the organization. To guard routes by a permission of the user's
|
|
163
|
+
membership, give a `PrivateSet` `orgPermission` and `unauthorized`:
|
|
146
164
|
|
|
147
165
|
<!-- prettier-ignore -->
|
|
148
166
|
```tsx
|
|
@@ -152,9 +170,6 @@ import { tenancy } from 'src/tenancy'
|
|
|
152
170
|
<PrivateSet wrap={OrgLayout} unauthenticated="login">
|
|
153
171
|
<Route path="/org/{orgSlug}/projects" page={ProjectsPage} name="projects" />
|
|
154
172
|
</PrivateSet>
|
|
155
|
-
<PrivateSet wrap={OrgLayout} unauthenticated="login" unauthorized="projects" orgRoles="owner">
|
|
156
|
-
<Route path="/org/{orgSlug}/settings" page={SettingsPage} name="settings" />
|
|
157
|
-
</PrivateSet>
|
|
158
173
|
<PrivateSet wrap={OrgLayout} unauthenticated="login" unauthorized="projects" orgPermission="members.manage">
|
|
159
174
|
<Route path="/org/{orgSlug}/members" page={MembersPage} name="members" />
|
|
160
175
|
</PrivateSet>
|
|
@@ -169,8 +184,8 @@ export const tenancy = createTenancy({
|
|
|
169
184
|
})
|
|
170
185
|
```
|
|
171
186
|
|
|
172
|
-
The router checks the
|
|
173
|
-
`orgSlug` param. A `PrivateSet` with `
|
|
187
|
+
The router checks the permission in the organization of the route's
|
|
188
|
+
`orgSlug` param. A `PrivateSet` with `orgPermission` throws when
|
|
174
189
|
the router has no `tenancy` or its `useOrg` finds no organization for the
|
|
175
190
|
route; with the default lookup, that's a route without `{orgSlug}`. A route
|
|
176
191
|
with `{orgSlug}` outside a `PrivateSet` is a public page with no organization
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,38 +1,10 @@
|
|
|
1
1
|
type Roles = string | readonly string[];
|
|
2
|
-
/**
|
|
3
|
-
* Whether the current request holds one of `roles` in an organization.
|
|
4
|
-
*
|
|
5
|
-
* With `organizationId`, this checks `context.currentUser.memberships` for a
|
|
6
|
-
* membership in that specific organization — useful for checking access to
|
|
7
|
-
* an organization other than the one in scope. Without it, this checks the
|
|
8
|
-
* role on `context.currentOrg`, the organization already resolved for this
|
|
9
|
-
* request.
|
|
10
|
-
*
|
|
11
|
-
* Returns `false` when there's no authenticated user, no matching
|
|
12
|
-
* organization, or no matching membership. An empty `roles` array returns
|
|
13
|
-
* `true` as long as a matching membership exists.
|
|
14
|
-
*/
|
|
15
|
-
export declare function hasOrgRole(roles: Roles, organizationId?: string): boolean;
|
|
16
2
|
export interface TenancyAuth {
|
|
17
3
|
/**
|
|
18
4
|
* Whether the signed-in user holds one of `roles` in `currentUser.roles`,
|
|
19
|
-
* in any organization or none. What the single-tenant `hasRole()` checked
|
|
20
|
-
* `hasOrgRole()` checks the role in an organization.
|
|
5
|
+
* in any organization or none. What the single-tenant `hasRole()` checked.
|
|
21
6
|
*/
|
|
22
7
|
hasUserRole(roles: Roles): boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Guards a service or directive on the current organization: signed in,
|
|
25
|
-
* a member of the current organization and, when `roles` are given, with
|
|
26
|
-
* one of them there. With no `roles`, any member gets in, as any
|
|
27
|
-
* signed-in user gets past the single-tenant `requireAuth()`: use `roles`
|
|
28
|
-
* or `requirePermission()` for anything narrower.
|
|
29
|
-
*
|
|
30
|
-
* Throws `AuthenticationError` when no one is signed in, and
|
|
31
|
-
* `ForbiddenError` otherwise.
|
|
32
|
-
*/
|
|
33
|
-
requireOrgAuth(options?: {
|
|
34
|
-
roles?: Roles;
|
|
35
|
-
}): void;
|
|
36
8
|
/**
|
|
37
9
|
* Guards a service or directive on the signed-in user alone, in any
|
|
38
10
|
* organization or none: their account, their memberships, accepting an
|
|
@@ -45,20 +17,20 @@ export interface TenancyAuth {
|
|
|
45
17
|
requireUserAuth(options?: {
|
|
46
18
|
roles?: Roles;
|
|
47
19
|
}): void;
|
|
48
|
-
/** The standalone `hasOrgRole`: with no roles, any membership */
|
|
49
|
-
hasOrgRole(roles?: Roles, organizationId?: string): boolean;
|
|
50
20
|
}
|
|
51
21
|
/**
|
|
52
22
|
* The auth checks for an app with tenancy, which `api/src/lib/auth.ts`
|
|
53
23
|
* exports in place of its single-tenant `hasRole` and `requireAuth`:
|
|
54
24
|
*
|
|
55
25
|
* ```ts
|
|
56
|
-
* export const { hasUserRole,
|
|
26
|
+
* export const { hasUserRole, requireUserAuth } =
|
|
57
27
|
* createTenancyAuth()
|
|
58
28
|
* ```
|
|
59
29
|
*
|
|
60
30
|
* There's no `hasRole` or `requireAuth`: every check says whether it's
|
|
61
|
-
* about the signed-in user alone or the current organization.
|
|
31
|
+
* about the signed-in user alone or the current organization. Checks on
|
|
32
|
+
* the current organization are permissions (`createPermissions`), so a
|
|
33
|
+
* role can do only what its entry in the permission definitions lists.
|
|
62
34
|
*/
|
|
63
35
|
export declare function createTenancyAuth(): TenancyAuth;
|
|
64
36
|
export {};
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,KAAK,KAAK,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;AAwBvC,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAA;IAClC;;;;;;;;OAQG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG,IAAI,CAAA;CACnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAuB/C"}
|
package/dist/auth.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { context } from "@cedarjs/context";
|
|
2
2
|
import { AuthenticationError, ForbiddenError } from "@cedarjs/graphql-server";
|
|
3
|
-
|
|
4
|
-
const toList = (roles) => roles === void 0 ? [] : typeof roles === "string" ? [roles] : [...roles];
|
|
3
|
+
const toList = (roles) => typeof roles === "string" ? [roles] : [...roles];
|
|
5
4
|
function userRoles() {
|
|
6
5
|
const roles = context.currentUser?.roles;
|
|
7
6
|
if (typeof roles === "string") {
|
|
@@ -12,27 +11,6 @@ function userRoles() {
|
|
|
12
11
|
}
|
|
13
12
|
return [];
|
|
14
13
|
}
|
|
15
|
-
function hasOrgRole(roles, organizationId) {
|
|
16
|
-
const roleList = toList(roles);
|
|
17
|
-
if (organizationId) {
|
|
18
|
-
const currentUser = context.currentUser;
|
|
19
|
-
if (!isUserWithMemberships(currentUser)) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
const membership = currentUser.memberships.find(
|
|
23
|
-
(m) => m.organizationId === organizationId
|
|
24
|
-
);
|
|
25
|
-
if (!membership) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
return roleList.length === 0 || roleList.includes(membership.role);
|
|
29
|
-
}
|
|
30
|
-
const currentOrg = getCurrentOrg();
|
|
31
|
-
if (!currentOrg) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
return roleList.length === 0 || roleList.includes(currentOrg.role);
|
|
35
|
-
}
|
|
36
14
|
function createTenancyAuth() {
|
|
37
15
|
return {
|
|
38
16
|
hasUserRole(roles) {
|
|
@@ -46,27 +24,9 @@ function createTenancyAuth() {
|
|
|
46
24
|
if (roles && !userRoles().some((role) => toList(roles).includes(role))) {
|
|
47
25
|
throw new ForbiddenError("You don't have access to do that.");
|
|
48
26
|
}
|
|
49
|
-
},
|
|
50
|
-
requireOrgAuth({ roles } = {}) {
|
|
51
|
-
if (!context.currentUser) {
|
|
52
|
-
throw new AuthenticationError("You don't have permission to do that.");
|
|
53
|
-
}
|
|
54
|
-
const currentOrg = getCurrentOrg();
|
|
55
|
-
if (!currentOrg) {
|
|
56
|
-
throw new ForbiddenError(
|
|
57
|
-
"You must be a member of an organization to do that."
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
if (roles && !toList(roles).includes(currentOrg.role)) {
|
|
61
|
-
throw new ForbiddenError("You don't have access to do that.");
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
hasOrgRole(roles = [], organizationId) {
|
|
65
|
-
return hasOrgRole(roles, organizationId);
|
|
66
27
|
}
|
|
67
28
|
};
|
|
68
29
|
}
|
|
69
30
|
export {
|
|
70
|
-
createTenancyAuth
|
|
71
|
-
hasOrgRole
|
|
31
|
+
createTenancyAuth
|
|
72
32
|
};
|
package/dist/permissions.d.ts
CHANGED
|
@@ -21,9 +21,17 @@ export interface Permissions<Permission extends string> {
|
|
|
21
21
|
* `AuthenticationError` when no one is signed in, and `ForbiddenError`
|
|
22
22
|
* when they may not.
|
|
23
23
|
*/
|
|
24
|
-
requirePermission(
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
requirePermission(permission: Permission): void;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the signed-in user may give `role` to someone in the current
|
|
27
|
+
* organization, or take it away: they hold every permission `role` has,
|
|
28
|
+
* so no one can hand out more than they may do themselves. False for a
|
|
29
|
+
* role the definitions don't list.
|
|
30
|
+
*
|
|
31
|
+
* This is only the role half: who may manage members at all is a
|
|
32
|
+
* permission of its own (`members.manage`, say) on the field.
|
|
33
|
+
*/
|
|
34
|
+
canGrantRole(role: string): boolean;
|
|
27
35
|
/**
|
|
28
36
|
* What a membership may do: its role's permissions. The one place a membership's permissions are worked out, which
|
|
29
37
|
* `hasPermission`, `requirePermission` and `withPermissions` all use.
|
|
@@ -55,7 +63,7 @@ export interface Permissions<Permission extends string> {
|
|
|
55
63
|
* // api/src/lib/permissions.ts
|
|
56
64
|
* import { permissions } from '@my-app/permissions'
|
|
57
65
|
*
|
|
58
|
-
* export const { hasPermission, requirePermission, withPermissions } =
|
|
66
|
+
* export const { hasPermission, requirePermission, canGrantRole, withPermissions } =
|
|
59
67
|
* createPermissions(permissions)
|
|
60
68
|
* ```
|
|
61
69
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAEnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,YAAY,EACV,qBAAqB,EACrB,cAAc,GACf,MAAM,wBAAwB,CAAA;AAE/B,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,CACzB,UAAU,EACV,UAAU,SAAS,MAAM,IACvB,UAAU,GAAG;IAAE,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAAA;AAE9C,MAAM,WAAW,WAAW,CAAC,UAAU,SAAS,MAAM;IACpD;;;;OAIG;IACH,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAA;IAC9C;;;;OAIG;IACH,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAEnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,YAAY,EACV,qBAAqB,EACrB,cAAc,GACf,MAAM,wBAAwB,CAAA;AAE/B,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,CACzB,UAAU,EACV,UAAU,SAAS,MAAM,IACvB,UAAU,GAAG;IAAE,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAAA;AAE9C,MAAM,WAAW,WAAW,CAAC,UAAU,SAAS,MAAM;IACpD;;;;OAIG;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;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,wBAAwB,GAAG,UAAU,EAAE,CAAA;IACjE;;;;OAIG;IACH,eAAe,CAAC,IAAI,SAAS;QAAE,WAAW,EAAE,wBAAwB,EAAE,CAAA;KAAE,EACtE,IAAI,EAAE,IAAI,GACT,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG;QAC7B,WAAW,EAAE,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,CAAA;KACxE,CAAA;IACD,eAAe,CAAC,IAAI,SAAS;QAAE,WAAW,EAAE,wBAAwB,EAAE,CAAA;KAAE,EACtE,IAAI,EAAE,IAAI,GAAG,IAAI,GAEf,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG;QAC3B,WAAW,EAAE,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,CAAA;KACxE,CAAC,GACF,IAAI,CAAA;CACT;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,UAAU,SAAS,MAAM,EAC/D,WAAW,EAAE,qBAAqB,CAAC,UAAU,CAAC,GAC7C,WAAW,CAAC,UAAU,CAAC,CAqGzB"}
|
package/dist/permissions.js
CHANGED
|
@@ -46,7 +46,17 @@ function createPermissions(definitions) {
|
|
|
46
46
|
hasPermission,
|
|
47
47
|
permissionsOf,
|
|
48
48
|
withPermissions,
|
|
49
|
-
|
|
49
|
+
canGrantRole(role) {
|
|
50
|
+
const membership = context.currentUser ? currentMembership() : void 0;
|
|
51
|
+
if (!membership || !Object.hasOwn(definitions.roles, role)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const held = permissionsOf(membership);
|
|
55
|
+
return definitions.roles[role].every(
|
|
56
|
+
(permission) => held.includes(permission)
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
requirePermission(permission) {
|
|
50
60
|
assertDefined(permission);
|
|
51
61
|
if (!context.currentUser) {
|
|
52
62
|
throw new AuthenticationError("You don't have permission to do that.");
|
|
@@ -36,8 +36,7 @@ export interface CreateTenancyOptions {
|
|
|
36
36
|
* Makes the `tenancy` to pass to the `Router`. The router then renders the
|
|
37
37
|
* organization's provider itself, around the layouts and page of every
|
|
38
38
|
* `PrivateSet` whose route is in an organization (by default, one with
|
|
39
|
-
* `{orgSlug}` in its path), and checks `
|
|
40
|
-
* those sets.
|
|
39
|
+
* `{orgSlug}` in its path), and checks `orgPermission` on those sets.
|
|
41
40
|
*
|
|
42
41
|
* Call it once, at the top level of a module, like `web/src/tenancy.tsx`:
|
|
43
42
|
* a new tenancy on each render would remount the provider.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createTenancy.d.ts","sourceRoot":"","sources":["../../src/web/createTenancy.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAoB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"createTenancy.d.ts","sourceRoot":"","sources":["../../src/web/createTenancy.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAoB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAQxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAOlD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC3B,SAAS,GAAG,IAAI,CAAA;AAErB,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC;AA+ID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAwBzE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,SAAkB,CAAA"}
|
|
@@ -5,7 +5,6 @@ import { HasPermissionContext } from "@cedarjs/auth";
|
|
|
5
5
|
import { navigate, useLocation, useParams } from "@cedarjs/router";
|
|
6
6
|
import { useCreateApolloClient } from "@cedarjs/web/apollo";
|
|
7
7
|
import { isOrgMembership } from "./getMemberships.js";
|
|
8
|
-
import { hasOrgRole } from "./hasOrgRole.js";
|
|
9
8
|
import { getOrgClient } from "./orgClients.js";
|
|
10
9
|
import { OrgContext } from "./OrgContext.js";
|
|
11
10
|
import { useOrgBySlug } from "./useOrgBySlug.js";
|
|
@@ -88,7 +87,6 @@ function OrgProviderImplementation({
|
|
|
88
87
|
const contextValue = {
|
|
89
88
|
org,
|
|
90
89
|
memberships,
|
|
91
|
-
hasOrgRole: (roles, organizationId) => hasOrgRole(memberships, roles, organizationId ?? org.id),
|
|
92
90
|
setOrg
|
|
93
91
|
};
|
|
94
92
|
return /* @__PURE__ */ jsx(ApolloProvider, { client: orgClient, children: /* @__PURE__ */ jsx(OrgContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(HasPermissionContext.Provider, { value: hasPermission, children }) }) });
|
package/dist/web/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export type { CreateTenancyOptions, UseTenancyOrg } from './createTenancy.js';
|
|
2
2
|
export { createTenancy, tenancy } from './createTenancy.js';
|
|
3
3
|
export { getMemberships } from './getMemberships.js';
|
|
4
|
-
export { hasOrgRole } from './hasOrgRole.js';
|
|
5
4
|
export { clearOrgClients } from './orgClients.js';
|
|
6
5
|
export type { CurrentOrgSummary, OrgContextValue, OrgMembership, } from './types.js';
|
|
7
6
|
export { useCurrentOrg } from './useCurrentOrg.js';
|
package/dist/web/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/web/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createTenancy, tenancy } from "./createTenancy.js";
|
|
2
2
|
import { getMemberships } from "./getMemberships.js";
|
|
3
|
-
import { hasOrgRole } from "./hasOrgRole.js";
|
|
4
3
|
import { clearOrgClients } from "./orgClients.js";
|
|
5
4
|
import { useCurrentOrg } from "./useCurrentOrg.js";
|
|
6
5
|
import { useOrgBySlug } from "./useOrgBySlug.js";
|
|
@@ -8,7 +7,6 @@ export {
|
|
|
8
7
|
clearOrgClients,
|
|
9
8
|
createTenancy,
|
|
10
9
|
getMemberships,
|
|
11
|
-
hasOrgRole,
|
|
12
10
|
tenancy,
|
|
13
11
|
useCurrentOrg,
|
|
14
12
|
useOrgBySlug
|
package/dist/web/types.d.ts
CHANGED
|
@@ -38,7 +38,6 @@ export interface CurrentOrgSummary {
|
|
|
38
38
|
export interface OrgContextValue {
|
|
39
39
|
org: CurrentOrgSummary | undefined;
|
|
40
40
|
memberships: OrgMembership[];
|
|
41
|
-
hasOrgRole(roles: string | string[], organizationId?: string): boolean;
|
|
42
41
|
setOrg(idOrSlug: string): void;
|
|
43
42
|
}
|
|
44
43
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/web/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,YAAY,EAAE;QACZ,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,iBAAiB,GAAG,SAAS,CAAA;IAClC,WAAW,EAAE,aAAa,EAAE,CAAA;IAC5B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,YAAY,EAAE;QACZ,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,iBAAiB,GAAG,SAAS,CAAA;IAClC,WAAW,EAAE,aAAa,EAAE,CAAA;IAC5B,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarjs/tenancy",
|
|
3
|
-
"version": "8.0.0-canary.
|
|
3
|
+
"version": "8.0.0-canary.3567",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/cedarjs/cedar.git",
|
|
@@ -44,17 +44,17 @@
|
|
|
44
44
|
"test:watch": "vitest watch"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@cedarjs/api": "8.0.0-canary.
|
|
48
|
-
"@cedarjs/context": "8.0.0-canary.
|
|
49
|
-
"@cedarjs/graphql-server": "8.0.0-canary.
|
|
47
|
+
"@cedarjs/api": "8.0.0-canary.3567",
|
|
48
|
+
"@cedarjs/context": "8.0.0-canary.3567",
|
|
49
|
+
"@cedarjs/graphql-server": "8.0.0-canary.3567"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@apollo/client": "4.3.5",
|
|
53
53
|
"@arethetypeswrong/cli": "0.18.5",
|
|
54
|
-
"@cedarjs/auth": "8.0.0-canary.
|
|
55
|
-
"@cedarjs/framework-tools": "8.0.0-canary.
|
|
56
|
-
"@cedarjs/router": "8.0.0-canary.
|
|
57
|
-
"@cedarjs/web": "8.0.0-canary.
|
|
54
|
+
"@cedarjs/auth": "8.0.0-canary.3567",
|
|
55
|
+
"@cedarjs/framework-tools": "8.0.0-canary.3567",
|
|
56
|
+
"@cedarjs/router": "8.0.0-canary.3567",
|
|
57
|
+
"@cedarjs/web": "8.0.0-canary.3567",
|
|
58
58
|
"@prisma/adapter-better-sqlite3": "7.10.1",
|
|
59
59
|
"@prisma/client": "7.10.1",
|
|
60
60
|
"@testing-library/dom": "10.4.2",
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"@apollo/client": "4.3.5",
|
|
83
|
-
"@cedarjs/auth": "8.0.0-canary.
|
|
84
|
-
"@cedarjs/router": "8.0.0-canary.
|
|
85
|
-
"@cedarjs/web": "8.0.0-canary.
|
|
83
|
+
"@cedarjs/auth": "8.0.0-canary.3567",
|
|
84
|
+
"@cedarjs/router": "8.0.0-canary.3567",
|
|
85
|
+
"@cedarjs/web": "8.0.0-canary.3567",
|
|
86
86
|
"@prisma/client": "7.10.1",
|
|
87
87
|
"react": "19.3.1"
|
|
88
88
|
},
|
package/dist/web/hasOrgRole.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { OrgMembership } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Pure role check over a memberships snapshot, with no dependency on auth or
|
|
4
|
-
* context. `useCurrentOrg().hasOrgRole` and the app's own code (checking a
|
|
5
|
-
* membership other than the current organization's) both build on this.
|
|
6
|
-
*
|
|
7
|
-
* Returns `false` when there is no membership in `organizationId`. An empty
|
|
8
|
-
* `roles` list returns `true` when a membership exists, matching
|
|
9
|
-
* `@cedarjs/tenancy`'s server-side `hasOrgRole`.
|
|
10
|
-
*/
|
|
11
|
-
export declare function hasOrgRole(memberships: readonly OrgMembership[] | undefined, roles: string | string[], organizationId: string): boolean;
|
|
12
|
-
//# sourceMappingURL=hasOrgRole.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hasOrgRole.d.ts","sourceRoot":"","sources":["../../src/web/hasOrgRole.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,WAAW,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,EACjD,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,cAAc,EAAE,MAAM,GACrB,OAAO,CAgBT"}
|
package/dist/web/hasOrgRole.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
function hasOrgRole(memberships, roles, organizationId) {
|
|
2
|
-
const membership = memberships?.find(
|
|
3
|
-
(candidate) => candidate.organizationId === organizationId
|
|
4
|
-
);
|
|
5
|
-
if (!membership) {
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
const roleList = Array.isArray(roles) ? roles : [roles];
|
|
9
|
-
if (roleList.length === 0) {
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
return roleList.includes(membership.role);
|
|
13
|
-
}
|
|
14
|
-
export {
|
|
15
|
-
hasOrgRole
|
|
16
|
-
};
|