@rdlabo/workers-hono-kit 0.6.17 → 0.6.18
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 +34 -0
- package/dist/authorization/role-policy.d.ts +32 -0
- package/dist/authorization/role-policy.js +25 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -616,6 +616,40 @@ The package ships three `bin` commands (run via `npx` or an npm script in the co
|
|
|
616
616
|
| `workers-hono-kit-check-subrequest-fanout [dir…]` | CI gate that greps for per-item external-call fan-outs (`runWithConcurrency(` / `PromisePool` / `.withConcurrency(`) that would eventually exceed the Workers subrequest cap. Annotate a genuinely-safe site with `subrequest-ok`. Scans `src` by default; exits 1 on an un-annotated marker. |
|
|
617
617
|
| `workers-hono-kit-db-baseline [--migrations ./drizzle]` | Brownfield first-deploy helper: record the baseline `0000` migration as *already applied* on an existing MySQL DB without running its DDL (the CLI wrapper around `baselineMigrations` / `readBaselineEntry`). Reads DB credentials from `DB_SECRET` (AWS RDS managed secret) or the individual `DB_*` env vars. |
|
|
618
618
|
|
|
619
|
+
## Storage-agnostic role policies
|
|
620
|
+
|
|
621
|
+
`createRolePolicy` builds pure RBAC checks without coupling the policy to a database schema. The
|
|
622
|
+
application can resolve roles from a membership table, a `users.role` column, token claims, or any
|
|
623
|
+
other source.
|
|
624
|
+
|
|
625
|
+
```ts
|
|
626
|
+
import { createRolePolicy } from '@rdlabo/workers-hono-kit';
|
|
627
|
+
|
|
628
|
+
type Role = 'owner' | 'admin' | 'member' | 'read';
|
|
629
|
+
type Permission = 'organization.manage' | 'resource.write' | 'resource.read';
|
|
630
|
+
|
|
631
|
+
const policy = createRolePolicy<Role, Permission>({
|
|
632
|
+
permissions: {
|
|
633
|
+
owner: ['organization.manage', 'resource.write', 'resource.read'],
|
|
634
|
+
admin: ['resource.write', 'resource.read'],
|
|
635
|
+
member: ['resource.write', 'resource.read'],
|
|
636
|
+
read: ['resource.read'],
|
|
637
|
+
},
|
|
638
|
+
assignableRoles: {
|
|
639
|
+
owner: ['admin', 'member', 'read'],
|
|
640
|
+
admin: ['member', 'read'],
|
|
641
|
+
member: [],
|
|
642
|
+
read: [],
|
|
643
|
+
},
|
|
644
|
+
manageableRoles: {
|
|
645
|
+
owner: ['admin', 'member', 'read'],
|
|
646
|
+
admin: ['member', 'read'],
|
|
647
|
+
member: [],
|
|
648
|
+
read: [],
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
```
|
|
652
|
+
|
|
619
653
|
## Development
|
|
620
654
|
|
|
621
655
|
```bash
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** A role-to-permissions lookup used by {@link createRolePolicy}. */
|
|
2
|
+
export type RolePermissionMap<Role extends string, Permission extends string> = Readonly<Record<Role, readonly Permission[]>>;
|
|
3
|
+
/** A role-to-roles lookup used for assignment and management boundaries. */
|
|
4
|
+
export type RoleRelationMap<Role extends string> = Readonly<Record<Role, readonly Role[]>>;
|
|
5
|
+
/** Configuration for a storage-agnostic role policy. */
|
|
6
|
+
export interface RolePolicyConfig<Role extends string, Permission extends string> {
|
|
7
|
+
/** Permissions granted to each role. */
|
|
8
|
+
permissions: RolePermissionMap<Role, Permission>;
|
|
9
|
+
/** Roles that each actor role may assign to another subject. */
|
|
10
|
+
assignableRoles: RoleRelationMap<Role>;
|
|
11
|
+
/** Existing subject roles that each actor role may manage. */
|
|
12
|
+
manageableRoles: RoleRelationMap<Role>;
|
|
13
|
+
}
|
|
14
|
+
/** Pure authorization checks produced from a role policy configuration. */
|
|
15
|
+
export interface RolePolicy<Role extends string, Permission extends string> {
|
|
16
|
+
/** Returns whether `role` grants `permission`. */
|
|
17
|
+
hasPermission(role: Role, permission: Permission): boolean;
|
|
18
|
+
/** Returns whether `actorRole` may assign `nextRole`. */
|
|
19
|
+
canAssignRole(actorRole: Role, nextRole: Role): boolean;
|
|
20
|
+
/** Returns whether `actorRole` may manage a subject with `targetRole`. */
|
|
21
|
+
canManageRole(actorRole: Role, targetRole: Role): boolean;
|
|
22
|
+
/** Returns whether an actor may change a subject from one role to another. */
|
|
23
|
+
canChangeRole(actorRole: Role, currentRole: Role, nextRole: Role): boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates pure, schema-independent RBAC checks.
|
|
27
|
+
*
|
|
28
|
+
* The caller resolves a role from any persistence model (for example `group_users.role`,
|
|
29
|
+
* `users.role`, a token claim, or an external identity provider) and passes that role into these
|
|
30
|
+
* checks. Keeping lookup and policy separate makes the same policy reusable across applications.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createRolePolicy<Role extends string, Permission extends string>(config: RolePolicyConfig<Role, Permission>): RolePolicy<Role, Permission>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function ownListIncludes(record, key, value) {
|
|
2
|
+
if (!Object.prototype.hasOwnProperty.call(record, key)) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
return record[key]?.includes(value) ?? false;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates pure, schema-independent RBAC checks.
|
|
9
|
+
*
|
|
10
|
+
* The caller resolves a role from any persistence model (for example `group_users.role`,
|
|
11
|
+
* `users.role`, a token claim, or an external identity provider) and passes that role into these
|
|
12
|
+
* checks. Keeping lookup and policy separate makes the same policy reusable across applications.
|
|
13
|
+
*/
|
|
14
|
+
export function createRolePolicy(config) {
|
|
15
|
+
// Keep the public config strongly typed while treating runtime values as untrusted input.
|
|
16
|
+
const permissions = config.permissions;
|
|
17
|
+
const assignableRoles = config.assignableRoles;
|
|
18
|
+
const manageableRoles = config.manageableRoles;
|
|
19
|
+
return {
|
|
20
|
+
hasPermission: (role, permission) => ownListIncludes(permissions, role, permission),
|
|
21
|
+
canAssignRole: (actorRole, nextRole) => ownListIncludes(assignableRoles, actorRole, nextRole),
|
|
22
|
+
canManageRole: (actorRole, targetRole) => ownListIncludes(manageableRoles, actorRole, targetRole),
|
|
23
|
+
canChangeRole: (actorRole, currentRole, nextRole) => ownListIncludes(manageableRoles, actorRole, currentRole) && ownListIncludes(assignableRoles, actorRole, nextRole),
|
|
24
|
+
};
|
|
25
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export { createIsolateMemo } from './container/isolate-memo.js';
|
|
|
24
24
|
export type { IsolateMemo } from './container/isolate-memo.js';
|
|
25
25
|
export { createContainerRuntime } from './container/middleware.js';
|
|
26
26
|
export type { ContainerBuildContext, ContainerRuntime, ContainerRuntimeOptions } from './container/middleware.js';
|
|
27
|
+
export { createRolePolicy } from './authorization/role-policy.js';
|
|
28
|
+
export type { RolePermissionMap, RolePolicy, RolePolicyConfig, RoleRelationMap } from './authorization/role-policy.js';
|
|
27
29
|
export { getUserProtocol } from './http/user-protocol.js';
|
|
28
30
|
export type { IUserProtocol } from './http/user-protocol.js';
|
|
29
31
|
export { getAppInfo } from './http/app-info.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ export { perfLog } from './middleware/perf-log.js';
|
|
|
21
21
|
export { createMaintenanceMiddleware, createMaintenanceWaitHandler, isMaintenanceEnabled, MAINTENANCE_BODY, MAINTENANCE_CODE, MAINTENANCE_WAIT_PATH, } from './middleware/maintenance.js';
|
|
22
22
|
export { createIsolateMemo } from './container/isolate-memo.js';
|
|
23
23
|
export { createContainerRuntime } from './container/middleware.js';
|
|
24
|
+
// authorization
|
|
25
|
+
export { createRolePolicy } from './authorization/role-policy.js';
|
|
24
26
|
// http
|
|
25
27
|
export { getUserProtocol } from './http/user-protocol.js';
|
|
26
28
|
export { getAppInfo } from './http/app-info.js';
|