@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 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, hasOrgRole, requireUserAuth, requireOrgAuth } =
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
- `requireOrgAuth` (and the `@requireOrgAuth` directive `setup tenancy` adds) is
95
- about the current organization: it throws `AuthenticationError` with no
96
- current user, and `ForbiddenError` with no current organization or when the
97
- user's role there isn't one of `roles` when they're given. With no `roles`,
98
- any member gets in. `requireUserAuth` (and `@requireUserAuth`) is for fields about the user
99
- rather than an organization: it needs a signed-in user, and checks `roles`
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: ['projects.read', 'projects.edit', 'members.manage'],
111
+ permissions: [
112
+ 'organization.read',
113
+ 'projects.read',
114
+ 'projects.edit',
115
+ 'members.manage',
116
+ ],
111
117
  roles: {
112
- owner: ['projects.read', 'projects.edit', 'members.manage'],
113
- member: ['projects.read'],
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 { hasPermission, requirePermission, withPermissions } =
125
- createPermissions(permissions)
130
+ export const {
131
+ hasPermission,
132
+ requirePermission,
133
+ canGrantRole,
134
+ withPermissions,
135
+ } = createPermissions(permissions)
126
136
  ```
127
137
 
128
- Roles map to permissions in one place; fields ask for a permission with
129
- `@requirePermission(permission: "projects.edit")` or `requirePermission()`.
130
- A role only gets what its list says, so a new role is one entry. Permission
131
- names are typed from the definitions. `withPermissions` in `getCurrentUser` gives each
132
- membership its permissions, which `useAuth().hasPermission()` checks inside
133
- an organization route. Every permission is about an organization: fields about the user
134
- rather than an organization use `@requireUserAuth`.
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`/`hasOrgRole` for the client
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. `useCurrentOrg().hasOrgRole` checks the user's role
144
- in the organization. To guard routes by it, give a `PrivateSet` `orgRoles` or
145
- `orgPermission`, and `unauthorized`:
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 role and permission in the organization of the route's
173
- `orgSlug` param. A `PrivateSet` with `orgRoles` or `orgPermission` throws when
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, hasOrgRole, requireUserAuth, requireOrgAuth } =
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 {};
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAKA,KAAK,KAAK,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;AAwBvC;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CA4BzE;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAA;IAClC;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG,IAAI,CAAA;IACjD;;;;;;;;OAQG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG,IAAI,CAAA;IAClD,iEAAiE;IACjE,UAAU,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CA8C/C"}
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
- import { getCurrentOrg, isUserWithMemberships } from "./context.js";
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
  };
@@ -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(options: {
25
- permission: Permission;
26
- }): void;
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,OAAO,EAAE;QAAE,UAAU,EAAE,UAAU,CAAA;KAAE,GAAG,IAAI,CAAA;IAC5D;;;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,CAwFzB"}
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"}
@@ -46,7 +46,17 @@ function createPermissions(definitions) {
46
46
  hasPermission,
47
47
  permissionsOf,
48
48
  withPermissions,
49
- requirePermission({ permission }) {
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 `orgRoles` and `orgPermission` on
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;AASxE,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;AAiJD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAwBzE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,SAAkB,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 }) }) });
@@ -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';
@@ -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,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,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"}
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
@@ -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
@@ -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,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACtE,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B"}
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.3565",
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.3565",
48
- "@cedarjs/context": "8.0.0-canary.3565",
49
- "@cedarjs/graphql-server": "8.0.0-canary.3565"
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.3565",
55
- "@cedarjs/framework-tools": "8.0.0-canary.3565",
56
- "@cedarjs/router": "8.0.0-canary.3565",
57
- "@cedarjs/web": "8.0.0-canary.3565",
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.3565",
84
- "@cedarjs/router": "8.0.0-canary.3565",
85
- "@cedarjs/web": "8.0.0-canary.3565",
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
  },
@@ -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"}
@@ -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
- };