@cfast/permissions 0.0.1 → 0.1.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/dist/index.d.ts +28 -1
- package/dist/index.js +10 -0
- package/llms.txt +192 -0
- package/package.json +18 -10
- package/LICENSE +0 -21
- package/dist/chunk-FVAAEIAE.js +0 -32
- package/dist/chunk-IPYPD2CZ.js +0 -53
- package/dist/chunk-PNHVTXCZ.js +0 -39
- package/dist/chunk-YYYHMPTS.js +0 -33
- package/dist/chunk-ZTQJZJFW.js +0 -38
- package/dist/client-8HHp1rPO.d.ts +0 -62
- package/dist/client-BBcryLDZ.d.ts +0 -61
- package/dist/client-BKD8jH5P.d.ts +0 -56
- package/dist/client-ChpyEV1r.d.ts +0 -62
- package/dist/client-CuB6igvw.d.ts +0 -195
- package/dist/client-DduKFZmk.d.ts +0 -59
- package/dist/client-FbortuK3.d.ts +0 -53
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,33 @@ declare function grant(action: PermissionAction, subject: DrizzleTable | "all",
|
|
|
72
72
|
where?: WhereClause;
|
|
73
73
|
}): Grant;
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Checks whether a set of resolved grants permits a specific action on a table.
|
|
77
|
+
*
|
|
78
|
+
* Unlike {@link checkPermissions} (which takes a role string and the full permissions
|
|
79
|
+
* object), `can` works directly with the user's resolved grants — the same grants
|
|
80
|
+
* available in loaders, actions, and client-side via `useActions`.
|
|
81
|
+
*
|
|
82
|
+
* @param grants - The user's resolved permission grants.
|
|
83
|
+
* @param action - The permission action to check (e.g., `"create"`, `"read"`).
|
|
84
|
+
* @param table - The Drizzle table object to check against.
|
|
85
|
+
* @returns `true` if any grant permits the action on the table.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { can } from "@cfast/permissions";
|
|
90
|
+
*
|
|
91
|
+
* // In a loader
|
|
92
|
+
* if (!can(ctx.auth.grants, "create", posts)) {
|
|
93
|
+
* throw redirect("/");
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* // In a component
|
|
97
|
+
* {can(grants, "update", posts) && <Button>Edit</Button>}
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function can(grants: Grant[], action: PermissionAction, table: DrizzleTable): boolean;
|
|
101
|
+
|
|
75
102
|
/**
|
|
76
103
|
* Checks whether a role satisfies a set of permission descriptors.
|
|
77
104
|
*
|
|
@@ -123,4 +150,4 @@ declare function checkPermissions(role: string, permissions: Permissions, descri
|
|
|
123
150
|
*/
|
|
124
151
|
declare function resolveGrants(permissions: Permissions, roles: string[]): Grant[];
|
|
125
152
|
|
|
126
|
-
export { DrizzleTable, Grant, PermissionAction, PermissionCheckResult, PermissionDescriptor, Permissions, PermissionsConfig, WhereClause, checkPermissions, definePermissions, grant, resolveGrants };
|
|
153
|
+
export { DrizzleTable, Grant, PermissionAction, PermissionCheckResult, PermissionDescriptor, Permissions, PermissionsConfig, WhereClause, can, checkPermissions, definePermissions, grant, resolveGrants };
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,15 @@ function resolveHierarchy(roles, grants, hierarchy) {
|
|
|
58
58
|
return resolved;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// src/can.ts
|
|
62
|
+
function can(grants, action, table) {
|
|
63
|
+
return grants.some((g) => {
|
|
64
|
+
const actionOk = g.action === action || g.action === "manage";
|
|
65
|
+
const subjectOk = g.subject === "all" || typeof g.subject === "object" && getTableName(g.subject) === getTableName(table);
|
|
66
|
+
return actionOk && subjectOk;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
61
70
|
// src/check.ts
|
|
62
71
|
function grantMatches(g, action, table) {
|
|
63
72
|
const actionOk = g.action === action || g.action === "manage";
|
|
@@ -162,6 +171,7 @@ function resolveGrants(permissions, roles) {
|
|
|
162
171
|
export {
|
|
163
172
|
CRUD_ACTIONS,
|
|
164
173
|
ForbiddenError,
|
|
174
|
+
can,
|
|
165
175
|
checkPermissions,
|
|
166
176
|
definePermissions,
|
|
167
177
|
getTableName,
|
package/llms.txt
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# @cfast/permissions
|
|
2
|
+
|
|
3
|
+
> Isomorphic, Drizzle-native permission system. Define permissions once, enforce as SQL WHERE clauses.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
Use this package to define role-based permissions that compile down to Drizzle `where` clauses for Cloudflare D1. Permissions defined here are consumed by `@cfast/db` for server enforcement and `@cfast/actions` for client-side UI introspection.
|
|
7
|
+
|
|
8
|
+
## Key concepts
|
|
9
|
+
- **Permissions are WHERE clauses**: A read permission becomes a SQL filter. You don't write a check then repeat the logic in your query.
|
|
10
|
+
- **Two layers**: (1) Structural -- "does this role have any grant for update on posts?" (2) Row-level -- "does the grant's WHERE clause include this specific row?" Structural checks are cheap and work client-side. Row-level checks happen at execution time.
|
|
11
|
+
- **`"manage"` = all CRUD**: A shorthand for granting `read`, `create`, `update`, `delete`.
|
|
12
|
+
- **`"all"` = every table**: `grant("manage", "all")` grants full access to everything.
|
|
13
|
+
- **Role hierarchy**: Roles can inherit from parent roles. Multiple WHERE clauses on the same action+table are OR'd (more permissive grant wins).
|
|
14
|
+
|
|
15
|
+
## API Reference
|
|
16
|
+
|
|
17
|
+
### `definePermissions(config): Permissions`
|
|
18
|
+
```typescript
|
|
19
|
+
import { definePermissions, grant } from "@cfast/permissions";
|
|
20
|
+
|
|
21
|
+
const permissions = definePermissions({
|
|
22
|
+
roles: ["anonymous", "user", "admin"] as const,
|
|
23
|
+
grants: {
|
|
24
|
+
anonymous: [ grant("read", posts, { where: (post) => eq(post.published, true) }) ],
|
|
25
|
+
user: [ grant("read", posts), grant("create", posts) ],
|
|
26
|
+
admin: [ grant("manage", "all") ],
|
|
27
|
+
},
|
|
28
|
+
hierarchy: { // optional
|
|
29
|
+
user: ["anonymous"], // user inherits anonymous grants
|
|
30
|
+
admin: ["user"], // admin inherits user grants
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Curried form** for typed user in WHERE clauses:
|
|
36
|
+
```typescript
|
|
37
|
+
const permissions = definePermissions<MyUser>()({
|
|
38
|
+
roles: [...] as const,
|
|
39
|
+
grants: (grant) => ({
|
|
40
|
+
user: [ grant("update", posts, { where: (post, user) => eq(post.authorId, user.id) }) ],
|
|
41
|
+
// ...
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Returns** `Permissions<TRoles>`:
|
|
47
|
+
```typescript
|
|
48
|
+
type Permissions<TRoles> = {
|
|
49
|
+
roles: TRoles;
|
|
50
|
+
grants: Record<TRoles[number], Grant[]>;
|
|
51
|
+
resolvedGrants: Record<TRoles[number], Grant[]>; // hierarchy-expanded
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `grant(action, subject, options?): Grant`
|
|
56
|
+
```typescript
|
|
57
|
+
grant(action: PermissionAction, subject: DrizzleTable | "all", options?: { where?: WhereClause }): Grant
|
|
58
|
+
```
|
|
59
|
+
- `PermissionAction`: `"read" | "create" | "update" | "delete" | "manage"`
|
|
60
|
+
- `WhereClause`: `(columns, user) => DrizzleSQL | undefined`
|
|
61
|
+
|
|
62
|
+
### `checkPermissions(role, permissions, descriptors): PermissionCheckResult`
|
|
63
|
+
```typescript
|
|
64
|
+
import { checkPermissions } from "@cfast/permissions";
|
|
65
|
+
|
|
66
|
+
const result = checkPermissions("user", permissions, [
|
|
67
|
+
{ action: "update", table: posts },
|
|
68
|
+
]);
|
|
69
|
+
result.permitted; // boolean
|
|
70
|
+
result.denied; // PermissionDescriptor[]
|
|
71
|
+
result.reasons; // string[]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `resolveGrants(permissions, roles): Grant[]`
|
|
75
|
+
Merges grants from multiple roles into a flat array. Deduplicates by action+subject and OR-merges WHERE clauses.
|
|
76
|
+
|
|
77
|
+
### `ForbiddenError`
|
|
78
|
+
```typescript
|
|
79
|
+
import { ForbiddenError } from "@cfast/permissions";
|
|
80
|
+
|
|
81
|
+
class ForbiddenError extends Error {
|
|
82
|
+
readonly action: PermissionAction;
|
|
83
|
+
readonly table: DrizzleTable;
|
|
84
|
+
readonly role: string | undefined;
|
|
85
|
+
readonly descriptors: PermissionDescriptor[];
|
|
86
|
+
toJSON(): { name, message, action, table, role };
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `can(grants, action, table): boolean`
|
|
91
|
+
```typescript
|
|
92
|
+
import { can } from "@cfast/permissions";
|
|
93
|
+
function can(grants: Grant[], action: PermissionAction, table: DrizzleTable): boolean
|
|
94
|
+
```
|
|
95
|
+
Quick grant-level permission check that works directly with resolved grants. Unlike `checkPermissions` (which takes a role string + full permissions object), `can` works with the user's resolved grants -- the same grants available in loaders, actions, and client-side. Returns `true` if any grant permits the action on the table (including `manage`/`all` wildcards).
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
can(grants, "create", posts) // → boolean
|
|
99
|
+
can(grants, "delete", comments) // → boolean
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### `CRUD_ACTIONS`
|
|
103
|
+
```typescript
|
|
104
|
+
const CRUD_ACTIONS: readonly CrudAction[] = ["read", "create", "update", "delete"];
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Client entrypoint
|
|
108
|
+
```typescript
|
|
109
|
+
import { ForbiddenError, can } from "@cfast/permissions/client";
|
|
110
|
+
import type { PermissionAction, CrudAction, PermissionDescriptor, PermissionCheckResult } from "@cfast/permissions/client";
|
|
111
|
+
```
|
|
112
|
+
Use this in client bundles to avoid importing server-only code. `can` is available from both the main and client entrypoints.
|
|
113
|
+
|
|
114
|
+
## Usage Examples
|
|
115
|
+
|
|
116
|
+
### Ownership-based permissions with hierarchy
|
|
117
|
+
```typescript
|
|
118
|
+
import { definePermissions, grant } from "@cfast/permissions";
|
|
119
|
+
import { eq } from "drizzle-orm";
|
|
120
|
+
import { posts, comments } from "./schema";
|
|
121
|
+
|
|
122
|
+
export const permissions = definePermissions({
|
|
123
|
+
roles: ["anonymous", "user", "editor", "admin"] as const,
|
|
124
|
+
hierarchy: {
|
|
125
|
+
user: ["anonymous"],
|
|
126
|
+
editor: ["user"],
|
|
127
|
+
admin: ["editor"],
|
|
128
|
+
},
|
|
129
|
+
grants: {
|
|
130
|
+
anonymous: [
|
|
131
|
+
grant("read", posts, { where: (p) => eq(p.published, true) }),
|
|
132
|
+
],
|
|
133
|
+
user: [
|
|
134
|
+
grant("create", posts),
|
|
135
|
+
grant("update", posts, { where: (p, u) => eq(p.authorId, u.id) }),
|
|
136
|
+
grant("delete", posts, { where: (p, u) => eq(p.authorId, u.id) }),
|
|
137
|
+
],
|
|
138
|
+
editor: [
|
|
139
|
+
grant("read", posts), // unrestricted overrides inherited "published only"
|
|
140
|
+
grant("update", posts),
|
|
141
|
+
grant("delete", posts),
|
|
142
|
+
],
|
|
143
|
+
admin: [
|
|
144
|
+
grant("manage", "all"),
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Quick permission check with `can()`
|
|
151
|
+
```typescript
|
|
152
|
+
import { can } from "@cfast/permissions";
|
|
153
|
+
|
|
154
|
+
// In a loader/action -- grants come from auth.requireUser()
|
|
155
|
+
const { grants } = await auth.requireUser(request);
|
|
156
|
+
if (can(grants, "update", posts)) {
|
|
157
|
+
// user has structural permission to update posts
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// In a React component -- grants come from loader data
|
|
161
|
+
function PostCard({ post, grants }) {
|
|
162
|
+
return (
|
|
163
|
+
<div>
|
|
164
|
+
<h2>{post.title}</h2>
|
|
165
|
+
{can(grants, "update", posts) && <Button>Edit</Button>}
|
|
166
|
+
{can(grants, "delete", posts) && <Button color="danger">Delete</Button>}
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Checking permissions manually
|
|
173
|
+
```typescript
|
|
174
|
+
const result = checkPermissions("user", permissions, [
|
|
175
|
+
{ action: "delete", table: posts },
|
|
176
|
+
]);
|
|
177
|
+
if (!result.permitted) {
|
|
178
|
+
console.log(result.reasons); // ["Role 'user' cannot delete on 'posts'"] -- if no grant exists
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Integration
|
|
183
|
+
- **`@cfast/db`**: Pass `permissions` to `createDb()`. Operations auto-enforce permissions.
|
|
184
|
+
- **`@cfast/actions`**: Actions extract `PermissionDescriptor[]` from operations for client-side `permitted` booleans.
|
|
185
|
+
- **`@cfast/admin`**: Admin CRUD goes through the same permission system.
|
|
186
|
+
|
|
187
|
+
## Common Mistakes
|
|
188
|
+
- **Forgetting `as const` on roles array** -- without it, TypeScript infers `string[]` and you lose type checking on grant keys.
|
|
189
|
+
- **Expecting WHERE on `"create"` grants** -- create is boolean (can/can't). There's no existing row to filter.
|
|
190
|
+
- **Circular hierarchy** -- detected at runtime and throws `Error`. E.g., A inherits B inherits A.
|
|
191
|
+
- **Using `checkPermissions` for row-level security** -- it only does structural checks (action + table). Row-level WHERE enforcement happens at execution time in `@cfast/db`.
|
|
192
|
+
- **Importing `definePermissions` in client bundles** -- use `@cfast/permissions/client` instead to keep the bundle small.
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cfast/permissions",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Isomorphic, composable permission system with Drizzle-native row-level access control",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cfast",
|
|
7
|
+
"cloudflare-workers",
|
|
8
|
+
"permissions",
|
|
9
|
+
"rbac",
|
|
10
|
+
"access-control"
|
|
11
|
+
],
|
|
5
12
|
"license": "MIT",
|
|
6
13
|
"repository": {
|
|
7
14
|
"type": "git",
|
|
@@ -22,12 +29,20 @@
|
|
|
22
29
|
}
|
|
23
30
|
},
|
|
24
31
|
"files": [
|
|
25
|
-
"dist"
|
|
32
|
+
"dist",
|
|
33
|
+
"llms.txt"
|
|
26
34
|
],
|
|
27
35
|
"sideEffects": false,
|
|
28
36
|
"publishConfig": {
|
|
29
37
|
"access": "public"
|
|
30
38
|
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup src/index.ts src/client.ts --format esm --dts",
|
|
41
|
+
"dev": "tsup src/index.ts src/client.ts --format esm --dts --watch",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"lint": "eslint src/",
|
|
44
|
+
"test": "vitest run"
|
|
45
|
+
},
|
|
31
46
|
"peerDependencies": {
|
|
32
47
|
"drizzle-orm": ">=0.35"
|
|
33
48
|
},
|
|
@@ -35,12 +50,5 @@
|
|
|
35
50
|
"tsup": "^8",
|
|
36
51
|
"typescript": "^5.7",
|
|
37
52
|
"vitest": "^4.1.0"
|
|
38
|
-
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"build": "tsup src/index.ts src/client.ts --format esm --dts",
|
|
41
|
-
"dev": "tsup src/index.ts src/client.ts --format esm --dts --watch",
|
|
42
|
-
"typecheck": "tsc --noEmit",
|
|
43
|
-
"lint": "eslint src/",
|
|
44
|
-
"test": "vitest run"
|
|
45
53
|
}
|
|
46
|
-
}
|
|
54
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Daniel Schmidt
|
|
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/dist/chunk-FVAAEIAE.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
function getTableName(table) {
|
|
3
|
-
return table._?.name ?? "unknown";
|
|
4
|
-
}
|
|
5
|
-
var ForbiddenError = class extends Error {
|
|
6
|
-
action;
|
|
7
|
-
table;
|
|
8
|
-
role;
|
|
9
|
-
descriptors;
|
|
10
|
-
constructor(options) {
|
|
11
|
-
const tableName = getTableName(options.table);
|
|
12
|
-
super(`Role '${options.role}' cannot ${options.action} on '${tableName}'`);
|
|
13
|
-
this.name = "ForbiddenError";
|
|
14
|
-
this.action = options.action;
|
|
15
|
-
this.table = options.table;
|
|
16
|
-
this.role = options.role;
|
|
17
|
-
this.descriptors = options.descriptors ?? [];
|
|
18
|
-
}
|
|
19
|
-
toJSON() {
|
|
20
|
-
return {
|
|
21
|
-
name: this.name,
|
|
22
|
-
message: this.message,
|
|
23
|
-
action: this.action,
|
|
24
|
-
table: getTableName(this.table),
|
|
25
|
-
role: this.role
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
ForbiddenError
|
|
32
|
-
};
|
package/dist/chunk-IPYPD2CZ.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// src/types.ts
|
|
2
|
-
var DRIZZLE_NAME_SYMBOL = /* @__PURE__ */ Symbol.for("drizzle:Name");
|
|
3
|
-
function getTableName(table) {
|
|
4
|
-
return table[DRIZZLE_NAME_SYMBOL] ?? "unknown";
|
|
5
|
-
}
|
|
6
|
-
var CRUD_ACTIONS = ["read", "create", "update", "delete"];
|
|
7
|
-
|
|
8
|
-
// src/errors.ts
|
|
9
|
-
var ForbiddenError = class extends Error {
|
|
10
|
-
/** The action that was denied (e.g., `"delete"`). */
|
|
11
|
-
action;
|
|
12
|
-
/** The Drizzle table the action targeted. */
|
|
13
|
-
table;
|
|
14
|
-
/** The role that lacked the permission, or `undefined` if not specified. */
|
|
15
|
-
role;
|
|
16
|
-
/** The full list of permission descriptors that were checked. */
|
|
17
|
-
descriptors;
|
|
18
|
-
/**
|
|
19
|
-
* Creates a new `ForbiddenError`.
|
|
20
|
-
*
|
|
21
|
-
* @param options - The action, table, and optional role/descriptors for the error.
|
|
22
|
-
*/
|
|
23
|
-
constructor(options) {
|
|
24
|
-
const tableName = getTableName(options.table);
|
|
25
|
-
const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
|
|
26
|
-
super(msg);
|
|
27
|
-
this.name = "ForbiddenError";
|
|
28
|
-
this.action = options.action;
|
|
29
|
-
this.table = options.table;
|
|
30
|
-
this.role = options.role;
|
|
31
|
-
this.descriptors = options.descriptors ?? [];
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Serializes the error to a JSON-safe object for server-to-client transfer.
|
|
35
|
-
*
|
|
36
|
-
* @returns A plain object with `name`, `message`, `action`, `table`, and `role` fields.
|
|
37
|
-
*/
|
|
38
|
-
toJSON() {
|
|
39
|
-
return {
|
|
40
|
-
name: this.name,
|
|
41
|
-
message: this.message,
|
|
42
|
-
action: this.action,
|
|
43
|
-
table: getTableName(this.table),
|
|
44
|
-
role: this.role
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export {
|
|
50
|
-
getTableName,
|
|
51
|
-
CRUD_ACTIONS,
|
|
52
|
-
ForbiddenError
|
|
53
|
-
};
|
package/dist/chunk-PNHVTXCZ.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// src/types.ts
|
|
2
|
-
var DRIZZLE_NAME_SYMBOL = /* @__PURE__ */ Symbol.for("drizzle:Name");
|
|
3
|
-
function getTableName(table) {
|
|
4
|
-
return table[DRIZZLE_NAME_SYMBOL] ?? "unknown";
|
|
5
|
-
}
|
|
6
|
-
var CRUD_ACTIONS = ["read", "create", "update", "delete"];
|
|
7
|
-
|
|
8
|
-
// src/errors.ts
|
|
9
|
-
var ForbiddenError = class extends Error {
|
|
10
|
-
action;
|
|
11
|
-
table;
|
|
12
|
-
role;
|
|
13
|
-
descriptors;
|
|
14
|
-
constructor(options) {
|
|
15
|
-
const tableName = getTableName(options.table);
|
|
16
|
-
const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
|
|
17
|
-
super(msg);
|
|
18
|
-
this.name = "ForbiddenError";
|
|
19
|
-
this.action = options.action;
|
|
20
|
-
this.table = options.table;
|
|
21
|
-
this.role = options.role;
|
|
22
|
-
this.descriptors = options.descriptors ?? [];
|
|
23
|
-
}
|
|
24
|
-
toJSON() {
|
|
25
|
-
return {
|
|
26
|
-
name: this.name,
|
|
27
|
-
message: this.message,
|
|
28
|
-
action: this.action,
|
|
29
|
-
table: getTableName(this.table),
|
|
30
|
-
role: this.role
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export {
|
|
36
|
-
getTableName,
|
|
37
|
-
CRUD_ACTIONS,
|
|
38
|
-
ForbiddenError
|
|
39
|
-
};
|
package/dist/chunk-YYYHMPTS.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
function getTableName(table) {
|
|
3
|
-
return table._?.name ?? "unknown";
|
|
4
|
-
}
|
|
5
|
-
var ForbiddenError = class extends Error {
|
|
6
|
-
action;
|
|
7
|
-
table;
|
|
8
|
-
role;
|
|
9
|
-
descriptors;
|
|
10
|
-
constructor(options) {
|
|
11
|
-
const tableName = getTableName(options.table);
|
|
12
|
-
const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
|
|
13
|
-
super(msg);
|
|
14
|
-
this.name = "ForbiddenError";
|
|
15
|
-
this.action = options.action;
|
|
16
|
-
this.table = options.table;
|
|
17
|
-
this.role = options.role;
|
|
18
|
-
this.descriptors = options.descriptors ?? [];
|
|
19
|
-
}
|
|
20
|
-
toJSON() {
|
|
21
|
-
return {
|
|
22
|
-
name: this.name,
|
|
23
|
-
message: this.message,
|
|
24
|
-
action: this.action,
|
|
25
|
-
table: getTableName(this.table),
|
|
26
|
-
role: this.role
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export {
|
|
32
|
-
ForbiddenError
|
|
33
|
-
};
|
package/dist/chunk-ZTQJZJFW.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// src/types.ts
|
|
2
|
-
function getTableName(table) {
|
|
3
|
-
return table._?.name ?? "unknown";
|
|
4
|
-
}
|
|
5
|
-
var CRUD_ACTIONS = ["read", "create", "update", "delete"];
|
|
6
|
-
|
|
7
|
-
// src/errors.ts
|
|
8
|
-
var ForbiddenError = class extends Error {
|
|
9
|
-
action;
|
|
10
|
-
table;
|
|
11
|
-
role;
|
|
12
|
-
descriptors;
|
|
13
|
-
constructor(options) {
|
|
14
|
-
const tableName = getTableName(options.table);
|
|
15
|
-
const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
|
|
16
|
-
super(msg);
|
|
17
|
-
this.name = "ForbiddenError";
|
|
18
|
-
this.action = options.action;
|
|
19
|
-
this.table = options.table;
|
|
20
|
-
this.role = options.role;
|
|
21
|
-
this.descriptors = options.descriptors ?? [];
|
|
22
|
-
}
|
|
23
|
-
toJSON() {
|
|
24
|
-
return {
|
|
25
|
-
name: this.name,
|
|
26
|
-
message: this.message,
|
|
27
|
-
action: this.action,
|
|
28
|
-
table: getTableName(this.table),
|
|
29
|
-
role: this.role
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export {
|
|
35
|
-
getTableName,
|
|
36
|
-
CRUD_ACTIONS,
|
|
37
|
-
ForbiddenError
|
|
38
|
-
};
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
type DrizzleTable = {
|
|
2
|
-
_: {
|
|
3
|
-
name: string;
|
|
4
|
-
};
|
|
5
|
-
};
|
|
6
|
-
type DrizzleSQL = {
|
|
7
|
-
getSQL(): unknown;
|
|
8
|
-
};
|
|
9
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
10
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
11
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
12
|
-
type WhereClause = (columns: Record<string, unknown>, user: any) => DrizzleSQL | undefined;
|
|
13
|
-
type Grant = {
|
|
14
|
-
action: PermissionAction;
|
|
15
|
-
subject: DrizzleTable | "all";
|
|
16
|
-
where?: WhereClause;
|
|
17
|
-
};
|
|
18
|
-
type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
|
|
19
|
-
where?: (columns: Record<string, unknown>, user: TUser) => DrizzleSQL | undefined;
|
|
20
|
-
}) => Grant;
|
|
21
|
-
type PermissionDescriptor = {
|
|
22
|
-
action: PermissionAction;
|
|
23
|
-
table: DrizzleTable;
|
|
24
|
-
};
|
|
25
|
-
type PermissionCheckResult = {
|
|
26
|
-
permitted: boolean;
|
|
27
|
-
denied: PermissionDescriptor[];
|
|
28
|
-
reasons: string[];
|
|
29
|
-
};
|
|
30
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
31
|
-
roles: TRoles;
|
|
32
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
33
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
34
|
-
};
|
|
35
|
-
type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
36
|
-
roles: TRoles;
|
|
37
|
-
grants: Record<TRoles[number], Grant[]>;
|
|
38
|
-
resolvedGrants: Record<TRoles[number], Grant[]>;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
type ForbiddenErrorOptions = {
|
|
42
|
-
action: PermissionAction;
|
|
43
|
-
table: DrizzleTable;
|
|
44
|
-
role?: string;
|
|
45
|
-
descriptors?: PermissionDescriptor[];
|
|
46
|
-
};
|
|
47
|
-
declare class ForbiddenError extends Error {
|
|
48
|
-
readonly action: PermissionAction;
|
|
49
|
-
readonly table: DrizzleTable;
|
|
50
|
-
readonly role: string | undefined;
|
|
51
|
-
readonly descriptors: PermissionDescriptor[];
|
|
52
|
-
constructor(options: ForbiddenErrorOptions);
|
|
53
|
-
toJSON(): {
|
|
54
|
-
name: string;
|
|
55
|
-
message: string;
|
|
56
|
-
action: PermissionAction;
|
|
57
|
-
table: string;
|
|
58
|
-
role: string | undefined;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { SQL } from 'drizzle-orm';
|
|
2
|
-
|
|
3
|
-
type DrizzleTable = {
|
|
4
|
-
_: {
|
|
5
|
-
name: string;
|
|
6
|
-
};
|
|
7
|
-
};
|
|
8
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
9
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
10
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
11
|
-
type WhereClause = (columns: Record<string, unknown>, user: any) => SQL | undefined;
|
|
12
|
-
type Grant = {
|
|
13
|
-
action: PermissionAction;
|
|
14
|
-
subject: DrizzleTable | "all";
|
|
15
|
-
where?: WhereClause;
|
|
16
|
-
};
|
|
17
|
-
type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
|
|
18
|
-
where?: (columns: Record<string, unknown>, user: TUser) => SQL | undefined;
|
|
19
|
-
}) => Grant;
|
|
20
|
-
type PermissionDescriptor = {
|
|
21
|
-
action: PermissionAction;
|
|
22
|
-
table: DrizzleTable;
|
|
23
|
-
};
|
|
24
|
-
type PermissionCheckResult = {
|
|
25
|
-
permitted: boolean;
|
|
26
|
-
denied: PermissionDescriptor[];
|
|
27
|
-
reasons: string[];
|
|
28
|
-
};
|
|
29
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
30
|
-
roles: TRoles;
|
|
31
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
32
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
33
|
-
};
|
|
34
|
-
type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
35
|
-
roles: TRoles;
|
|
36
|
-
grants: Record<TRoles[number], Grant[]>;
|
|
37
|
-
resolvedGrants: Record<TRoles[number], Grant[]>;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
type ForbiddenErrorOptions = {
|
|
41
|
-
action: PermissionAction;
|
|
42
|
-
table: DrizzleTable;
|
|
43
|
-
role: string;
|
|
44
|
-
descriptors?: PermissionDescriptor[];
|
|
45
|
-
};
|
|
46
|
-
declare class ForbiddenError extends Error {
|
|
47
|
-
readonly action: PermissionAction;
|
|
48
|
-
readonly table: DrizzleTable;
|
|
49
|
-
readonly role: string;
|
|
50
|
-
readonly descriptors: PermissionDescriptor[];
|
|
51
|
-
constructor(options: ForbiddenErrorOptions);
|
|
52
|
-
toJSON(): {
|
|
53
|
-
name: string;
|
|
54
|
-
message: string;
|
|
55
|
-
action: PermissionAction;
|
|
56
|
-
table: string;
|
|
57
|
-
role: string;
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { Table, SQL } from 'drizzle-orm';
|
|
2
|
-
|
|
3
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
4
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
5
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
6
|
-
type WhereClause = (columns: Record<string, unknown>, user: any) => SQL | undefined;
|
|
7
|
-
type Grant = {
|
|
8
|
-
action: PermissionAction;
|
|
9
|
-
subject: Table | "all";
|
|
10
|
-
where?: WhereClause;
|
|
11
|
-
};
|
|
12
|
-
type GrantFn<TUser> = (action: PermissionAction, subject: Table | "all", options?: {
|
|
13
|
-
where?: (columns: Record<string, unknown>, user: TUser) => SQL | undefined;
|
|
14
|
-
}) => Grant;
|
|
15
|
-
type PermissionDescriptor = {
|
|
16
|
-
action: PermissionAction;
|
|
17
|
-
table: Table;
|
|
18
|
-
};
|
|
19
|
-
type PermissionCheckResult = {
|
|
20
|
-
permitted: boolean;
|
|
21
|
-
denied: PermissionDescriptor[];
|
|
22
|
-
reasons: string[];
|
|
23
|
-
};
|
|
24
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
25
|
-
roles: TRoles;
|
|
26
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
27
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
28
|
-
};
|
|
29
|
-
type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
30
|
-
roles: TRoles;
|
|
31
|
-
grants: Record<TRoles[number], Grant[]>;
|
|
32
|
-
resolvedGrants: Record<TRoles[number], Grant[]>;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
type ForbiddenErrorOptions = {
|
|
36
|
-
action: PermissionAction;
|
|
37
|
-
table: Table;
|
|
38
|
-
role: string;
|
|
39
|
-
descriptors?: PermissionDescriptor[];
|
|
40
|
-
};
|
|
41
|
-
declare class ForbiddenError extends Error {
|
|
42
|
-
readonly action: PermissionAction;
|
|
43
|
-
readonly table: Table;
|
|
44
|
-
readonly role: string;
|
|
45
|
-
readonly descriptors: PermissionDescriptor[];
|
|
46
|
-
constructor(options: ForbiddenErrorOptions);
|
|
47
|
-
toJSON(): {
|
|
48
|
-
name: string;
|
|
49
|
-
message: string;
|
|
50
|
-
action: PermissionAction;
|
|
51
|
-
table: string;
|
|
52
|
-
role: string;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export { CRUD_ACTIONS as C, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
type DrizzleTable = {
|
|
2
|
-
_: {
|
|
3
|
-
name: string;
|
|
4
|
-
};
|
|
5
|
-
};
|
|
6
|
-
type DrizzleSQL = {
|
|
7
|
-
getSQL(): unknown;
|
|
8
|
-
};
|
|
9
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
10
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
11
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
12
|
-
type WhereClause = (columns: Record<string, unknown>, user: any) => DrizzleSQL | undefined;
|
|
13
|
-
type Grant = {
|
|
14
|
-
action: PermissionAction;
|
|
15
|
-
subject: DrizzleTable | "all";
|
|
16
|
-
where?: WhereClause;
|
|
17
|
-
};
|
|
18
|
-
type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
|
|
19
|
-
where?: (columns: Record<string, unknown>, user: TUser) => DrizzleSQL | undefined;
|
|
20
|
-
}) => Grant;
|
|
21
|
-
type PermissionDescriptor = {
|
|
22
|
-
action: PermissionAction;
|
|
23
|
-
table: DrizzleTable;
|
|
24
|
-
};
|
|
25
|
-
type PermissionCheckResult = {
|
|
26
|
-
permitted: boolean;
|
|
27
|
-
denied: PermissionDescriptor[];
|
|
28
|
-
reasons: string[];
|
|
29
|
-
};
|
|
30
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
31
|
-
roles: TRoles;
|
|
32
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
33
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
34
|
-
};
|
|
35
|
-
type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
36
|
-
roles: TRoles;
|
|
37
|
-
grants: Record<TRoles[number], Grant[]>;
|
|
38
|
-
resolvedGrants: Record<TRoles[number], Grant[]>;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
type ForbiddenErrorOptions = {
|
|
42
|
-
action: PermissionAction;
|
|
43
|
-
table: DrizzleTable;
|
|
44
|
-
role: string;
|
|
45
|
-
descriptors?: PermissionDescriptor[];
|
|
46
|
-
};
|
|
47
|
-
declare class ForbiddenError extends Error {
|
|
48
|
-
readonly action: PermissionAction;
|
|
49
|
-
readonly table: DrizzleTable;
|
|
50
|
-
readonly role: string;
|
|
51
|
-
readonly descriptors: PermissionDescriptor[];
|
|
52
|
-
constructor(options: ForbiddenErrorOptions);
|
|
53
|
-
toJSON(): {
|
|
54
|
-
name: string;
|
|
55
|
-
message: string;
|
|
56
|
-
action: PermissionAction;
|
|
57
|
-
table: string;
|
|
58
|
-
role: string;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal structural type for a Drizzle ORM table reference.
|
|
3
|
-
*
|
|
4
|
-
* Drizzle stores table metadata via Symbols (e.g., `Symbol('drizzle:Name')`).
|
|
5
|
-
* This loose type avoids importing `drizzle-orm` directly into the permissions package.
|
|
6
|
-
*/
|
|
7
|
-
type DrizzleTable = Record<string | symbol, any>;
|
|
8
|
-
/**
|
|
9
|
-
* Minimal structural type for a Drizzle SQL expression.
|
|
10
|
-
*
|
|
11
|
-
* Matches any object with a `getSQL()` method, which includes Drizzle's
|
|
12
|
-
* `SQL`, `SQLWrapper`, and condition builder results.
|
|
13
|
-
*/
|
|
14
|
-
type DrizzleSQL = {
|
|
15
|
-
getSQL(): unknown;
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* Extracts the table name string from a Drizzle table reference.
|
|
19
|
-
*
|
|
20
|
-
* @param table - A Drizzle table object containing the `drizzle:Name` symbol.
|
|
21
|
-
* @returns The table name, or `"unknown"` if the symbol is not present.
|
|
22
|
-
*/
|
|
23
|
-
declare function getTableName(table: DrizzleTable): string;
|
|
24
|
-
/**
|
|
25
|
-
* A permission action: one of the four CRUD operations, or `"manage"` for all.
|
|
26
|
-
*
|
|
27
|
-
* - `"read"` maps to `SELECT` queries.
|
|
28
|
-
* - `"create"` maps to `INSERT` statements.
|
|
29
|
-
* - `"update"` maps to `UPDATE` statements.
|
|
30
|
-
* - `"delete"` maps to `DELETE` statements.
|
|
31
|
-
* - `"manage"` is shorthand for granting all four CRUD actions.
|
|
32
|
-
*/
|
|
33
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
34
|
-
/**
|
|
35
|
-
* A CRUD-only permission action (excludes `"manage"`).
|
|
36
|
-
*
|
|
37
|
-
* Useful when you need to iterate over concrete operations without the
|
|
38
|
-
* `"manage"` shorthand. See also {@link CRUD_ACTIONS}.
|
|
39
|
-
*/
|
|
40
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
41
|
-
/**
|
|
42
|
-
* Readonly array of the four CRUD action strings, useful for iteration.
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```typescript
|
|
46
|
-
* import { CRUD_ACTIONS } from "@cfast/permissions";
|
|
47
|
-
*
|
|
48
|
-
* for (const action of CRUD_ACTIONS) {
|
|
49
|
-
* console.log(action); // "read", "create", "update", "delete"
|
|
50
|
-
* }
|
|
51
|
-
* ```
|
|
52
|
-
*/
|
|
53
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
54
|
-
/**
|
|
55
|
-
* A function that produces a Drizzle `WHERE` clause for row-level permission filtering.
|
|
56
|
-
*
|
|
57
|
-
* @param columns - The table's column references for building filter expressions.
|
|
58
|
-
* @param user - The current user object (from `@cfast/auth`).
|
|
59
|
-
* @returns A Drizzle SQL expression to restrict matching rows, or `undefined` for no restriction.
|
|
60
|
-
*/
|
|
61
|
-
type WhereClause = (columns: Record<string, unknown>, user: any) => DrizzleSQL | undefined;
|
|
62
|
-
/**
|
|
63
|
-
* A single permission grant: an action on a subject, optionally restricted by a `where` clause.
|
|
64
|
-
*/
|
|
65
|
-
type Grant = {
|
|
66
|
-
/** The permitted operation. `"manage"` is shorthand for all four CRUD actions. */
|
|
67
|
-
action: PermissionAction;
|
|
68
|
-
/** The Drizzle table this grant applies to, or `"all"` for every table. */
|
|
69
|
-
subject: DrizzleTable | "all";
|
|
70
|
-
/** Optional row-level filter that restricts which rows this grant covers. */
|
|
71
|
-
where?: WhereClause;
|
|
72
|
-
};
|
|
73
|
-
/**
|
|
74
|
-
* Type-safe grant builder function, parameterized by the user type.
|
|
75
|
-
*
|
|
76
|
-
* Used when `grants` is provided as a callback in {@link PermissionsConfig}
|
|
77
|
-
* so that `where` clauses receive a correctly typed `user` parameter.
|
|
78
|
-
*
|
|
79
|
-
* @typeParam TUser - The user type passed to `where` clause callbacks.
|
|
80
|
-
*/
|
|
81
|
-
type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
|
|
82
|
-
where?: (columns: Record<string, unknown>, user: TUser) => DrizzleSQL | undefined;
|
|
83
|
-
}) => Grant;
|
|
84
|
-
/**
|
|
85
|
-
* Structural description of a permission requirement.
|
|
86
|
-
*
|
|
87
|
-
* Describes *what kind* of operation on *which table* without specifying concrete row values.
|
|
88
|
-
* This is what makes client-side permission introspection possible: you can check whether a
|
|
89
|
-
* role has the right grants without knowing the specific row being accessed.
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```typescript
|
|
93
|
-
* const descriptor: PermissionDescriptor = {
|
|
94
|
-
* action: "update",
|
|
95
|
-
* table: posts,
|
|
96
|
-
* };
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
type PermissionDescriptor = {
|
|
100
|
-
/** The operation being checked. */
|
|
101
|
-
action: PermissionAction;
|
|
102
|
-
/** The Drizzle table the operation targets. */
|
|
103
|
-
table: DrizzleTable;
|
|
104
|
-
};
|
|
105
|
-
/**
|
|
106
|
-
* Result of a permission check via {@link checkPermissions}.
|
|
107
|
-
*/
|
|
108
|
-
type PermissionCheckResult = {
|
|
109
|
-
/** `true` only if every descriptor in the check was satisfied. */
|
|
110
|
-
permitted: boolean;
|
|
111
|
-
/** The descriptors that were not satisfied. */
|
|
112
|
-
denied: PermissionDescriptor[];
|
|
113
|
-
/** Human-readable reasons for each denial. */
|
|
114
|
-
reasons: string[];
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* Configuration object for {@link definePermissions}.
|
|
118
|
-
*
|
|
119
|
-
* @typeParam TRoles - Tuple of role name string literals (use `as const`).
|
|
120
|
-
* @typeParam TUser - The user type for typed `where` clauses (defaults to `unknown`).
|
|
121
|
-
*/
|
|
122
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
123
|
-
/** All roles in the application, declared with `as const` for type inference. */
|
|
124
|
-
roles: TRoles;
|
|
125
|
-
/** A map from role to grant arrays, or a callback that receives a typed `grant` function. */
|
|
126
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
127
|
-
/** Optional role hierarchy declaring which roles inherit from which. */
|
|
128
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* The resolved permissions object returned by {@link definePermissions}.
|
|
132
|
-
*
|
|
133
|
-
* Contains the original roles and grants, plus the hierarchy-expanded `resolvedGrants`.
|
|
134
|
-
* Pass this to `createDb()` for server-side enforcement or import it on the client
|
|
135
|
-
* for UI-level permission introspection.
|
|
136
|
-
*
|
|
137
|
-
* @typeParam TRoles - Tuple of role name string literals.
|
|
138
|
-
*/
|
|
139
|
-
type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
140
|
-
/** The role names from the configuration. */
|
|
141
|
-
roles: TRoles;
|
|
142
|
-
/** The raw grants as declared (before hierarchy expansion). */
|
|
143
|
-
grants: Record<TRoles[number], Grant[]>;
|
|
144
|
-
/** Grants expanded with inherited grants from the role hierarchy. */
|
|
145
|
-
resolvedGrants: Record<TRoles[number], Grant[]>;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/** Options for constructing a {@link ForbiddenError}. */
|
|
149
|
-
type ForbiddenErrorOptions = {
|
|
150
|
-
/** The action that was denied. */
|
|
151
|
-
action: PermissionAction;
|
|
152
|
-
/** The Drizzle table the action targeted. */
|
|
153
|
-
table: DrizzleTable;
|
|
154
|
-
/** The role that lacked the permission, if known. */
|
|
155
|
-
role?: string;
|
|
156
|
-
/** The full list of permission descriptors that were checked. */
|
|
157
|
-
descriptors?: PermissionDescriptor[];
|
|
158
|
-
};
|
|
159
|
-
/**
|
|
160
|
-
* Error thrown when a permission check fails during an operation.
|
|
161
|
-
*
|
|
162
|
-
* Extends `Error` with structured fields for the denied action, target table,
|
|
163
|
-
* and role. Includes a `toJSON()` method so it can be serialized across the
|
|
164
|
-
* server/client boundary.
|
|
165
|
-
*/
|
|
166
|
-
declare class ForbiddenError extends Error {
|
|
167
|
-
/** The action that was denied (e.g., `"delete"`). */
|
|
168
|
-
readonly action: PermissionAction;
|
|
169
|
-
/** The Drizzle table the action targeted. */
|
|
170
|
-
readonly table: DrizzleTable;
|
|
171
|
-
/** The role that lacked the permission, or `undefined` if not specified. */
|
|
172
|
-
readonly role: string | undefined;
|
|
173
|
-
/** The full list of permission descriptors that were checked. */
|
|
174
|
-
readonly descriptors: PermissionDescriptor[];
|
|
175
|
-
/**
|
|
176
|
-
* Creates a new `ForbiddenError`.
|
|
177
|
-
*
|
|
178
|
-
* @param options - The action, table, and optional role/descriptors for the error.
|
|
179
|
-
*/
|
|
180
|
-
constructor(options: ForbiddenErrorOptions);
|
|
181
|
-
/**
|
|
182
|
-
* Serializes the error to a JSON-safe object for server-to-client transfer.
|
|
183
|
-
*
|
|
184
|
-
* @returns A plain object with `name`, `message`, `action`, `table`, and `role` fields.
|
|
185
|
-
*/
|
|
186
|
-
toJSON(): {
|
|
187
|
-
name: string;
|
|
188
|
-
message: string;
|
|
189
|
-
action: PermissionAction;
|
|
190
|
-
table: string;
|
|
191
|
-
role: string | undefined;
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f, getTableName as g };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
type DrizzleTable = Record<string | symbol, any>;
|
|
2
|
-
type DrizzleSQL = {
|
|
3
|
-
getSQL(): unknown;
|
|
4
|
-
};
|
|
5
|
-
declare function getTableName(table: DrizzleTable): string;
|
|
6
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
7
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
8
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
9
|
-
type WhereClause = (columns: Record<string, unknown>, user: any) => DrizzleSQL | undefined;
|
|
10
|
-
type Grant = {
|
|
11
|
-
action: PermissionAction;
|
|
12
|
-
subject: DrizzleTable | "all";
|
|
13
|
-
where?: WhereClause;
|
|
14
|
-
};
|
|
15
|
-
type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
|
|
16
|
-
where?: (columns: Record<string, unknown>, user: TUser) => DrizzleSQL | undefined;
|
|
17
|
-
}) => Grant;
|
|
18
|
-
type PermissionDescriptor = {
|
|
19
|
-
action: PermissionAction;
|
|
20
|
-
table: DrizzleTable;
|
|
21
|
-
};
|
|
22
|
-
type PermissionCheckResult = {
|
|
23
|
-
permitted: boolean;
|
|
24
|
-
denied: PermissionDescriptor[];
|
|
25
|
-
reasons: string[];
|
|
26
|
-
};
|
|
27
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
28
|
-
roles: TRoles;
|
|
29
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
30
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
31
|
-
};
|
|
32
|
-
type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
33
|
-
roles: TRoles;
|
|
34
|
-
grants: Record<TRoles[number], Grant[]>;
|
|
35
|
-
resolvedGrants: Record<TRoles[number], Grant[]>;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
type ForbiddenErrorOptions = {
|
|
39
|
-
action: PermissionAction;
|
|
40
|
-
table: DrizzleTable;
|
|
41
|
-
role?: string;
|
|
42
|
-
descriptors?: PermissionDescriptor[];
|
|
43
|
-
};
|
|
44
|
-
declare class ForbiddenError extends Error {
|
|
45
|
-
readonly action: PermissionAction;
|
|
46
|
-
readonly table: DrizzleTable;
|
|
47
|
-
readonly role: string | undefined;
|
|
48
|
-
readonly descriptors: PermissionDescriptor[];
|
|
49
|
-
constructor(options: ForbiddenErrorOptions);
|
|
50
|
-
toJSON(): {
|
|
51
|
-
name: string;
|
|
52
|
-
message: string;
|
|
53
|
-
action: PermissionAction;
|
|
54
|
-
table: string;
|
|
55
|
-
role: string | undefined;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f, getTableName as g };
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { Table, SQL } from 'drizzle-orm';
|
|
2
|
-
|
|
3
|
-
type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
|
|
4
|
-
type CrudAction = Exclude<PermissionAction, "manage">;
|
|
5
|
-
declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
6
|
-
type WhereClause<TUser = unknown> = (columns: Record<string, unknown>, user: TUser) => SQL | undefined;
|
|
7
|
-
type Grant<TUser = unknown> = {
|
|
8
|
-
action: PermissionAction;
|
|
9
|
-
subject: Table | "all";
|
|
10
|
-
where?: WhereClause<TUser>;
|
|
11
|
-
};
|
|
12
|
-
type PermissionDescriptor = {
|
|
13
|
-
action: PermissionAction;
|
|
14
|
-
table: Table;
|
|
15
|
-
};
|
|
16
|
-
type PermissionCheckResult = {
|
|
17
|
-
permitted: boolean;
|
|
18
|
-
denied: PermissionDescriptor[];
|
|
19
|
-
reasons: string[];
|
|
20
|
-
};
|
|
21
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
22
|
-
roles: TRoles;
|
|
23
|
-
grants: Record<TRoles[number], Grant<TUser>[]>;
|
|
24
|
-
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
25
|
-
};
|
|
26
|
-
type Permissions<TRoles extends readonly string[] = readonly string[], TUser = unknown> = {
|
|
27
|
-
roles: TRoles;
|
|
28
|
-
grants: Record<TRoles[number], Grant<TUser>[]>;
|
|
29
|
-
resolvedGrants: Record<TRoles[number], Grant<TUser>[]>;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type ForbiddenErrorOptions = {
|
|
33
|
-
action: PermissionAction;
|
|
34
|
-
table: Table;
|
|
35
|
-
role: string;
|
|
36
|
-
descriptors?: PermissionDescriptor[];
|
|
37
|
-
};
|
|
38
|
-
declare class ForbiddenError extends Error {
|
|
39
|
-
readonly action: PermissionAction;
|
|
40
|
-
readonly table: Table;
|
|
41
|
-
readonly role: string;
|
|
42
|
-
readonly descriptors: PermissionDescriptor[];
|
|
43
|
-
constructor(options: ForbiddenErrorOptions);
|
|
44
|
-
toJSON(): {
|
|
45
|
-
name: string;
|
|
46
|
-
message: string;
|
|
47
|
-
action: PermissionAction;
|
|
48
|
-
table: string;
|
|
49
|
-
role: string;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { CRUD_ACTIONS as C, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e };
|