@usehercules/convex 0.0.0
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 +478 -0
- package/dist/_generated/component.d.ts +184 -0
- package/dist/_generated/component.d.ts.map +1 -0
- package/dist/_generated/component.js +11 -0
- package/dist/_generated/component.js.map +1 -0
- package/dist/checker/cli.d.ts +3 -0
- package/dist/checker/cli.d.ts.map +1 -0
- package/dist/checker/cli.js +71 -0
- package/dist/checker/cli.js.map +1 -0
- package/dist/checker/index.d.ts +28 -0
- package/dist/checker/index.d.ts.map +1 -0
- package/dist/checker/index.js +1928 -0
- package/dist/checker/index.js.map +1 -0
- package/dist/client/access-admin.d.ts +818 -0
- package/dist/client/access-admin.d.ts.map +1 -0
- package/dist/client/access-admin.js +1830 -0
- package/dist/client/access-admin.js.map +1 -0
- package/dist/client/http.d.ts +19 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +76 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/index.d.ts +440 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +654 -0
- package/dist/client/index.js.map +1 -0
- package/dist/component/authz.d.ts +114 -0
- package/dist/component/authz.d.ts.map +1 -0
- package/dist/component/authz.js +168 -0
- package/dist/component/authz.js.map +1 -0
- package/dist/component/checks.d.ts +86 -0
- package/dist/component/checks.d.ts.map +1 -0
- package/dist/component/checks.js +184 -0
- package/dist/component/checks.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/effective.d.ts +82 -0
- package/dist/component/effective.d.ts.map +1 -0
- package/dist/component/effective.js +757 -0
- package/dist/component/effective.js.map +1 -0
- package/dist/component/queries.d.ts +170 -0
- package/dist/component/queries.d.ts.map +1 -0
- package/dist/component/queries.js +633 -0
- package/dist/component/queries.js.map +1 -0
- package/dist/component/schema.d.ts +258 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +222 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/sync.d.ts +85 -0
- package/dist/component/sync.d.ts.map +1 -0
- package/dist/component/sync.js +851 -0
- package/dist/component/sync.js.map +1 -0
- package/dist/shared/projection-protocol.d.ts +1624 -0
- package/dist/shared/projection-protocol.d.ts.map +1 -0
- package/dist/shared/projection-protocol.js +561 -0
- package/dist/shared/projection-protocol.js.map +1 -0
- package/dist/shared/sync.d.ts +24 -0
- package/dist/shared/sync.d.ts.map +1 -0
- package/dist/shared/sync.js +18 -0
- package/dist/shared/sync.js.map +1 -0
- package/dist/shared/token.d.ts +5 -0
- package/dist/shared/token.d.ts.map +1 -0
- package/dist/shared/token.js +19 -0
- package/dist/shared/token.js.map +1 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Grant Gurvis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
# @usehercules/convex
|
|
2
|
+
|
|
3
|
+
Convex component for Hercules **managed Access Control**: multi-tenant scopes,
|
|
4
|
+
roles, permissions, and resource-level grants, enforced inside your Convex
|
|
5
|
+
functions. The app reads from a local mirror that Hercules keeps in sync with
|
|
6
|
+
the control plane.
|
|
7
|
+
|
|
8
|
+
This README and the published `dist/client/index.d.ts` and
|
|
9
|
+
`dist/client/access-admin.d.ts` files are the authoritative public contract.
|
|
10
|
+
Use their TypeScript signatures and your local wrappers. Do not inspect package
|
|
11
|
+
or component implementation internals to infer behavior. Public REST payloads
|
|
12
|
+
are documented at https://docs-cloud.hercules.app.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
Call `createAccessControl` once in `convex/hercules.ts` and re-export the
|
|
17
|
+
builders. Use these builders instead of the raw `./_generated/server` ones for
|
|
18
|
+
anything permissioned.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createAccessControl } from "@usehercules/convex";
|
|
22
|
+
import { components } from "./_generated/api";
|
|
23
|
+
import { action, mutation, query } from "./_generated/server";
|
|
24
|
+
|
|
25
|
+
export const {
|
|
26
|
+
publicQuery,
|
|
27
|
+
publicMutation,
|
|
28
|
+
publicAction,
|
|
29
|
+
authenticatedQuery,
|
|
30
|
+
authenticatedMutation,
|
|
31
|
+
authenticatedAction,
|
|
32
|
+
accessQuery,
|
|
33
|
+
accessMutation,
|
|
34
|
+
accessAction,
|
|
35
|
+
hasPermission,
|
|
36
|
+
requirePermission,
|
|
37
|
+
requireAnyPermission,
|
|
38
|
+
getEffectivePermissions,
|
|
39
|
+
getCurrentHerculesAuthUserId,
|
|
40
|
+
listMyMemberships,
|
|
41
|
+
listMyRoles,
|
|
42
|
+
listScopeMembers,
|
|
43
|
+
listScopeMemberDirectory,
|
|
44
|
+
getScopeMemberDirectoryEntry,
|
|
45
|
+
listScopeRoles,
|
|
46
|
+
listScopePermissions,
|
|
47
|
+
listDirectSubjectsForResource,
|
|
48
|
+
} = createAccessControl({ query, mutation, action, components });
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
scopeFromArg,
|
|
52
|
+
scopeFromDefaultParentResource,
|
|
53
|
+
scopeFromDefaultResource,
|
|
54
|
+
scopeFromParentResource,
|
|
55
|
+
scopeFromResource,
|
|
56
|
+
} from "@usehercules/convex";
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## IAM catalog
|
|
60
|
+
|
|
61
|
+
`hercules/iam.jsonc` is the only writer for reusable permissions, reusable
|
|
62
|
+
roles, base role permissions, and `orgAdminGrantablePermissions`. Runtime APIs
|
|
63
|
+
manage members, invitations, groups, organization roles, overrides,
|
|
64
|
+
exceptions, and resource access.
|
|
65
|
+
|
|
66
|
+
```jsonc
|
|
67
|
+
{
|
|
68
|
+
"$schema": "https://schemas.hercules.app/iam/v1.json",
|
|
69
|
+
"version": "v1",
|
|
70
|
+
"permissions": {
|
|
71
|
+
"app.documents:read": { "name": "Read documents" },
|
|
72
|
+
"app.documents:update": { "name": "Update documents" },
|
|
73
|
+
"app.documents:manage_members": { "name": "Share documents" },
|
|
74
|
+
},
|
|
75
|
+
"orgAdminGrantablePermissions": [
|
|
76
|
+
"app.documents:read",
|
|
77
|
+
"app.documents:update",
|
|
78
|
+
],
|
|
79
|
+
"roles": {
|
|
80
|
+
"owner": { "type": "built_in" },
|
|
81
|
+
"admin": { "type": "built_in" },
|
|
82
|
+
"member": { "type": "built_in" },
|
|
83
|
+
"reviewer": { "type": "custom", "name": "Reviewer" },
|
|
84
|
+
},
|
|
85
|
+
"rolePermissions": {
|
|
86
|
+
"member": ["app.documents:read"],
|
|
87
|
+
"reviewer": ["app.documents:read"],
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- Permission keys are `app.<resource>:<action>`.
|
|
93
|
+
- Check concrete actions at runtime. Do not check `manage` or `*`.
|
|
94
|
+
- Declare `owner` and `admin`, but do not add app-authored base permissions to
|
|
95
|
+
them.
|
|
96
|
+
- A resource role that may share its resource needs the exact
|
|
97
|
+
`<resourceType>:manage_members` permission.
|
|
98
|
+
- A failed IAM apply means the build failed, even if TypeScript and Vite passed.
|
|
99
|
+
|
|
100
|
+
## Enforcing access
|
|
101
|
+
|
|
102
|
+
`accessQuery` / `accessMutation` / `accessAction` take a `permission` and a
|
|
103
|
+
`scope`. Choose the scope helper from the app shape:
|
|
104
|
+
|
|
105
|
+
| App shape | Create/list | Existing row | Child create |
|
|
106
|
+
| ------------------- | -------------- | -------------------------- | -------------------------------- |
|
|
107
|
+
| Default app scope | omit `scope` | `scopeFromDefaultResource` | `scopeFromDefaultParentResource` |
|
|
108
|
+
| Organization scopes | `scopeFromArg` | `scopeFromResource` | `scopeFromParentResource` |
|
|
109
|
+
|
|
110
|
+
The default-scope resource helpers load the referenced row but do not require a
|
|
111
|
+
scope id column. Organization helpers read or accept the organization scope.
|
|
112
|
+
Gate **every** protected read and write; `authenticatedQuery` only proves
|
|
113
|
+
sign-in.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { v } from "convex/values";
|
|
117
|
+
import {
|
|
118
|
+
accessQuery,
|
|
119
|
+
accessMutation,
|
|
120
|
+
scopeFromArg,
|
|
121
|
+
scopeFromResource,
|
|
122
|
+
} from "./hercules";
|
|
123
|
+
|
|
124
|
+
// Read: scope from an arg. "view" is a real permission; grant it to every role
|
|
125
|
+
// that should see the data, including a read-only role.
|
|
126
|
+
export const listProjects = accessQuery({
|
|
127
|
+
permission: "app.project:view",
|
|
128
|
+
scope: scopeFromArg("orgScopeId"),
|
|
129
|
+
args: { orgScopeId: v.string() },
|
|
130
|
+
handler: async (ctx, args) =>
|
|
131
|
+
ctx.db
|
|
132
|
+
.query("projects")
|
|
133
|
+
.withIndex("by_org", (q) => q.eq("orgScopeId", args.orgScopeId))
|
|
134
|
+
.collect(),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Write on a specific row: scope from the resource, so the caller cannot pair
|
|
138
|
+
// their scope with another org's row.
|
|
139
|
+
export const archiveProject = accessMutation({
|
|
140
|
+
permission: "app.project:archive",
|
|
141
|
+
scope: scopeFromResource("projects", "projectId"),
|
|
142
|
+
args: { projectId: v.id("projects") },
|
|
143
|
+
handler: async (ctx, args) =>
|
|
144
|
+
ctx.db.patch(args.projectId, { status: "archived" }),
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Resource-level (per-resource) permissions
|
|
149
|
+
|
|
150
|
+
`scopeFromResource` names the specific resource, so a resource grant on that
|
|
151
|
+
resource is applied on top of the scope check. `hasPermission` and
|
|
152
|
+
`getEffectivePermissions` also accept a `{ resource }` ref for per-resource UI
|
|
153
|
+
gating.
|
|
154
|
+
|
|
155
|
+
Declare trusted parent resources for child authorization. The target and
|
|
156
|
+
ancestors are evaluated together with the child permission, so any applicable
|
|
157
|
+
deny wins. Parent access applies only when its binding uses
|
|
158
|
+
`appliesTo: "self_and_descendants"`.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
export const updateTask = accessMutation({
|
|
162
|
+
permission: "app.tasks:update",
|
|
163
|
+
scope: scopeFromResource("tasks", "taskId", {
|
|
164
|
+
authorizeAgainst: (task) => [
|
|
165
|
+
{ type: "app.projects", id: String(task.projectId) },
|
|
166
|
+
],
|
|
167
|
+
}),
|
|
168
|
+
args: { taskId: v.id("tasks"), title: v.string() },
|
|
169
|
+
handler: async (ctx, args) =>
|
|
170
|
+
ctx.db.patch(args.taskId, { title: args.title }),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export const createTask = accessMutation({
|
|
174
|
+
permission: "app.tasks:create",
|
|
175
|
+
scope: scopeFromParentResource("projects", "projectId", {
|
|
176
|
+
parentResourceType: "app.projects",
|
|
177
|
+
authorizeAgainst: (project) => [
|
|
178
|
+
{ type: "app.workspaces", id: String(project.workspaceId) },
|
|
179
|
+
],
|
|
180
|
+
}),
|
|
181
|
+
args: { projectId: v.id("projects"), title: v.string() },
|
|
182
|
+
handler: async (ctx, args) => {
|
|
183
|
+
const project = await ctx.db.get(args.projectId);
|
|
184
|
+
if (!project) throw new Error("Project not found");
|
|
185
|
+
return await ctx.db.insert("tasks", {
|
|
186
|
+
orgScopeId: project.orgScopeId,
|
|
187
|
+
projectId: args.projectId,
|
|
188
|
+
title: args.title,
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Keep the requested permission on the child. The helper loads the trusted parent
|
|
195
|
+
row, adds that parent first, then appends `authorizeAgainst` ancestors. Use the
|
|
196
|
+
default-scope variants for the same recipe without an org scope column.
|
|
197
|
+
|
|
198
|
+
> **Matching note:** a self-only binding targets the permission resource type.
|
|
199
|
+
> A descendant-enabled binding targets the parent resource type while keeping
|
|
200
|
+
> the child permission key. Table names are only used to load rows. Explicit
|
|
201
|
+
> resource references must use canonical `app.*` types.
|
|
202
|
+
|
|
203
|
+
For a default-scope app, use the matching helpers without adding a persisted
|
|
204
|
+
scope id to each row:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
scope: scopeFromDefaultResource("tasks", "taskId", {
|
|
208
|
+
authorizeAgainst: (task) => [
|
|
209
|
+
{ type: "app.projects", id: String(task.projectId) },
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
scope: scopeFromDefaultParentResource("projects", "projectId", {
|
|
214
|
+
parentResourceType: "app.projects",
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Identity and app relationships
|
|
219
|
+
|
|
220
|
+
Use `getCurrentHerculesAuthUserId` for the stable Hercules Auth user id. It
|
|
221
|
+
returns the verified OIDC `sub`; never parse `tokenIdentifier`.
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
export const getMyProfile = accessQuery({
|
|
225
|
+
permission: "app.profiles:read",
|
|
226
|
+
args: {},
|
|
227
|
+
handler: async (ctx) => {
|
|
228
|
+
const herculesAuthUserId = await getCurrentHerculesAuthUserId(ctx);
|
|
229
|
+
if (!herculesAuthUserId) throw new Error("Authentication required");
|
|
230
|
+
return await ctx.db
|
|
231
|
+
.query("profiles")
|
|
232
|
+
.withIndex("by_auth_user", (q) =>
|
|
233
|
+
q.eq("herculesAuthUserId", herculesAuthUserId),
|
|
234
|
+
)
|
|
235
|
+
.unique();
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Application tables own product relationships such as owner, assignee,
|
|
241
|
+
attending user, or linked profile. Access Control owns capabilities. Enforce
|
|
242
|
+
both:
|
|
243
|
+
|
|
244
|
+
1. Gate the function with a concrete Access Control permission.
|
|
245
|
+
2. Load the trusted application row.
|
|
246
|
+
3. Apply any relationship-based row or field restriction.
|
|
247
|
+
|
|
248
|
+
A relationship may narrow an authorized result; it must not grant a capability
|
|
249
|
+
by itself. Use a managed resource grant when the relationship should confer
|
|
250
|
+
additional access.
|
|
251
|
+
|
|
252
|
+
## In-app admin screens
|
|
253
|
+
|
|
254
|
+
Read the scope's members, roles, and catalog with the `listScope*` helpers.
|
|
255
|
+
Each self-gates on the matching `system.*:read` permission and returns `[]`
|
|
256
|
+
when the caller lacks it (`owner`/`admin` hold these automatically).
|
|
257
|
+
|
|
258
|
+
For member-facing pickers, use `listScopeMemberDirectory`. It is gated by
|
|
259
|
+
`app.members:read` and returns bounded pages of active users with only their
|
|
260
|
+
principal id, Hercules Auth user id, name, email, optional image, and
|
|
261
|
+
authoritative effective scope-role keys.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
export const teamMembers = authenticatedQuery({
|
|
265
|
+
args: { scopeId: v.string() },
|
|
266
|
+
handler: async (ctx, args) => {
|
|
267
|
+
const page = await listScopeMemberDirectory(ctx, { scopeId: args.scopeId, limit: 50 });
|
|
268
|
+
return {
|
|
269
|
+
...page,
|
|
270
|
+
members: page.members.filter((member) => member.roleKeys.includes("reviewer")),
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`roleKeys` comes from managed scope-role bindings, including active group
|
|
277
|
+
membership. Do not copy it into an app-owned role table.
|
|
278
|
+
|
|
279
|
+
### Authority matrix
|
|
280
|
+
|
|
281
|
+
| Surface | Convex exposure | Authority |
|
|
282
|
+
| -------------------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------- |
|
|
283
|
+
| `createAccessAdminActions`, `createAccessInvitation`, `createResourceInvitation` | Internal only | Service via `HERCULES_API_KEY` |
|
|
284
|
+
| `createAccessUserActions` | Public `authenticatedAction` | Signed-in app user via `idToken`; the control plane applies runtime role checks |
|
|
285
|
+
| `createAccessScopeAction` | Public `authenticatedAction` | Authenticated creator after `canCreateScope`; the creator becomes Owner |
|
|
286
|
+
| `createAccessScope` | App-owned authenticated function | Authenticated creator from `ctx`; the app supplies its own product-policy gate |
|
|
287
|
+
| `createResourceCreatorBootstrapAction` | Public `authenticatedAction` | Trusted resource creator; one fixed initial resource role |
|
|
288
|
+
| `acceptAccessInvitation` | App-owned authenticated function | Invitee identified by the invitation token and `idToken` |
|
|
289
|
+
|
|
290
|
+
Never call generated `internal.accessAdmin.*` actions from an exported public,
|
|
291
|
+
authenticated, or access builder, directly or through a helper. Service
|
|
292
|
+
authority is only for trusted internal workflows.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
"use node";
|
|
296
|
+
import { createAccessAdminActions } from "@usehercules/convex/access-admin";
|
|
297
|
+
import { internalAction } from "./_generated/server";
|
|
298
|
+
|
|
299
|
+
export const { assignRole, removeRole, createInvitation } =
|
|
300
|
+
createAccessAdminActions({ internalAction });
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Use `createAccessUserActions` for user-initiated administration.
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
"use node";
|
|
307
|
+
import { createAccessUserActions } from "@usehercules/convex/access-admin";
|
|
308
|
+
import { authenticatedAction } from "./hercules";
|
|
309
|
+
|
|
310
|
+
export const {
|
|
311
|
+
assignRole,
|
|
312
|
+
replaceMemberRoles,
|
|
313
|
+
listGrantableRoles,
|
|
314
|
+
createResourceGrant,
|
|
315
|
+
replaceResourceGrants,
|
|
316
|
+
revokeResourceGrant,
|
|
317
|
+
setResourcePermissionRules,
|
|
318
|
+
listResourceInvitations,
|
|
319
|
+
revokeInvitation,
|
|
320
|
+
} = createAccessUserActions({ authenticatedAction });
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
`idToken` authenticates the actor only. In trusted Convex code, load the
|
|
324
|
+
resource row to derive its scope and resource id, use its canonical `app.*`
|
|
325
|
+
resource type, and resolve a selected `herculesAuthUserId` with
|
|
326
|
+
`getScopeMemberDirectoryEntry`; pass the returned `principalId` as the
|
|
327
|
+
recipient. Do not trust a browser-supplied principal or scope/resource pair.
|
|
328
|
+
|
|
329
|
+
Use `listGrantableRoles` for role pickers. It returns only roles the current
|
|
330
|
+
actor may confer at the exact scope or resource target. Set `subjectType` to
|
|
331
|
+
match the intended user or group recipient:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
await ctx.runAction(api.accessUser.listGrantableRoles, {
|
|
335
|
+
scopeId,
|
|
336
|
+
subjectType: "user",
|
|
337
|
+
target: {
|
|
338
|
+
type: "resource",
|
|
339
|
+
resourceType: "app.documents",
|
|
340
|
+
resourceId: String(documentId),
|
|
341
|
+
appliesTo: "self",
|
|
342
|
+
},
|
|
343
|
+
idToken,
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Do not use `listScopeRoles` for a write picker. It is the complete mirrored
|
|
348
|
+
catalog for administrators and may include roles the current actor cannot
|
|
349
|
+
assign at that target. The write still reauthorizes after a picker result.
|
|
350
|
+
|
|
351
|
+
Common public action calls:
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
await ctx.runAction(api.accessUser.replaceMemberRoles, {
|
|
355
|
+
scopeId,
|
|
356
|
+
herculesAuthUserId,
|
|
357
|
+
roleKeys: ["reviewer"],
|
|
358
|
+
idToken,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
await ctx.runAction(api.accessUser.createInvitation, {
|
|
362
|
+
scopeId,
|
|
363
|
+
email,
|
|
364
|
+
roleKeys: ["member"],
|
|
365
|
+
idToken,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
await ctx.runAction(api.accessUser.replaceResourceGrants, {
|
|
369
|
+
scopeId,
|
|
370
|
+
resourceType: "app.documents",
|
|
371
|
+
resourceId: String(documentId),
|
|
372
|
+
subjects: [{ principalId, grants: [{ roleKey: "reviewer" }] }],
|
|
373
|
+
idToken,
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Give a resource creator its initial manager role
|
|
378
|
+
|
|
379
|
+
When creating a resource should make its creator the resource manager, use
|
|
380
|
+
`createResourceCreatorBootstrapAction`. It is deliberately narrower than a
|
|
381
|
+
normal grant action:
|
|
382
|
+
|
|
383
|
+
- the browser passes only the resource id;
|
|
384
|
+
- trusted app data supplies the creator and scope;
|
|
385
|
+
- the resource type, role, and descendant behavior are fixed in code;
|
|
386
|
+
- the caller must still be an active member of the scope; and
|
|
387
|
+
- an active resource is never bootstrapped again, so revoked access is not
|
|
388
|
+
silently restored.
|
|
389
|
+
|
|
390
|
+
Create the app row as `provisioning`, record its creator with
|
|
391
|
+
`getCurrentHerculesAuthUserId`, and exclude provisioning rows from normal
|
|
392
|
+
queries. Then expose one action:
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
// convex/accessUser.ts
|
|
396
|
+
"use node";
|
|
397
|
+
|
|
398
|
+
import { createResourceCreatorBootstrapAction } from "@usehercules/convex/access-admin";
|
|
399
|
+
import type { Id } from "./_generated/dataModel";
|
|
400
|
+
import { internal } from "./_generated/api";
|
|
401
|
+
import { authenticatedAction, listMyMemberships } from "./hercules";
|
|
402
|
+
|
|
403
|
+
export const bootstrapProjectCreator =
|
|
404
|
+
createResourceCreatorBootstrapAction({
|
|
405
|
+
authenticatedAction,
|
|
406
|
+
resourceType: "app.projects",
|
|
407
|
+
managerRoleKey: "project_manager",
|
|
408
|
+
appliesTo: "self_and_descendants",
|
|
409
|
+
listMyMemberships,
|
|
410
|
+
getBootstrapTarget: async (ctx, { resourceId }) =>
|
|
411
|
+
await ctx.runQuery(internal.projects.getCreatorBootstrapTarget, {
|
|
412
|
+
projectId: resourceId as Id<"projects">,
|
|
413
|
+
}),
|
|
414
|
+
activateResource: async (
|
|
415
|
+
ctx,
|
|
416
|
+
{ resourceId, creatorHerculesAuthUserId },
|
|
417
|
+
) => {
|
|
418
|
+
await ctx.runMutation(internal.projects.activateCreatorBootstrap, {
|
|
419
|
+
projectId: resourceId as Id<"projects">,
|
|
420
|
+
creatorHerculesAuthUserId,
|
|
421
|
+
});
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The internal query returns only `{ scopeId, resourceId,
|
|
427
|
+
creatorHerculesAuthUserId, state }`. The activation mutation must re-read the
|
|
428
|
+
row and require the same creator plus `state === "provisioning"` before
|
|
429
|
+
changing it to `active`. If activation fails after the grant, retry the action;
|
|
430
|
+
the managed grant write is idempotent.
|
|
431
|
+
|
|
432
|
+
Do not accept a browser-supplied scope, role, resource type, or recipient, and
|
|
433
|
+
do not expose a raw service-authority action. If every scope Admin should
|
|
434
|
+
already manage every resource, skip creator bootstrap entirely: the built-in
|
|
435
|
+
Admin role already covers ordinary `app.*` permissions.
|
|
436
|
+
|
|
437
|
+
For a browser-selected user, resolve active scope membership before writing:
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
const recipient = await getScopeMemberDirectoryEntry(ctx, {
|
|
441
|
+
scopeId,
|
|
442
|
+
herculesAuthUserId,
|
|
443
|
+
});
|
|
444
|
+
if (!recipient) throw new Error("Active member not found");
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Grant `app.members:read` to roles that may use a member-facing directory.
|
|
448
|
+
Access-administration screens should use `listScopeMembers`, which requires
|
|
449
|
+
`system.members:read`.
|
|
450
|
+
|
|
451
|
+
Use `replaceMemberRoles` to atomically replace up to 500 direct scope roles for
|
|
452
|
+
one member.
|
|
453
|
+
Use `replaceResourceGrants` to atomically replace direct grants for each listed
|
|
454
|
+
subject; `grants: []` clears that subject. Each request must include 1-100
|
|
455
|
+
subjects and may involve at most 500 distinct existing or desired grants. Split
|
|
456
|
+
larger edits by subjects, never by one subject's complete grant set.
|
|
457
|
+
`createResourceGrant` requires one exact `resourceId`. For one grant, use its
|
|
458
|
+
returned `grantId` or
|
|
459
|
+
`listDirectSubjectsForResource`, then call `revokeResourceGrant`. Use
|
|
460
|
+
`listResourceInvitations` and `revokeInvitation` for pending resource
|
|
461
|
+
invitations. `setResourcePermissionRules` atomically applies a rule batch;
|
|
462
|
+
`effect: "clear"` removes a listed rule.
|
|
463
|
+
|
|
464
|
+
Create organization scopes with `createAccessScopeAction` or
|
|
465
|
+
`createAccessScope`. The authenticated creator is sent as the scope Owner
|
|
466
|
+
automatically; do not create a second self-grant.
|
|
467
|
+
|
|
468
|
+
## Notes
|
|
469
|
+
|
|
470
|
+
- Reads come from the local mirror, which lags the control plane by a short
|
|
471
|
+
projection-sync window after any change. Treat `undefined` query results and
|
|
472
|
+
a just-changed-but-not-yet-synced state as "loading", not "denied".
|
|
473
|
+
- Run `hercules-convex-access-check convex` (the `./checker` export) in lint to
|
|
474
|
+
catch deterministic source patterns. It is static and does not prove runtime
|
|
475
|
+
role decisions or control-plane writes are authorized.
|
|
476
|
+
- Before claiming completion, exercise at least one intended allow, one denied
|
|
477
|
+
operation, one cross-row or cross-scope isolation case, and each sensitive
|
|
478
|
+
field-redaction path with real non-Owner identities.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated `ComponentApi` utility.
|
|
3
|
+
*
|
|
4
|
+
* THIS CODE MATCHES THE CONVEX-GENERATED COMPONENT API SHAPE.
|
|
5
|
+
*
|
|
6
|
+
* To regenerate, run `npx convex dev`.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { FunctionReference } from "convex/server";
|
|
10
|
+
import type { AccessProjectionSyncPayload, ScopeKind, SyncResponse } from "../shared/sync";
|
|
11
|
+
type AuthorizationArgs = {
|
|
12
|
+
tokenIdentifier?: string;
|
|
13
|
+
scopeId?: string;
|
|
14
|
+
permission?: string;
|
|
15
|
+
resourceType?: string;
|
|
16
|
+
resourceId?: string;
|
|
17
|
+
ancestors?: AuthorizationAncestor[];
|
|
18
|
+
};
|
|
19
|
+
type AuthorizationAncestor = {
|
|
20
|
+
resourceType: string;
|
|
21
|
+
resourceId: string;
|
|
22
|
+
};
|
|
23
|
+
type AuthorizationDecision = {
|
|
24
|
+
allowed: boolean;
|
|
25
|
+
reasonCode: string;
|
|
26
|
+
explicitDeny: boolean;
|
|
27
|
+
sourceVersion?: number;
|
|
28
|
+
principalId?: string;
|
|
29
|
+
effectiveRoleIds: string[];
|
|
30
|
+
};
|
|
31
|
+
type ListMyMembershipsArgs = {
|
|
32
|
+
tokenIdentifier?: string;
|
|
33
|
+
};
|
|
34
|
+
type GetDeploymentEntryStatusArgs = {
|
|
35
|
+
tokenIdentifier?: string;
|
|
36
|
+
};
|
|
37
|
+
type ListMyRolesArgs = {
|
|
38
|
+
tokenIdentifier?: string;
|
|
39
|
+
scopeId: string;
|
|
40
|
+
};
|
|
41
|
+
type GetEffectivePermissionsArgs = {
|
|
42
|
+
tokenIdentifier?: string;
|
|
43
|
+
scopeId: string;
|
|
44
|
+
resourceType?: string;
|
|
45
|
+
resourceId?: string;
|
|
46
|
+
ancestors?: AuthorizationAncestor[];
|
|
47
|
+
};
|
|
48
|
+
type ListScopeArgs = {
|
|
49
|
+
tokenIdentifier?: string;
|
|
50
|
+
scopeId: string;
|
|
51
|
+
};
|
|
52
|
+
type ListScopeMemberDirectoryArgs = ListScopeArgs & {
|
|
53
|
+
cursor?: string;
|
|
54
|
+
limit?: number;
|
|
55
|
+
};
|
|
56
|
+
type ListDirectSubjectsArgs = {
|
|
57
|
+
tokenIdentifier?: string;
|
|
58
|
+
scopeId: string;
|
|
59
|
+
resourceType: string;
|
|
60
|
+
resourceId: string;
|
|
61
|
+
permission: string;
|
|
62
|
+
};
|
|
63
|
+
type RoleSummary = {
|
|
64
|
+
roleId: string;
|
|
65
|
+
roleKey: string;
|
|
66
|
+
roleName: string;
|
|
67
|
+
roleKind: "system" | "custom";
|
|
68
|
+
};
|
|
69
|
+
type ScopeMember = {
|
|
70
|
+
principalId: string;
|
|
71
|
+
type: "user" | "group";
|
|
72
|
+
herculesAuthUserId?: string;
|
|
73
|
+
status: "active" | "blocked" | "suspended" | "pending_approval" | "removed";
|
|
74
|
+
joinedAt: number;
|
|
75
|
+
name?: string;
|
|
76
|
+
email?: string;
|
|
77
|
+
image?: string;
|
|
78
|
+
roles: RoleSummary[];
|
|
79
|
+
};
|
|
80
|
+
type ScopeMemberDirectoryEntry = {
|
|
81
|
+
principalId: string;
|
|
82
|
+
herculesAuthUserId: string;
|
|
83
|
+
name: string;
|
|
84
|
+
email: string;
|
|
85
|
+
image?: string;
|
|
86
|
+
roleKeys: string[];
|
|
87
|
+
};
|
|
88
|
+
type ScopeMemberDirectoryPage = {
|
|
89
|
+
members: ScopeMemberDirectoryEntry[];
|
|
90
|
+
cursor?: string;
|
|
91
|
+
};
|
|
92
|
+
type ScopeRoleSummary = RoleSummary & {
|
|
93
|
+
shared: boolean;
|
|
94
|
+
};
|
|
95
|
+
type ScopePermissionSummary = {
|
|
96
|
+
permissionId: string;
|
|
97
|
+
key: string;
|
|
98
|
+
resourceType: string;
|
|
99
|
+
action: string;
|
|
100
|
+
classification: "delegable" | "owner_only";
|
|
101
|
+
tenantAssignable: boolean;
|
|
102
|
+
};
|
|
103
|
+
type DirectResourceSubject = {
|
|
104
|
+
grantId: string;
|
|
105
|
+
principalId: string;
|
|
106
|
+
type: "user" | "group";
|
|
107
|
+
herculesAuthUserId?: string;
|
|
108
|
+
status: "active" | "blocked" | "suspended" | "pending_approval" | "removed";
|
|
109
|
+
name?: string;
|
|
110
|
+
email?: string;
|
|
111
|
+
image?: string;
|
|
112
|
+
effect: "allow" | "deny";
|
|
113
|
+
appliesTo: "self" | "self_and_descendants";
|
|
114
|
+
expiresAt?: number;
|
|
115
|
+
roleId?: string;
|
|
116
|
+
roleKey?: string;
|
|
117
|
+
roleName?: string;
|
|
118
|
+
permissionId?: string;
|
|
119
|
+
permissionKey?: string;
|
|
120
|
+
};
|
|
121
|
+
type Membership = {
|
|
122
|
+
scopeId: string;
|
|
123
|
+
scopeName: string;
|
|
124
|
+
kind: ScopeKind;
|
|
125
|
+
roles: RoleSummary[];
|
|
126
|
+
joinedAt: number;
|
|
127
|
+
status: "active" | "blocked" | "suspended" | "pending_approval" | "removed";
|
|
128
|
+
};
|
|
129
|
+
type DeploymentEntryStatus = {
|
|
130
|
+
kind: "principal";
|
|
131
|
+
principalId: string;
|
|
132
|
+
status: "active" | "blocked" | "suspended" | "pending_approval" | "removed";
|
|
133
|
+
stateVersion: number;
|
|
134
|
+
} | {
|
|
135
|
+
kind: "fallback";
|
|
136
|
+
reason: "identity_missing" | "identity_invalid" | "unexpected_issuer" | "mirror_not_ready" | "default_scope_missing" | "principal_missing";
|
|
137
|
+
stateVersion?: number;
|
|
138
|
+
};
|
|
139
|
+
type EffectivePermissionsResult = {
|
|
140
|
+
allowed: boolean;
|
|
141
|
+
reasonCode: string;
|
|
142
|
+
sourceVersion?: number;
|
|
143
|
+
scopeId?: string;
|
|
144
|
+
principalId?: string;
|
|
145
|
+
effectiveRoleIds: string[];
|
|
146
|
+
wildcard: "none" | "immutable" | "default";
|
|
147
|
+
permissions: string[];
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* A utility for referencing the Hercules Access Control component's exposed API.
|
|
151
|
+
*
|
|
152
|
+
* Useful when expecting a parameter like `components.hercules`.
|
|
153
|
+
*/
|
|
154
|
+
export type ComponentApi<Name extends string | undefined = string | undefined> = {
|
|
155
|
+
checks: {
|
|
156
|
+
authorize: FunctionReference<"query", "public", AuthorizationArgs, AuthorizationDecision, Name>;
|
|
157
|
+
authorizeMany: FunctionReference<"query", "public", {
|
|
158
|
+
tokenIdentifier?: string;
|
|
159
|
+
checks: Array<Omit<AuthorizationArgs, "tokenIdentifier"> & {
|
|
160
|
+
permission: string;
|
|
161
|
+
}>;
|
|
162
|
+
}, AuthorizationDecision[], Name>;
|
|
163
|
+
};
|
|
164
|
+
queries: {
|
|
165
|
+
getDeploymentEntryStatus: FunctionReference<"query", "public", GetDeploymentEntryStatusArgs, DeploymentEntryStatus, Name>;
|
|
166
|
+
listMyMemberships: FunctionReference<"query", "public", ListMyMembershipsArgs, Membership[], Name>;
|
|
167
|
+
listMyRoles: FunctionReference<"query", "public", ListMyRolesArgs, RoleSummary[], Name>;
|
|
168
|
+
getEffectivePermissions: FunctionReference<"query", "public", GetEffectivePermissionsArgs, EffectivePermissionsResult, Name>;
|
|
169
|
+
listScopeMemberDirectory: FunctionReference<"query", "public", ListScopeMemberDirectoryArgs, ScopeMemberDirectoryPage, Name>;
|
|
170
|
+
getScopeMemberDirectoryEntry: FunctionReference<"query", "public", ListScopeArgs & {
|
|
171
|
+
principalId?: string;
|
|
172
|
+
herculesAuthUserId?: string;
|
|
173
|
+
}, ScopeMemberDirectoryEntry | null, Name>;
|
|
174
|
+
listScopeMembers: FunctionReference<"query", "public", ListScopeArgs, ScopeMember[], Name>;
|
|
175
|
+
listScopeRoles: FunctionReference<"query", "public", ListScopeArgs, ScopeRoleSummary[], Name>;
|
|
176
|
+
listScopePermissions: FunctionReference<"query", "public", ListScopeArgs, ScopePermissionSummary[], Name>;
|
|
177
|
+
listDirectSubjectsForResource: FunctionReference<"query", "public", ListDirectSubjectsArgs, DirectResourceSubject[], Name>;
|
|
178
|
+
};
|
|
179
|
+
sync: {
|
|
180
|
+
applySync: FunctionReference<"mutation", "public", AccessProjectionSyncPayload, SyncResponse, Name>;
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
export {};
|
|
184
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/_generated/component.ts"],"names":[],"mappings":"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"}
|