@lark-apaas/auth-sdk 0.1.0-alpha.7 → 0.1.0-alpha.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,9 +12,11 @@ yarn add @lark-apaas/auth-sdk
12
12
 
13
13
  ## 快速开始
14
14
 
15
+ ### 模版接入
16
+
15
17
  ```tsx
16
18
  import React from 'react';
17
- import { AuthProvider, Can, useAuthAbility } from '@lark-apaas/auth-sdk';
19
+ import { AuthProvider, CanRole, useAuthAbility } from '@lark-apaas/auth-sdk';
18
20
 
19
21
  export default function App() {
20
22
  return (
@@ -31,23 +33,75 @@ export default function App() {
31
33
  </AuthProvider>
32
34
  );
33
35
  }
36
+ ```
37
+
38
+
39
+ ### 开发组件 - 使用 CanRole 组件
40
+
41
+ ```tsx
42
+ import { CanRole } from '@lark-apaas/auth-sdk';
43
+
44
+ function Home() {
45
+ return (
46
+ <div>
47
+ <CanRole roles={['role_admin']}>
48
+ <div>管理员按钮</div>
49
+ </CanRole>
50
+ <CanRole roles={['role_admin', 'role_editor']}>
51
+ <div>编辑按钮</div>
52
+ </CanRole>
53
+ </div>
54
+ );
55
+ }
56
+ ```
57
+
58
+ ### 开发组件 - 使用 AbilityContext 处理复杂场景
59
+
60
+ ```tsx
61
+ import { useContext } from 'react';
62
+ import { AbilityContext, ROLE_SUBJECT } from '@lark-apaas/auth-sdk';
34
63
 
35
64
  function Home() {
36
- const ability = useAuthAbility();
65
+ const ability = useContext(AbilityContext);
37
66
  return (
38
67
  <div>
39
- <Can I="admin" a="@role">
68
+ {ability.can('role_admin', ROLE_SUBJECT) || ability.can('role_editor', ROLE_SUBJECT) ? (
40
69
  <div>可见的仪表盘</div>
41
- </Can>
42
- <button disabled={ability.cannot('reader', '@role')}>创建任务</button>
70
+ ) : null}
43
71
  </div>
44
72
  );
45
73
  }
46
74
  ```
47
75
 
76
+ ### 开发组件 - 见 Client Toolkit
77
+
48
78
  ---
49
79
 
50
- ## 核心 API
80
+ ## 核心 API - 面向 Agent
81
+
82
+ ### CanRole 组件 (推荐)
83
+
84
+ - **作用**: 条件渲染,只有当角色 id 包含在 `roles` 中时才渲染子内容。
85
+
86
+ ```tsx
87
+ <CanRole roles={['role_admin', 'role_editor']}>
88
+ <button>删除任务</button>
89
+ </CanRole>
90
+ ```
91
+
92
+ ### AbilityContext 提供原子化的权限判断能力
93
+
94
+ - **作用**: 从 React 上下文获取 CASL `Ability` 实例,使用 `ability.can(action, subject)` 做权限判断。
95
+
96
+ ```tsx
97
+ import { useContext } from 'react';
98
+ import { AbilityContext } from '@lark-apaas/auth-sdk';
99
+
100
+ const ability = useContext(AbilityContext);
101
+ const canCreate = ability.can('role_editor', '@role');
102
+ ```
103
+
104
+ ## 核心 API - 面向接入方
51
105
 
52
106
  ### AuthProvider
53
107
 
