@svadmin/casbin 0.0.0-stage → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@svadmin/casbin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.1",
4
+ "description": "Casbin policy-based AccessControlProvider adapter for svadmin",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "src/**/*.ts",
9
+ "!src/**/*.test.*",
10
+ "!src/**/*.spec.*"
11
+ ],
12
+ "main": "src/index.ts",
13
+ "types": "src/index.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./src/index.ts",
17
+ "default": "./src/index.ts"
18
+ }
19
+ },
20
+ "peerDependencies": {
21
+ "@svadmin/core": "^0.80.0"
22
+ },
23
+ "keywords": [
24
+ "svadmin",
25
+ "casbin",
26
+ "rbac",
27
+ "perm",
28
+ "abac",
29
+ "access-control",
30
+ "authorization"
31
+ ],
32
+ "license": "MIT",
33
+ "author": "zuohuadong",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/vibeunion/svadmin.git",
37
+ "directory": "packages/casbin"
38
+ },
39
+ "homepage": "https://github.com/vibeunion/svadmin#readme",
40
+ "bugs": {
41
+ "url": "https://github.com/vibeunion/svadmin/issues"
42
+ },
43
+ "svadmin": {
44
+ "capabilities": [
45
+ "access-control"
46
+ ],
47
+ "core": "^0.55.0",
48
+ "stability": "stable",
49
+ "migration": null
50
+ }
51
+ }
@@ -0,0 +1,120 @@
1
+ import type { AccessControlProvider, CanParams, CanResult } from '@svadmin/core';
2
+
3
+ export interface CasbinEnforcerLike {
4
+ enforce: (...args: unknown[]) => Promise<boolean> | boolean;
5
+ }
6
+
7
+ export interface CasbinAdapterOptions {
8
+ /** Initialized casbin enforcer instance */
9
+ enforcer?: CasbinEnforcerLike;
10
+ /** Remote Casbin REST service endpoint URL */
11
+ apiUrl?: string;
12
+ /** Authorization bearer token for remote API */
13
+ token?: string;
14
+ /** Function returning the current subject user or role string, e.g. () => 'alice' */
15
+ user: () => string | Promise<string>;
16
+ /** Optional function returning tenant/domain string for domain-based RBAC */
17
+ domain?: () => string | Promise<string | undefined> | undefined;
18
+ /** Custom action mapping from svadmin actions ('list', 'edit', etc.) to Casbin actions ('read', 'write', etc.) */
19
+ actionMap?: (action: string) => string;
20
+ /** Custom resource mapping from svadmin resource names to Casbin object names */
21
+ resourceMap?: (resource: string) => string;
22
+ /** Custom argument builder if using advanced PERM schemas */
23
+ buildParams?: (ctx: {
24
+ user: string;
25
+ domain?: string | undefined;
26
+ resource: string;
27
+ action: string;
28
+ params?: unknown;
29
+ }) => unknown[] | Promise<unknown[]>;
30
+ fetch?: typeof fetch;
31
+ }
32
+
33
+ const defaultActionMap: Record<string, string> = {
34
+ list: 'read',
35
+ show: 'read',
36
+ create: 'write',
37
+ edit: 'write',
38
+ delete: 'delete',
39
+ };
40
+
41
+ export function createCasbinAccessControl(options: CasbinAdapterOptions): AccessControlProvider {
42
+ const request = options.fetch ?? fetch;
43
+
44
+ async function checkPermission(args: unknown[]): Promise<boolean> {
45
+ if (options.enforcer) {
46
+ return Boolean(await options.enforcer.enforce(...args));
47
+ }
48
+
49
+ if (!options.apiUrl) {
50
+ throw new Error('Casbin adapter requires either options.enforcer or options.apiUrl');
51
+ }
52
+
53
+ const endpoint = `${options.apiUrl.replace(/\/+$/, '')}/api/enforce`;
54
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
55
+ if (options.token) {
56
+ headers['Authorization'] = `Bearer ${options.token}`;
57
+ }
58
+
59
+ const response = await request(endpoint, {
60
+ method: 'POST',
61
+ headers,
62
+ body: JSON.stringify({ params: args }),
63
+ });
64
+
65
+ if (!response.ok) {
66
+ throw new Error(`Casbin enforce check failed with status ${response.status}`);
67
+ }
68
+
69
+ const result = await response.json() as { allowed?: boolean; result?: boolean };
70
+ return Boolean(result.allowed ?? result.result);
71
+ }
72
+
73
+ return {
74
+ async can(params: CanParams): Promise<CanResult> {
75
+ try {
76
+ const user = await options.user();
77
+ const domain = options.domain ? await options.domain() : undefined;
78
+ const action = options.actionMap
79
+ ? options.actionMap(params.action)
80
+ : defaultActionMap[params.action] ?? params.action;
81
+ const resource = options.resourceMap
82
+ ? options.resourceMap(params.resource)
83
+ : params.resource;
84
+
85
+ let enforceArgs: unknown[];
86
+ if (options.buildParams) {
87
+ enforceArgs = await options.buildParams({
88
+ user,
89
+ domain,
90
+ resource,
91
+ action,
92
+ params: params.params,
93
+ });
94
+ } else if (domain) {
95
+ // Standard domain-based RBAC: enforce(sub, dom, obj, act)
96
+ enforceArgs = [user, domain, resource, action];
97
+ } else {
98
+ // Standard RBAC / ACL: enforce(sub, obj, act)
99
+ enforceArgs = [user, resource, action];
100
+ }
101
+
102
+ const allowed = await checkPermission(enforceArgs);
103
+
104
+ if (allowed) {
105
+ return { can: true };
106
+ }
107
+
108
+ return {
109
+ can: false,
110
+ reason: `Casbin policy denied [${enforceArgs.join(', ')}]`,
111
+ };
112
+ } catch (err: unknown) {
113
+ return {
114
+ can: false,
115
+ reason: err instanceof Error ? err.message : 'Casbin authorization check failed',
116
+ };
117
+ }
118
+ },
119
+ };
120
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export {
2
+ createCasbinAccessControl,
3
+ type CasbinAdapterOptions,
4
+ type CasbinEnforcerLike,
5
+ } from './casbin-access-control.js';
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.