@@ -63,6 +117,7 @@ function Home() {
63
117
  - `children: React.ReactNode`
64
118
 
65
119
  示例:
120
+
66
121
  ```tsx
67
122
  <AuthProvider config={{
68
123
  enable: true,
@@ -72,37 +127,7 @@ function Home() {
72
127
  </AuthProvider>
73
128
  ```
74
129
 
75
- ### useAuth
76
-
77
- - **作用**: 访问权限状态与方法。
78
- - **返回**:
79
- - `ability: Ability` CASL `Ability` 实例
80
- - `isLoading: boolean` 是否正在加载权限数据
81
- - `error: Error | null` 最近一次加载错误(如果有)
82
- - `fetchPermissions(userId?: string): Promise<void>` 手动拉取权限数据
83
-
84
- ```tsx
85
- const { ability, isLoading, error, fetchPermissions } = useAuth();
86
- ```
87
-
88
- ### useAuthAbility
89
-
90
- - **作用**: 获取 CASL `Ability` 实例,使用 `ability.can(action, subject)` 做任意判断。
91
-
92
- ```tsx
93
- const ability = useAuthAbility();
94
- const canCreate = ability.can('Editor', '@role');
95
- ```
96
-
97
- ### Can 组件
98
-
99
- - **作用**: 条件渲染,只有当 `I` 对 `a` 可操作时才渲染子内容。 `a` 为资源类型,需要保证为 `@role`。
100
-
101
- ```tsx
102
- <Can I="Editor" a="@role">
103
- <button>删除任务</button>
104
- </Can>
105
- ```
130
+ ## 核心 API - 面向 SDK 开发者
106
131
 
107
132
  ### PermissionClient
108
133
 
@@ -141,54 +166,15 @@ updateAbility(ability, { permissions: [{ id: 'p1', name: 'Task Read', sub: 'Task
141
166
  ## 类型与再导出
142
167
 
143
168
  - 从本包导出的类型:`PermissionApiResponse`、`PermissionApiConfig`、`AuthSdkConfig`、`CaslRule`。
144
- - 便捷再导出:`MongoAbility`, `AbilityBuilder`, `AbilityClass`(来自 `@casl/ability`)。
145
169
  - 最终用户代码中仅需要使用到的 API 有:
146
- - `useAuthAbility`: 获取 CASL `Ability` 实例,用于权限判断。
147
- - `Can` 组件: 条件渲染,只有当 `I` `a` 可操作时才渲染子内容。 `a` 为资源类型,需要保证为 `@role`。
148
- - `ROLE_SUBJECT`: 特殊 subject `@role`,用于角色判断。
149
- - `useAuth`: 获取权限接口的状态,配合 `fetchPermissions` 手动拉取权限数据。
150
-
170
+ - `CanRole` 组件: 条件渲染,只有当角色 id 包含在 `roles` 中时才渲染子内容。
171
+ - `AbilityContext`: React 上下文获取 CASL `Ability` 实例,用于权限判断。
172
+ - `ROLE_SUBJECT`: 特殊 subject `@role` 常量,用于角色判断。
151
173
 
152
174
  ## 集成建议与最佳实践
153
175
 
154
- - **直接使用组件**:`Can` 组件是直接使用的主要入口,可以使用 `useAuthAbility` 在特殊场景下手动判断权限。
155
- - **错误处理**:实现 `onError` 上报或提示;`onSuccess` 可做埋点。
156
- - **渲染时机**:根据 `useAuth()` 的 `isLoading`/`error` 渲染 Loading/Error 页,避免闪烁。
157
- - **与路由结合**:页面级的访问控制需要结合路由库(如 `react-router-dom`)和 `useAuthAbility` hook 来自行实现。
158
-
159
- ---
160
-
161
- ## 进阶示例
162
-
163
- ### 菜单按权限过滤
164
-
165
- ```tsx
166
- import { useAuthAbility } from '@lark-apaas/auth-sdk';
167
-
168
- const menus = [
169
- { name: 'Dashboard', path: '/dashboard', p: { action: 'Editor', subject: '@role' } },
170
- { name: 'Users', path: '/users', p: { action: 'Admin', subject: '@role' } },
171
- { name: 'Settings', path: '/settings', p: { action: 'Admin', subject: '@role' } },
172
- ];
173
-
174
- function Nav() {
175
- const ability = useAuthAbility();
176
- return (
177
- <nav>
178
- {menus.map(m => ability.can(m.p.action, m.p.subject) && (
179
- <a key={m.path} href={m.path}>{m.name}</a>
180
- ))}
181
- </nav>
182
- );
183
- }
184
- ```
185
-
186
- ---
187
-
188
- ## 常见问题(FAQ)
189
-
190
- - **如何做角色判断?** 角色被映射为对特殊 subject `@role` 的 action。你可以使用 `ability.can('admin_role', '@role')` 来判断当前用户是否拥有 `admin_role` 角色。
191
- - **如何实现路由守卫?** 您需要结合您使用的路由库(如 `react-router-dom`)和 `useAuthAbility` hook 来手动实现路由守卫。通过在路由渲染前检查权限,然后决定是否渲染组件或重定向。
176
+ - **直接使用组件**:`CanRole` 组件是直接使用的主要入口,可以使用 `AbilityContext` 获取 `Ability` 实例,在特殊场景下手动判断权限。
177
+ - **与路由结合**:页面级的访问控制需要结合路由库(如 `react-router-dom`)和 `AbilityContext` 来自行实现。
192
178
 
193
179
  ---
194
180
 
@@ -11,6 +11,7 @@ export declare const AbilityContext: React.Context<MongoAbility<import("@casl/ab
11
11
  */
12
12
  interface AuthStateContextValue {
13
13
  ability: MongoAbility;
14
+ permissions: string[];
14
15
  isLoading: boolean;
15
16
  error: Error | null;
16
17
  fetchPermissions: (userId?: string) => Promise<void>;
@@ -33,10 +34,9 @@ export interface AuthProviderProps {
33
34
  *
34
35
  * function App() {
35
36
  * return (
36
- * <AuthProvider userId="user-123" config={{
37
+ * <AuthProvider config={{
37
38
  * permissionApi: {
38
- * baseUrl: 'http://localhost:3000',
39
- * endpoint: '/mock-api/users/:userId/permissions'
39
+ * url: '/app/app_xxx/__runtime__/api/v1/permissions/roles',
40
40
  * }
41
41
  * }}>
42
42
  * <YourApp />
@@ -46,6 +46,13 @@ export interface AuthProviderProps {
46
46
  * ```
47
47
  */
48
48
  export declare function AuthProvider({ children, config }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
49
+ /**
50
+ * 获取 Ability 实例
51
+ *
52
+ * @param permissionApiConfig - 权限 API 配置
53
+ * @returns Ability 实例或错误
54
+ */
55
+ export declare function getAbility(permissionApiConfig: AuthSdkConfig['permissionApi']): Promise<Error | MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
49
56
  /**
50
57
  * useAuth Hook - 获取权限数据和加载状态
51
58
  *
@@ -104,6 +111,9 @@ export declare function useAuthAbility(): MongoAbility;
104
111
  * ```
105
112
  */
106
113
  export declare const Can: React.FunctionComponent<import("@casl/react").BoundCanProps<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
114
+ export declare const useCanRole: ({ roles }: {
115
+ roles: string[];
116
+ }) => boolean;
107
117
  /**
108
118
  * CanRole Component - 基于 Ability 实例的角色条件渲染组件
109
119
  *
@@ -113,16 +123,49 @@ export declare const Can: React.FunctionComponent<import("@casl/react").BoundCan
113
123
  *
114
124
  * function MyComponent() {
115
125
  * return (
116
- * <CanRole I="Admin">
126
+ * <CanRole roles={['role_admin', 'role_editor']}>
117
127
  * <TaskList />
118
128
  * </CanRole>
119
129
  * );
120
130
  * }
121
131
  * ```
122
132
  */
123
- export declare const CanRole: ({ children, I, }: {
133
+ export declare function CanRole({ children, roles, }: {
134
+ children: React.ReactNode;
135
+ roles: string[];
136
+ }): import("react/jsx-runtime").JSX.Element | null;
137
+ /**
138
+ * useUserPermissions Hook - 获取当前用户的权限点位列表
139
+ */
140
+ export declare function useUserPermissions(): string[];
141
+ /**
142
+ * useCanPermission Hook - 判断用户是否拥有指定权限点位
143
+ *
144
+ * @param permissions - 需要检查的权限点位列表
145
+ * @param match - 匹配模式:'any' 任一匹配即可,'all' 需要全部匹配
146
+ */
147
+ export declare function useCanPermission(permissions: string[], match?: 'any' | 'all'): boolean;
148
+ /**
149
+ * CanPermission Component - 基于权限点位的条件渲染组件
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * import { CanPermission } from '@lark-apaas/auth-sdk';
154
+ *
155
+ * function MyComponent() {
156
+ * return (
157
+ * <CanPermission permissions={['task:create', 'task:edit']} match="any">
158
+ * <TaskEditor />
159
+ * </CanPermission>
160
+ * );
161
+ * }
162
+ * ```
163
+ */
164
+ export declare function CanPermission({ children, permissions, match, fallback, }: {
124
165
  children: React.ReactNode;
125
- I: string;
126
- }) => import("react/jsx-runtime").JSX.Element;
166
+ permissions: string[];
167
+ match?: 'any' | 'all';
168
+ fallback?: React.ReactNode;
169
+ }): import("react/jsx-runtime").JSX.Element;
127
170
  export {};
128
171
  //# sourceMappingURL=AuthProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;GAEG;AACH,eAAO,MAAM,cAAc,uGAE1B,CAAC;AAEF;;;GAGG;AACH,UAAU,qBAAqB;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAOD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA2DnE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,IAAI,qBAAqB,CAQ/C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAE7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,GAAG,sJAA+C,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO,GAAI,kBAGrB;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,CAAC,EAAE,MAAM,CAAC;CACX,4CAIA,CAAC"}
1
+ {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;GAEG;AACH,eAAO,MAAM,cAAc,uGAE1B,CAAC;AAOF;;;GAGG;AACH,UAAU,qBAAqB;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAOD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA4EnE;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,mBAAmB,EAAE,aAAa,CAAC,eAAe,CAAC,2GAmBpD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,IAAI,qBAAqB,CAQ/C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAE7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,GAAG,sJAA+C,CAAC;AAEhE,eAAO,MAAM,UAAU,GAAa,WAAW;IAAE,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,KAAG,OASpE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,kDAIA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EAAE,EACrB,KAAK,GAAE,KAAK,GAAG,KAAa,GAC3B,OAAO,CAOT;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,WAAW,EACX,KAAa,EACb,QAAe,GAChB,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACtB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,2CAGA"}
@@ -1,26 +1,34 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { Fragment, jsx } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useState } from "react";
3
- import { ROLE_SUBJECT, createAbility, updateAbility } from "./ability-factory.js";
3
+ import { ROLE_SUBJECT, createAbility } from "./ability-factory.js";
4
4
  import { PermissionClient } from "./permission-client.js";
5
5
  import { createContextualCan } from "@casl/react";
6
6
  const AbilityContext = /*#__PURE__*/ createContext(createAbility({
7
7
  permissions: [],
8
8
  roles: []
9
9
  }));
10
+ const PermissionsContext = /*#__PURE__*/ createContext([]);
10
11
  const AuthStateContext = /*#__PURE__*/ createContext(null);
11
12
  function AuthProvider({ children, config }) {
12
- const [ability] = useState(()=>createAbility({}));
13
+ const [ability, setAbility] = useState(()=>createAbility({}));
14
+ const [permissions, setPermissions] = useState([]);
13
15
  const [isLoading, setIsLoading] = useState(false);
14
16
  const [error, setError] = useState(null);
15
- const [client] = useState(()=>new PermissionClient(config?.permissionApi));
17
+ const [client] = useState(()=>config?.permissionApi ? new PermissionClient(config.permissionApi) : null);
16
18
  const fetchPermissions = useCallback(async ()=>{
19
+ if (!client) return;
17
20
  setIsLoading(true);
18
21
  setError(null);
19
22
  try {
20
23
  const data = await client.fetchPermissions();
21
- updateAbility(ability, {
24
+ const newAbility = createAbility({
22
25
  roles: data.roles
23
26
  });
27
+ setAbility(newAbility);
28
+ if (config?.permissionPointsApi) try {
29
+ const userPermissions = await client.fetchUserPermissions(config.permissionPointsApi);
30
+ setPermissions(userPermissions);
31
+ } catch {}
24
32
  config?.onSuccess?.(data);
25
33
  } catch (err) {
26
34
  const error = err instanceof Error ? err : new Error(String(err));
@@ -30,7 +38,6 @@ function AuthProvider({ children, config }) {
30
38
  setIsLoading(false);
31
39
  }
32
40
  }, [
33
- ability,
34
41
  client,
35
42
  config
36
43
  ]);
@@ -42,18 +49,36 @@ function AuthProvider({ children, config }) {
42
49
  ]);
43
50
  const stateContextValue = {
44
51
  ability,
52
+ permissions,
45
53
  isLoading,
46
54
  error,
47
55
  fetchPermissions
48
56
  };
49
57
  return /*#__PURE__*/ jsx(AbilityContext.Provider, {
50
58
  value: ability,
51
- children: /*#__PURE__*/ jsx(AuthStateContext.Provider, {
52
- value: stateContextValue,
53
- children: children
59
+ children: /*#__PURE__*/ jsx(PermissionsContext.Provider, {
60
+ value: permissions,
61
+ children: /*#__PURE__*/ jsx(AuthStateContext.Provider, {
62
+ value: stateContextValue,
63
+ children: children
64
+ })
54
65
  })
55
66
  });
56
67
  }
68
+ async function getAbility(permissionApiConfig) {
69
+ if (!permissionApiConfig) return new Error('permissionApi config is required');
70
+ const client = new PermissionClient(permissionApiConfig);
71
+ try {
72
+ const data = await client.fetchPermissions();
73
+ const ability = createAbility({
74
+ roles: data.roles
75
+ });
76
+ return ability;
77
+ } catch (err) {
78
+ const error = err instanceof Error ? err : new Error(String(err));
79
+ return error;
80
+ }
81
+ }
57
82
  function useAuth() {
58
83
  const context = useContext(AuthStateContext);
59
84
  if (!context) throw new Error('useAuth must be used within an AuthProvider');
@@ -63,9 +88,34 @@ function useAuthAbility() {
63
88
  return useContext(AbilityContext);
64
89
  }
65
90
  const Can = createContextualCan(AbilityContext.Consumer);
66
- const CanRole = ({ children, I })=>/*#__PURE__*/ jsx(Can, {
67
- I: I,
68
- a: ROLE_SUBJECT,
91
+ const useCanRole = function({ roles }) {
92
+ const ability = useContext(AbilityContext);
93
+ const allowed = !roles || 0 === roles.length || roles.length > 0 && roles.some((role)=>ability.can(role, ROLE_SUBJECT));
94
+ return !!allowed;
95
+ };
96
+ function CanRole({ children, roles }) {
97
+ const allowed = useCanRole({
98
+ roles
99
+ });
100
+ return allowed ? /*#__PURE__*/ jsx(Fragment, {
101
+ children: children
102
+ }) : null;
103
+ }
104
+ function useUserPermissions() {
105
+ return useContext(PermissionsContext);
106
+ }
107
+ function useCanPermission(permissions, match = 'any') {
108
+ const userPermissions = useContext(PermissionsContext);
109
+ if (!permissions || 0 === permissions.length) return true;
110
+ const permSet = new Set(userPermissions);
111
+ return 'all' === match ? permissions.every((p)=>permSet.has(p)) : permissions.some((p)=>permSet.has(p));
112
+ }
113
+ function CanPermission({ children, permissions, match = 'any', fallback = null }) {
114
+ const allowed = useCanPermission(permissions, match);
115
+ return allowed ? /*#__PURE__*/ jsx(Fragment, {
69
116
  children: children
117
+ }) : /*#__PURE__*/ jsx(Fragment, {
118
+ children: fallback
70
119
  });
71
- export { AbilityContext, AuthProvider, Can, CanRole, useAuth, useAuthAbility };
120
+ }
121
+ export { AbilityContext, AuthProvider, Can, CanPermission, CanRole, getAbility, useAuth, useAuthAbility, useCanPermission, useCanRole, useUserPermissions };
package/lib/index.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  export type { PermissionApiResponse, PermissionApiConfig, AuthSdkConfig, } from './types';
8
8
  export { ROLE_SUBJECT } from './ability-factory';
9
9
  export { PermissionClient } from './permission-client';
10
- export { AuthProvider, useAuth, useAuthAbility, Can, AbilityContext, } from './AuthProvider';
10
+ export { AuthProvider, useAuth, CanRole, AbilityContext, useUserPermissions, useCanPermission, CanPermission, } from './AuthProvider';
11
+ export { CanPermission as CanPerm } from './AuthProvider';
11
12
  export type { AuthProviderProps } from './AuthProvider';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EACL,YAAY,EACZ,OAAO,EACP,cAAc,EACd,GAAG,EACH,cAAc,GACf,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { ROLE_SUBJECT } from "./ability-factory.js";
2
2
  import { PermissionClient } from "./permission-client.js";
3
- import { AbilityContext, AuthProvider, Can, useAuth, useAuthAbility } from "./AuthProvider.js";
4
- export { AbilityContext, AuthProvider, Can, PermissionClient, ROLE_SUBJECT, useAuth, useAuthAbility };
3
+ import { AbilityContext, AuthProvider, CanPermission, CanRole, useAuth, useCanPermission, useUserPermissions } from "./AuthProvider.js";
4
+ export { AbilityContext, AuthProvider, CanPermission as CanPerm, CanPermission, CanRole, PermissionClient, ROLE_SUBJECT, useAuth, useCanPermission, useUserPermissions };
@@ -4,11 +4,15 @@ import type { PermissionApiConfig, PermissionApiResponse } from './types';
4
4
  */
5
5
  export declare class PermissionClient {
6
6
  private config;
7
- constructor(config?: PermissionApiConfig);
7
+ constructor(config: PermissionApiConfig);
8
8
  /**
9
9
  * 获取用户权限数据
10
10
  */
11
11
  fetchPermissions(): Promise<PermissionApiResponse>;
12
+ /**
13
+ * 获取用户权限点位列表
14
+ */
15
+ fetchUserPermissions(config: PermissionApiConfig): Promise<string[]>;
12
16
  /**
13
17
  * 更新配置
14
18
  */
@@ -1 +1 @@
1
- {"version":3,"file":"permission-client.d.ts","sourceRoot":"","sources":["../src/permission-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAU1E;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,CAAC,EAAE,mBAAmB;IAOxC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAyDxD;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;IAOxD;;OAEG;IACH,SAAS,IAAI,mBAAmB;CAGjC"}
1
+ {"version":3,"file":"permission-client.d.ts","sourceRoot":"","sources":["../src/permission-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG1E;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,EAAE,mBAAmB;IAIvC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAoDxD;;OAEG;IACG,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA4C1E;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;IAOxD;;OAEG;IACH,SAAS,IAAI,mBAAmB;CAGjC"}
@@ -1,22 +1,14 @@
1
- const DEFAULT_CONFIG = {
2
- baseUrl: '',
3
- timeout: 5000
4
- };
1
+ import { slardar } from "@lark-apaas/internal-slardar";
5
2
  class PermissionClient {
6
3
  constructor(config){
7
- this.config = {
8
- ...DEFAULT_CONFIG,
9
- ...config
10
- };
4
+ this.config = config;
11
5
  }
12
6
  async fetchPermissions() {
13
- const { timeout = DEFAULT_CONFIG.timeout, headers = {} } = this.config;
14
- const { appId } = window;
15
- if (!appId) throw new Error('appId is required');
16
- const url = `/spark/app/${appId}/runtime/api/v1/permissions/roles`;
7
+ const { url, timeout = 5000, headers = {} } = this.config;
17
8
  const requestHeaders = {
9
+ ...headers,
18
10
  'Content-Type': 'application/json',
19
- ...headers
11
+ 'X-Suda-Csrf-Token': window.csrfToken || ''
20
12
  };
21
13
  const controller = new AbortController();
22
14
  const timeoutId = setTimeout(()=>controller.abort(), timeout);
@@ -36,7 +28,43 @@ class PermissionClient {
36
28
  };
37
29
  } catch (error) {
38
30
  clearTimeout(timeoutId);
39
- if (error instanceof Error && 'AbortError' === error.name) throw new Error(`Permission API request timeout after ${timeout}ms`);
31
+ if (error instanceof Error && 'AbortError' === error.name) {
32
+ slardar.captureException(error, {
33
+ source: 'auth-sdk',
34
+ type: 'timeout'
35
+ });
36
+ throw new Error(`Permission API request timeout after ${timeout}ms`);
37
+ }
38
+ slardar.captureException(error, {
39
+ source: 'auth-sdk',
40
+ type: 'fetch'
41
+ });
42
+ throw error;
43
+ }
44
+ }
45
+ async fetchUserPermissions(config) {
46
+ const { url, timeout = 5000, headers = {} } = config;
47
+ const requestHeaders = {
48
+ ...headers,
49
+ 'Content-Type': 'application/json',
50
+ 'X-Suda-Csrf-Token': window.csrfToken || ''
51
+ };
52
+ const controller = new AbortController();
53
+ const timeoutId = setTimeout(()=>controller.abort(), timeout);
54
+ try {
55
+ const response = await fetch(url, {
56
+ method: 'POST',
57
+ headers: requestHeaders,
58
+ signal: controller.signal,
59
+ credentials: 'include'
60
+ });
61
+ clearTimeout(timeoutId);
62
+ if (!response.ok) throw new Error(`Permissions API returned ${response.status}: ${response.statusText}`);
63
+ const data = await response.json();
64
+ return data.data?.permissions || [];
65
+ } catch (error) {
66
+ clearTimeout(timeoutId);
67
+ if (error instanceof Error && 'AbortError' === error.name) throw new Error(`Permissions API request timeout after ${timeout}ms`);
40
68
  throw error;
41
69
  }
42
70
  }
package/lib/types.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ declare global {
2
+ interface Window {
3
+ appId?: string;
4
+ csrfToken?: string;
5
+ userID?: string;
6
+ }
7
+ }
1
8
  /**
2
9
  * 权限操作类型
3
10
  */
@@ -28,6 +35,8 @@ export interface UserRole {
28
35
  */
29
36
  export interface PermissionApiResponse {
30
37
  roles: string[];
38
+ /** 用户的有效权限点位列表 */
39
+ permissions?: string[];
31
40
  fetchedAt?: string | Date;
32
41
  }
33
42
  /**
@@ -35,10 +44,9 @@ export interface PermissionApiResponse {
35
44
  */
36
45
  export interface PermissionApiConfig {
37
46
  /**
38
- * API 基础 URL
39
- * @default ''
47
+ * 权限接口完整 URL,由上层注入,不在 SDK 内部硬编码
40
48
  */
41
- baseUrl?: string;
49
+ url: string;
42
50
  /**
43
51
  * 请求超时时间(毫秒)
44
52
  * @default 5000
@@ -57,6 +65,11 @@ export interface AuthSdkConfig {
57
65
  * 权限 API 配置
58
66
  */
59
67
  permissionApi?: PermissionApiConfig;
68
+ /**
69
+ * 权限点位 API 配置(可选)
70
+ * 配置后 AuthProvider 会额外请求用户的有效权限点位
71
+ */
72
+ permissionPointsApi?: PermissionApiConfig;
60
73
  /**
61
74
  * 是否在初始化时使用获取权限
62
75
  * @default false
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,GACd,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,MAAM,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAEpC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;CACF;AAED;;GAEG;AACH,MAAM,MAAM,MAAM,GACd,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,MAAM,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kBAAkB;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAEpC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAE1C;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/auth-sdk",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.1.0-alpha.71",
4
4
  "description": "基于 CASL 的前端鉴权 SDK",
5
5
  "types": "./lib/index.d.ts",
6
6
  "main": "./lib/index.js",
@@ -15,11 +15,13 @@
15
15
  "scripts": {
16
16
  "build": "rslib build",
17
17
  "dev": "rslib build --watch",
18
- "type-check": "tsc --noEmit"
18
+ "type-check": "tsc --noEmit",
19
+ "prepublishOnly": "npm run build"
19
20
  },
20
21
  "dependencies": {
21
- "@casl/ability": "^6.7.1",
22
- "@casl/react": "^4.0.0"
22
+ "@casl/ability": "^6.7.5",
23
+ "@casl/react": "^5.0.0",
24
+ "@lark-apaas/internal-slardar": "^0.0.3"
23
25
  },
24
26
  "devDependencies": {
25
27
  "@rsbuild/core": "~1.4.13",