@lark-apaas/auth-sdk 0.1.0-alpha.0 → 0.1.0-alpha.4
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 +30 -199
- package/lib/AuthProvider.d.ts +29 -59
- package/lib/AuthProvider.d.ts.map +1 -1
- package/lib/AuthProvider.js +17 -86
- package/lib/ability-factory.d.ts +3 -3
- package/lib/ability-factory.d.ts.map +1 -1
- package/lib/ability-factory.js +2 -2
- package/lib/index.d.ts +4 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -5
- package/lib/permission-client.d.ts +1 -2
- package/lib/permission-client.d.ts.map +1 -1
- package/lib/permission-client.js +10 -13
- package/lib/types.d.ts +3 -24
- package/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/lib/CanRoute.d.ts +0 -42
- package/lib/CanRoute.d.ts.map +0 -1
- package/lib/CanRoute.js +0 -27
- package/lib/RouteGuard.d.ts +0 -42
- package/lib/RouteGuard.d.ts.map +0 -1
- package/lib/RouteGuard.js +0 -27
package/README.md
CHANGED
|
@@ -14,18 +14,16 @@ yarn add @lark-apaas/auth-sdk
|
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
16
|
import React from 'react';
|
|
17
|
-
import { AuthProvider, Can,
|
|
17
|
+
import { AuthProvider, Can, useAuthAbility } from '@lark-apaas/auth-sdk';
|
|
18
18
|
|
|
19
19
|
export default function App() {
|
|
20
20
|
return (
|
|
21
21
|
<AuthProvider
|
|
22
22
|
config={{
|
|
23
23
|
permissionApi: {
|
|
24
|
-
|
|
25
|
-
endpoint: '/api/users/:userId/permissions',
|
|
26
|
-
// 可选:apiToken、timeout、headers、withCredentials
|
|
24
|
+
timeout: 5000,
|
|
27
25
|
},
|
|
28
|
-
|
|
26
|
+
enable: true,
|
|
29
27
|
onError: (e) => console.error(e),
|
|
30
28
|
}}
|
|
31
29
|
>
|
|
@@ -35,13 +33,13 @@ export default function App() {
|
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
function Home() {
|
|
38
|
-
const ability =
|
|
36
|
+
const ability = useAuthAbility();
|
|
39
37
|
return (
|
|
40
38
|
<div>
|
|
41
|
-
<Can I="
|
|
39
|
+
<Can I="admin" a="@role">
|
|
42
40
|
<div>可见的仪表盘</div>
|
|
43
41
|
</Can>
|
|
44
|
-
<button disabled={ability.cannot('
|
|
42
|
+
<button disabled={ability.cannot('reader', '@role')}>创建任务</button>
|
|
45
43
|
</div>
|
|
46
44
|
);
|
|
47
45
|
}
|
|
@@ -55,24 +53,18 @@ function Home() {
|
|
|
55
53
|
- **作用**: 提供 `Ability` 与权限状态上下文,自动/手动拉取权限。
|
|
56
54
|
- **Props**:
|
|
57
55
|
- `config?: AuthSdkConfig`
|
|
58
|
-
- `noPermissionPath?: string` 无权限时的导航路径
|
|
59
56
|
- `permissionApi?: PermissionApiConfig` 拉取权限接口配置
|
|
60
|
-
- `baseUrl?: string`
|
|
61
|
-
- `endpoint?: string`,支持 `:userId` 占位
|
|
62
57
|
- `timeout?: number`,默认 5000ms
|
|
63
|
-
- `headers?: Record<string,string>`
|
|
64
|
-
|
|
65
|
-
- `withCredentials?: boolean`,默认 true
|
|
66
|
-
- `autoFetch?: boolean` 是否初始化自动拉取
|
|
58
|
+
- `headers?: Record<string,string>` 自定义请求头
|
|
59
|
+
- `enable?: boolean` 是否启用权限 SDK,默认 false
|
|
67
60
|
- `onError?: (error: Error) => void`
|
|
68
61
|
- `onSuccess?: (data: PermissionApiResponse) => void`
|
|
69
62
|
- `children: React.ReactNode`
|
|
70
63
|
|
|
71
64
|
示例:
|
|
72
65
|
```tsx
|
|
73
|
-
<AuthProvider
|
|
74
|
-
|
|
75
|
-
autoFetch: true,
|
|
66
|
+
<AuthProvider config={{
|
|
67
|
+
enable: true,
|
|
76
68
|
}}>
|
|
77
69
|
<App />
|
|
78
70
|
{/* Can/Hook 在其子树中生效 */}
|
|
@@ -82,121 +74,30 @@ function Home() {
|
|
|
82
74
|
### useAuth
|
|
83
75
|
- **作用**: 访问权限状态与方法。
|
|
84
76
|
- **返回**:
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
87
|
-
- `
|
|
88
|
-
- `
|
|
89
|
-
- `isLoading: boolean`
|
|
90
|
-
- `error: Error | null`
|
|
91
|
-
- `fetchPermissions(userId?: string): Promise<void>` 手动拉取
|
|
92
|
-
- `refetch(): Promise<void>` 按当前 `userId` 重新拉取
|
|
77
|
+
- `ability: Ability` CASL `Ability` 实例
|
|
78
|
+
- `isLoading: boolean` 是否正在加载权限数据
|
|
79
|
+
- `error: Error | null` 最近一次加载错误(如果有)
|
|
80
|
+
- `fetchPermissions(userId?: string): Promise<void>` 手动拉取权限数据
|
|
93
81
|
|
|
94
82
|
```tsx
|
|
95
|
-
const {
|
|
83
|
+
const { ability, isLoading, error, fetchPermissions } = useAuth();
|
|
96
84
|
```
|
|
97
85
|
|
|
98
|
-
###
|
|
86
|
+
### useAuthAbility
|
|
99
87
|
- **作用**: 获取 CASL `Ability` 实例,使用 `ability.can(action, subject)` 做任意判断。
|
|
100
88
|
|
|
101
89
|
```tsx
|
|
102
|
-
const ability =
|
|
103
|
-
const canCreate = ability.can('
|
|
90
|
+
const ability = useAuthAbility();
|
|
91
|
+
const canCreate = ability.can('Editor', '@role');
|
|
104
92
|
```
|
|
105
93
|
|
|
106
94
|
### Can 组件(来自 @casl/react 的 Contextual Can)
|
|
107
95
|
- **作用**: 条件渲染,只有当 `I` 对 `a` 可操作时才渲染子内容;也支持 render prop。
|
|
108
96
|
|
|
109
97
|
```tsx
|
|
110
|
-
<Can I="
|
|
98
|
+
<Can I="Editor" a="@role">
|
|
111
99
|
<button>删除任务</button>
|
|
112
100
|
</Can>
|
|
113
|
-
|
|
114
|
-
<Can I="download" a="Report">{(allowed) => (
|
|
115
|
-
<button disabled={!allowed}>下载报表</button>
|
|
116
|
-
)}</Can>
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### 批量权限判断:useCanPermission / CanPermission
|
|
120
|
-
- `useCanPermission({ permissions, or })`:一次检查多个权限;`or=true` 表示任一满足即可,否则默认全部需要满足。
|
|
121
|
-
- `CanPermission`:同上但以组件方式使用,支持 children 或 render prop。
|
|
122
|
-
|
|
123
|
-
```tsx
|
|
124
|
-
const allowed = useCanPermission({
|
|
125
|
-
permissions: [
|
|
126
|
-
{ action: 'read', subject: 'Report' },
|
|
127
|
-
{ action: 'download', subject: 'Report' },
|
|
128
|
-
],
|
|
129
|
-
or: false,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
<CanPermission
|
|
133
|
-
permissions={[{ action: 'manage', subject: 'Task' }, { action: 'delete', subject: 'Task' }]}>
|
|
134
|
-
<BulkActions />
|
|
135
|
-
</CanPermission>
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### 角色判断:useCanRole / CanRole
|
|
139
|
-
- 角色被映射为特殊 subject `@role` 下的 action。
|
|
140
|
-
- `useCanRole({ roles, and })`:是否拥有指定多个角色;`and=true` 表示需要同时具备。
|
|
141
|
-
- `CanRole`:以组件形式使用。
|
|
142
|
-
|
|
143
|
-
```tsx
|
|
144
|
-
const isAdmin = useCanRole({ roles: ['admin_role'] });
|
|
145
|
-
|
|
146
|
-
<CanRole roles={[ 'admin_role', 'ops_role' ]} and>
|
|
147
|
-
<DangerZone />
|
|
148
|
-
</CanRole>
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 路由守卫:CanRoute
|
|
152
|
-
- **作用**: 路由级别的权限守卫,支持权限和角色双重检查,无权限时可自动重定向。
|
|
153
|
-
- **Props**:
|
|
154
|
-
- `permissions?: Array<{ action: string; subject: string }>` 需要检查的权限列表
|
|
155
|
-
- `roles?: string[]` 需要检查的角色列表
|
|
156
|
-
- `children: React.ReactNode` 受保护的内容
|
|
157
|
-
- `fallback?: React.ReactNode` 无权限时渲染的内容(优先级最高)
|
|
158
|
-
- `redirectTo?: string` 无权限时重定向的路径(优先级次之)
|
|
159
|
-
- 如果既没有 `fallback` 也没有 `redirectTo`,会尝试使用 `AuthProvider` 配置的 `noPermissionPath`
|
|
160
|
-
- 如果都没有配置,则返回 `null`
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
import { CanRoute } from '@lark-apaas/auth-sdk';
|
|
164
|
-
import { Routes, Route } from 'react-router-dom';
|
|
165
|
-
|
|
166
|
-
// 基础用法:权限检查
|
|
167
|
-
<CanRoute permissions={[{ action: 'read', subject: 'Dashboard' }]}>
|
|
168
|
-
<Dashboard />
|
|
169
|
-
</CanRoute>
|
|
170
|
-
|
|
171
|
-
// 角色检查
|
|
172
|
-
<CanRoute roles={['admin', 'manager']}>
|
|
173
|
-
<AdminPanel />
|
|
174
|
-
</CanRoute>
|
|
175
|
-
|
|
176
|
-
// 自定义重定向
|
|
177
|
-
<CanRoute
|
|
178
|
-
permissions={[{ action: 'manage', subject: 'User' }]}
|
|
179
|
-
redirectTo="/unauthorized"
|
|
180
|
-
>
|
|
181
|
-
<UserManagement />
|
|
182
|
-
</CanRoute>
|
|
183
|
-
|
|
184
|
-
// 自定义 fallback 内容
|
|
185
|
-
<CanRoute
|
|
186
|
-
roles={['admin']}
|
|
187
|
-
fallback={<div>您没有管理员权限</div>}
|
|
188
|
-
>
|
|
189
|
-
<AdminSettings />
|
|
190
|
-
</CanRoute>
|
|
191
|
-
|
|
192
|
-
// 在路由中使用
|
|
193
|
-
<Routes>
|
|
194
|
-
<Route path="/admin" element={
|
|
195
|
-
<CanRoute permissions={[{ action: 'manage', subject: 'Task' }]}>
|
|
196
|
-
<AdminPage />
|
|
197
|
-
</CanRoute>
|
|
198
|
-
} />
|
|
199
|
-
</Routes>
|
|
200
101
|
```
|
|
201
102
|
|
|
202
103
|
### PermissionClient
|
|
@@ -232,20 +133,16 @@ updateAbility(ability, { permissions: [{ id: 'p1', name: 'Task Read', sub: 'Task
|
|
|
232
133
|
---
|
|
233
134
|
|
|
234
135
|
## 类型与再导出
|
|
235
|
-
- 从本包导出的类型:`
|
|
236
|
-
- 便捷再导出:`
|
|
136
|
+
- 从本包导出的类型:`PermissionApiResponse`、`PermissionApiConfig`、`AuthSdkConfig`、`CaslRule`。
|
|
137
|
+
- 便捷再导出:`MongoAbility`, `AbilityBuilder`, `AbilityClass`(来自 `@casl/ability`)。
|
|
237
138
|
|
|
238
139
|
---
|
|
239
140
|
|
|
240
141
|
## 集成建议与最佳实践
|
|
241
|
-
- **权限接口返回**:`{
|
|
142
|
+
- **权限接口返回**:`{ role_ids: string[] }`。
|
|
242
143
|
- **错误处理**:实现 `onError` 上报或提示;`onSuccess` 可做埋点。
|
|
243
144
|
- **渲染时机**:根据 `useAuth()` 的 `isLoading`/`error` 渲染 Loading/Error 页,避免闪烁。
|
|
244
|
-
-
|
|
245
|
-
- **路由守卫最佳实践**:
|
|
246
|
-
- 优先使用 `fallback` 属性展示友好的无权限提示
|
|
247
|
-
- 使用 `redirectTo` 重定向到专门的错误页面
|
|
248
|
-
- 在 `AuthProvider` 中配置全局的 `noPermissionPath` 作为兜底
|
|
145
|
+
- **与路由结合**:页面级的访问控制需要结合路由库(如 `react-router-dom`)和 `useAuthAbility` hook 来自行实现。
|
|
249
146
|
|
|
250
147
|
---
|
|
251
148
|
|
|
@@ -253,16 +150,16 @@ updateAbility(ability, { permissions: [{ id: 'p1', name: 'Task Read', sub: 'Task
|
|
|
253
150
|
|
|
254
151
|
### 菜单按权限过滤
|
|
255
152
|
```tsx
|
|
256
|
-
import {
|
|
153
|
+
import { useAuthAbility } from '@lark-apaas/auth-sdk';
|
|
257
154
|
|
|
258
155
|
const menus = [
|
|
259
|
-
{ name: 'Dashboard', path: '/dashboard', p: { action: '
|
|
260
|
-
{ name: 'Users', path: '/users', p: { action: '
|
|
261
|
-
{ name: 'Settings', path: '/settings', p: { action: '
|
|
156
|
+
{ name: 'Dashboard', path: '/dashboard', p: { action: 'Editor', subject: '@role' } },
|
|
157
|
+
{ name: 'Users', path: '/users', p: { action: 'Admin', subject: '@role' } },
|
|
158
|
+
{ name: 'Settings', path: '/settings', p: { action: 'Admin', subject: '@role' } },
|
|
262
159
|
];
|
|
263
160
|
|
|
264
161
|
function Nav() {
|
|
265
|
-
const ability =
|
|
162
|
+
const ability = useAuthAbility();
|
|
266
163
|
return (
|
|
267
164
|
<nav>
|
|
268
165
|
{menus.map(m => ability.can(m.p.action, m.p.subject) && (
|
|
@@ -273,79 +170,13 @@ function Nav() {
|
|
|
273
170
|
}
|
|
274
171
|
```
|
|
275
172
|
|
|
276
|
-
### 批量操作区
|
|
277
|
-
```tsx
|
|
278
|
-
import { BatchCan } from '@lark-apaas/auth-sdk';
|
|
279
|
-
|
|
280
|
-
<BatchCan permissions={[{ action: 'delete', subject: 'Task' }, { action: 'manage', subject: 'Task' }]}>
|
|
281
|
-
<div className="bulk-actions">
|
|
282
|
-
<button>批量删除</button>
|
|
283
|
-
<button>导出全部</button>
|
|
284
|
-
</div>
|
|
285
|
-
</BatchCan>
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### 路由级权限控制
|
|
289
|
-
```tsx
|
|
290
|
-
import { CanRoute } from '@lark-apaas/auth-sdk';
|
|
291
|
-
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
292
|
-
|
|
293
|
-
function App() {
|
|
294
|
-
return (
|
|
295
|
-
<Routes>
|
|
296
|
-
{/* 公开路由 */}
|
|
297
|
-
<Route path="/" element={<HomePage />} />
|
|
298
|
-
<Route path="/login" element={<LoginPage />} />
|
|
299
|
-
|
|
300
|
-
{/* 受保护的路由 */}
|
|
301
|
-
<Route path="/dashboard" element={
|
|
302
|
-
<CanRoute permissions={[{ action: 'read', subject: 'Dashboard' }]}>
|
|
303
|
-
<Dashboard />
|
|
304
|
-
</CanRoute>
|
|
305
|
-
} />
|
|
306
|
-
|
|
307
|
-
{/* 管理员路由 */}
|
|
308
|
-
<Route path="/admin" element={
|
|
309
|
-
<CanRoute
|
|
310
|
-
roles={['admin']}
|
|
311
|
-
fallback={<Navigate to="/unauthorized" replace />}
|
|
312
|
-
>
|
|
313
|
-
<AdminPanel />
|
|
314
|
-
</CanRoute>
|
|
315
|
-
} />
|
|
316
|
-
|
|
317
|
-
{/* 多权限检查 */}
|
|
318
|
-
<Route path="/reports" element={
|
|
319
|
-
<CanRoute
|
|
320
|
-
permissions={[
|
|
321
|
-
{ action: 'read', subject: 'Report' },
|
|
322
|
-
{ action: 'export', subject: 'Report' }
|
|
323
|
-
]}
|
|
324
|
-
redirectTo="/no-access"
|
|
325
|
-
>
|
|
326
|
-
<ReportsPage />
|
|
327
|
-
</CanRoute>
|
|
328
|
-
} />
|
|
329
|
-
|
|
330
|
-
{/* 错误页面 */}
|
|
331
|
-
<Route path="/unauthorized" element={<UnauthorizedPage />} />
|
|
332
|
-
<Route path="/no-access" element={<NoAccessPage />} />
|
|
333
|
-
</Routes>
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
```
|
|
337
|
-
|
|
338
173
|
---
|
|
339
174
|
|
|
340
175
|
## 常见问题(FAQ)
|
|
341
|
-
- **如何做角色判断?**
|
|
342
|
-
-
|
|
343
|
-
- **CanRoute 的重定向优先级?** `fallback` > `redirectTo` > `noPermissionPath`(AuthProvider 配置)> `null`。
|
|
344
|
-
- **CanRoute 需要 react-router-dom 依赖吗?** 是的,因为使用了 `Navigate` 和 `useLocation`,请确保项目中已安装。
|
|
176
|
+
- **如何做角色判断?** 角色被映射为对特殊 subject `@role` 的 action。你可以使用 `ability.can('admin_role', '@role')` 来判断当前用户是否拥有 `admin_role` 角色。
|
|
177
|
+
- **如何实现路由守卫?** 由于 `CanRoute` 组件已移除,您需要结合您使用的路由库(如 `react-router-dom`)和 `useAuthAbility` hook 来手动实现路由守卫。通过在路由渲染前检查权限,然后决定是否渲染组件或重定向。
|
|
345
178
|
|
|
346
179
|
---
|
|
347
180
|
|
|
348
181
|
## 许可
|
|
349
182
|
MIT
|
|
350
|
-
|
|
351
|
-
|
package/lib/AuthProvider.d.ts
CHANGED
|
@@ -1,59 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import type { AuthSdkConfig
|
|
2
|
+
import { MongoAbility } from '@casl/ability';
|
|
3
|
+
import type { AuthSdkConfig } from './types';
|
|
4
4
|
/**
|
|
5
5
|
* Ability Context - 用于在组件树中共享 Ability 实例
|
|
6
6
|
*/
|
|
7
|
-
export declare const AbilityContext: React.Context<
|
|
8
|
-
/** 权限点位鉴权相关的 Hooks 和组件 */
|
|
9
|
-
/**
|
|
10
|
-
* Can 组件 - 基于 Context 的条件渲染组件
|
|
11
|
-
* 使用 @casl/react 的 createContextualCan 创建,复用 CASL 的官方实现
|
|
12
|
-
*/
|
|
13
|
-
export declare const Can: React.FunctionComponent<import("@casl/react").BoundCanProps<Ability<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
|
|
14
|
-
export interface UseCanPermissionProps {
|
|
15
|
-
permissions?: Array<{
|
|
16
|
-
action: string;
|
|
17
|
-
subject: string;
|
|
18
|
-
}>;
|
|
19
|
-
or?: boolean;
|
|
20
|
-
}
|
|
21
|
-
export interface CanPermissionProps extends UseCanPermissionProps {
|
|
22
|
-
children: React.ReactNode | ((allowed: boolean) => React.ReactNode);
|
|
23
|
-
}
|
|
24
|
-
export declare const useCanPermission: ({ permissions, or }: UseCanPermissionProps) => boolean;
|
|
25
|
-
export declare function CanPermission({ permissions, children, or }: {
|
|
26
|
-
permissions: Array<{
|
|
27
|
-
action: string;
|
|
28
|
-
subject: string;
|
|
29
|
-
}>;
|
|
30
|
-
children: React.ReactNode | ((allowed: boolean) => React.ReactNode);
|
|
31
|
-
or?: boolean;
|
|
32
|
-
}): import("react/jsx-runtime").JSX.Element | null;
|
|
33
|
-
/** 角色鉴权相关的 Hooks 和组件 */
|
|
34
|
-
export interface UseCanRoleProps {
|
|
35
|
-
roles?: string[];
|
|
36
|
-
and?: boolean;
|
|
37
|
-
}
|
|
38
|
-
export declare const useCanRole: ({ roles, and }: UseCanRoleProps) => boolean;
|
|
39
|
-
export interface CanRoleProps extends UseCanRoleProps {
|
|
40
|
-
children: React.ReactNode | ((allowed: boolean) => React.ReactNode);
|
|
41
|
-
}
|
|
42
|
-
export declare const CanRole: ({ roles, and, children }: CanRoleProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export declare const AbilityContext: React.Context<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
|
|
43
8
|
/**
|
|
44
9
|
* Auth 状态 Context 类型定义
|
|
45
10
|
* 存储权限数据和加载状态
|
|
46
11
|
*/
|
|
47
12
|
interface AuthStateContextValue {
|
|
48
|
-
|
|
49
|
-
permissions: Permission[];
|
|
50
|
-
roles: string[];
|
|
51
|
-
userId: string | null;
|
|
52
|
-
setUserId: (userId: string) => void;
|
|
13
|
+
ability: MongoAbility;
|
|
53
14
|
isLoading: boolean;
|
|
54
15
|
error: Error | null;
|
|
55
16
|
fetchPermissions: (userId?: string) => Promise<void>;
|
|
56
|
-
refetch: () => Promise<void>;
|
|
57
17
|
}
|
|
58
18
|
/**
|
|
59
19
|
* Auth Provider Props
|
|
@@ -61,7 +21,6 @@ interface AuthStateContextValue {
|
|
|
61
21
|
export interface AuthProviderProps {
|
|
62
22
|
children: React.ReactNode;
|
|
63
23
|
config?: AuthSdkConfig;
|
|
64
|
-
userId?: string;
|
|
65
24
|
}
|
|
66
25
|
/**
|
|
67
26
|
* Auth Provider 组件
|
|
@@ -110,14 +69,14 @@ export declare function AuthProvider({ children, config }: AuthProviderProps): i
|
|
|
110
69
|
*/
|
|
111
70
|
export declare function useAuth(): AuthStateContextValue;
|
|
112
71
|
/**
|
|
113
|
-
*
|
|
72
|
+
* useAuthAbility Hook - 获取 Ability 实例
|
|
114
73
|
*
|
|
115
74
|
* @example
|
|
116
75
|
* ```tsx
|
|
117
|
-
* import {
|
|
76
|
+
* import { useAuthAbility } from '@lark-apaas/auth-sdk';
|
|
118
77
|
*
|
|
119
78
|
* function MyComponent() {
|
|
120
|
-
* const ability =
|
|
79
|
+
* const ability = useAuthAbility();
|
|
121
80
|
*
|
|
122
81
|
* return (
|
|
123
82
|
* <button disabled={ability.cannot('create', 'Task')}>
|
|
@@ -127,32 +86,43 @@ export declare function useAuth(): AuthStateContextValue;
|
|
|
127
86
|
* }
|
|
128
87
|
* ```
|
|
129
88
|
*/
|
|
130
|
-
export declare function
|
|
89
|
+
export declare function useAuthAbility(): MongoAbility;
|
|
131
90
|
/**
|
|
132
|
-
*
|
|
91
|
+
* Can Component - 基于 Ability 实例的条件渲染组件
|
|
133
92
|
*
|
|
134
93
|
* @example
|
|
135
94
|
* ```tsx
|
|
95
|
+
* import { Can } from '@lark-apaas/auth-sdk';
|
|
96
|
+
*
|
|
136
97
|
* function MyComponent() {
|
|
137
|
-
*
|
|
138
|
-
*
|
|
98
|
+
* return (
|
|
99
|
+
* <Can I="Admin" a="@role">
|
|
100
|
+
* <TaskList />
|
|
101
|
+
* </Can>
|
|
102
|
+
* );
|
|
139
103
|
* }
|
|
140
104
|
* ```
|
|
141
105
|
*/
|
|
142
|
-
export declare
|
|
106
|
+
export declare const Can: React.FunctionComponent<import("@casl/react").BoundCanProps<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
|
|
143
107
|
/**
|
|
144
|
-
*
|
|
108
|
+
* CanRole Component - 基于 Ability 实例的角色条件渲染组件
|
|
145
109
|
*
|
|
146
110
|
* @example
|
|
147
111
|
* ```tsx
|
|
112
|
+
* import { CanRole } from '@lark-apaas/auth-sdk';
|
|
113
|
+
*
|
|
148
114
|
* function MyComponent() {
|
|
149
|
-
*
|
|
150
|
-
*
|
|
115
|
+
* return (
|
|
116
|
+
* <CanRole I="Admin">
|
|
117
|
+
* <TaskList />
|
|
118
|
+
* </CanRole>
|
|
119
|
+
* );
|
|
151
120
|
* }
|
|
152
121
|
* ```
|
|
153
122
|
*/
|
|
154
|
-
export declare
|
|
155
|
-
|
|
156
|
-
|
|
123
|
+
export declare const CanRole: ({ children, I, }: {
|
|
124
|
+
children: React.ReactNode;
|
|
125
|
+
I: string;
|
|
126
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
157
127
|
export {};
|
|
158
128
|
//# sourceMappingURL=AuthProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
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"}
|
package/lib/AuthProvider.js
CHANGED
|
@@ -1,70 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
|
3
|
-
import { createContextualCan } from "@casl/react";
|
|
4
3
|
import { ROLE_SUBJECT, createAbility, updateAbility } from "./ability-factory.js";
|
|
5
4
|
import { PermissionClient } from "./permission-client.js";
|
|
5
|
+
import { createContextualCan } from "@casl/react";
|
|
6
6
|
const AbilityContext = /*#__PURE__*/ createContext(createAbility({
|
|
7
7
|
permissions: [],
|
|
8
8
|
roles: []
|
|
9
9
|
}));
|
|
10
|
-
const Can = createContextualCan(AbilityContext.Consumer);
|
|
11
|
-
const useCanPermission = function({ permissions, or }) {
|
|
12
|
-
const ability = useAbility();
|
|
13
|
-
let allowed = false;
|
|
14
|
-
allowed = or ? !permissions || !permissions.length || permissions.length > 0 && permissions.some(({ action, subject })=>ability.can(action, subject)) : !permissions || !permissions.length || permissions.length > 0 && permissions.every(({ action, subject })=>ability.can(action, subject));
|
|
15
|
-
return !!allowed;
|
|
16
|
-
};
|
|
17
|
-
function CanPermission({ permissions, children, or }) {
|
|
18
|
-
const allowed = useCanPermission({
|
|
19
|
-
permissions,
|
|
20
|
-
or
|
|
21
|
-
});
|
|
22
|
-
if ('function' == typeof children) return /*#__PURE__*/ jsx(Fragment, {
|
|
23
|
-
children: children(allowed)
|
|
24
|
-
});
|
|
25
|
-
return allowed ? /*#__PURE__*/ jsx(Fragment, {
|
|
26
|
-
children: children
|
|
27
|
-
}) : null;
|
|
28
|
-
}
|
|
29
|
-
const useCanRole = function({ roles, and }) {
|
|
30
|
-
const ability = useAbility();
|
|
31
|
-
let allowed = false;
|
|
32
|
-
allowed = and ? !roles || 0 === roles.length || roles.length > 0 && roles.every((role)=>ability.can(role, ROLE_SUBJECT)) : !roles || 0 === roles.length || roles.length > 0 && roles.some((role)=>ability.can(role, ROLE_SUBJECT));
|
|
33
|
-
return !!allowed;
|
|
34
|
-
};
|
|
35
|
-
const CanRole = function({ roles, and, children }) {
|
|
36
|
-
const allowed = useCanRole({
|
|
37
|
-
roles,
|
|
38
|
-
and
|
|
39
|
-
});
|
|
40
|
-
if ('function' == typeof children) return /*#__PURE__*/ jsx(Fragment, {
|
|
41
|
-
children: children(allowed)
|
|
42
|
-
});
|
|
43
|
-
return allowed ? /*#__PURE__*/ jsx(Fragment, {
|
|
44
|
-
children: children
|
|
45
|
-
}) : null;
|
|
46
|
-
};
|
|
47
10
|
const AuthStateContext = /*#__PURE__*/ createContext(null);
|
|
48
11
|
function AuthProvider({ children, config }) {
|
|
49
12
|
const [ability] = useState(()=>createAbility({}));
|
|
50
|
-
const [permissions, setPermissions] = useState([]);
|
|
51
|
-
const [roles, setRoles] = useState([]);
|
|
52
|
-
const [userId, setUserId] = useState(null);
|
|
53
13
|
const [isLoading, setIsLoading] = useState(false);
|
|
54
14
|
const [error, setError] = useState(null);
|
|
55
15
|
const [client] = useState(()=>new PermissionClient(config?.permissionApi));
|
|
56
|
-
const fetchPermissions = useCallback(async (
|
|
16
|
+
const fetchPermissions = useCallback(async ()=>{
|
|
57
17
|
setIsLoading(true);
|
|
58
18
|
setError(null);
|
|
59
19
|
try {
|
|
60
|
-
const data = await client.fetchPermissions(
|
|
61
|
-
const normalizedRoles = data.roles.map((role)=>'string' == typeof role ? role : role.name);
|
|
62
|
-
setPermissions(data.permissions);
|
|
63
|
-
setRoles(normalizedRoles);
|
|
64
|
-
setUserId(data.userId);
|
|
20
|
+
const data = await client.fetchPermissions();
|
|
65
21
|
updateAbility(ability, {
|
|
66
|
-
|
|
67
|
-
roles: normalizedRoles
|
|
22
|
+
roles: data.roles
|
|
68
23
|
});
|
|
69
24
|
config?.onSuccess?.(data);
|
|
70
25
|
} catch (err) {
|
|
@@ -79,28 +34,17 @@ function AuthProvider({ children, config }) {
|
|
|
79
34
|
client,
|
|
80
35
|
config
|
|
81
36
|
]);
|
|
82
|
-
const refetch = useCallback(async ()=>{
|
|
83
|
-
await fetchPermissions(userId);
|
|
84
|
-
}, [
|
|
85
|
-
userId,
|
|
86
|
-
fetchPermissions
|
|
87
|
-
]);
|
|
88
37
|
useEffect(()=>{
|
|
89
|
-
if (config?.
|
|
38
|
+
if (config?.enable !== false) fetchPermissions();
|
|
90
39
|
}, [
|
|
91
|
-
config?.
|
|
40
|
+
config?.enable,
|
|
92
41
|
fetchPermissions
|
|
93
42
|
]);
|
|
94
43
|
const stateContextValue = {
|
|
95
|
-
|
|
96
|
-
roles,
|
|
97
|
-
userId,
|
|
98
|
-
setUserId,
|
|
99
|
-
noPermissionPath: config?.noPermissionPath,
|
|
44
|
+
ability,
|
|
100
45
|
isLoading,
|
|
101
46
|
error,
|
|
102
|
-
fetchPermissions
|
|
103
|
-
refetch
|
|
47
|
+
fetchPermissions
|
|
104
48
|
};
|
|
105
49
|
return /*#__PURE__*/ jsx(AbilityContext.Provider, {
|
|
106
50
|
value: ability,
|
|
@@ -115,26 +59,13 @@ function useAuth() {
|
|
|
115
59
|
if (!context) throw new Error('useAuth must be used within an AuthProvider');
|
|
116
60
|
return context;
|
|
117
61
|
}
|
|
118
|
-
function
|
|
62
|
+
function useAuthAbility() {
|
|
119
63
|
return useContext(AbilityContext);
|
|
120
64
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
function useUserId() {
|
|
130
|
-
const { userId, setUserId } = useAuth();
|
|
131
|
-
return [
|
|
132
|
-
userId,
|
|
133
|
-
setUserId
|
|
134
|
-
];
|
|
135
|
-
}
|
|
136
|
-
function useNoPermissionPath() {
|
|
137
|
-
const { noPermissionPath } = useAuth();
|
|
138
|
-
return noPermissionPath;
|
|
139
|
-
}
|
|
140
|
-
export { AbilityContext, AuthProvider, Can, CanPermission, CanRole, useAbility, useAuth, useCanPermission, useCanRole, useNoPermissionPath, usePermissions, useRoles, useUserId };
|
|
65
|
+
const Can = createContextualCan(AbilityContext.Consumer);
|
|
66
|
+
const CanRole = ({ children, I })=>/*#__PURE__*/ jsx(Can, {
|
|
67
|
+
I: I,
|
|
68
|
+
a: ROLE_SUBJECT,
|
|
69
|
+
children: children
|
|
70
|
+
});
|
|
71
|
+
export { AbilityContext, AuthProvider, Can, CanRole, useAuth, useAuthAbility };
|
package/lib/ability-factory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ability } from '@casl/ability';
|
|
1
|
+
import { MongoAbility as Ability } from '@casl/ability';
|
|
2
2
|
import type { Permission, CaslRule } from './types';
|
|
3
3
|
export declare const ROLE_SUBJECT = "@role";
|
|
4
4
|
/**
|
|
@@ -6,9 +6,9 @@ export declare const ROLE_SUBJECT = "@role";
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function convertPermissionsToRules(permissions: Permission[], roles: string[]): CaslRule[];
|
|
8
8
|
/**
|
|
9
|
-
* 创建
|
|
9
|
+
* 创建 MongoAbility 实例
|
|
10
10
|
*/
|
|
11
|
-
export declare function createAbility({ permissions, roles }: {
|
|
11
|
+
export declare function createAbility({ permissions, roles, }: {
|
|
12
12
|
permissions?: Permission[];
|
|
13
13
|
roles?: string[];
|
|
14
14
|
}): Ability;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ability-factory.d.ts","sourceRoot":"","sources":["../src/ability-factory.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ability-factory.d.ts","sourceRoot":"","sources":["../src/ability-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,IAAI,OAAO,EAGxB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEpD,eAAO,MAAM,YAAY,UAAU,CAAC;AACpC;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,UAAU,EAAE,EACzB,KAAK,EAAE,MAAM,EAAE,GACd,QAAQ,EAAE,CAmBZ;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EACX,KAAK,GACN,EAAE;IACD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,GAAG,OAAO,CAkBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE;IAAE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACvE,IAAI,CAGN"}
|
package/lib/ability-factory.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
2
2
|
const ROLE_SUBJECT = '@role';
|
|
3
3
|
function convertPermissionsToRules(permissions, roles) {
|
|
4
4
|
const rules = [];
|
|
@@ -13,7 +13,7 @@ function convertPermissionsToRules(permissions, roles) {
|
|
|
13
13
|
return rules;
|
|
14
14
|
}
|
|
15
15
|
function createAbility({ permissions, roles }) {
|
|
16
|
-
const { build, can } = new AbilityBuilder(
|
|
16
|
+
const { build, can } = new AbilityBuilder(createMongoAbility);
|
|
17
17
|
const rules = convertPermissionsToRules(permissions || [], roles || []);
|
|
18
18
|
for (const rule of rules)if (Array.isArray(rule.action)) for (const action of rule.action)can(action, rule.subject, rule.fields);
|
|
19
19
|
else can(rule.action, rule.subject, rule.fields);
|
package/lib/index.d.ts
CHANGED
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
* 封装了权限数据获取和 Ability 初始化逻辑
|
|
6
6
|
*/
|
|
7
7
|
export type { Action, Subject, Permission, UserRole, PermissionApiResponse, PermissionApiConfig, AuthSdkConfig, CaslRule, } from './types';
|
|
8
|
-
export { createAbility, updateAbility, convertPermissionsToRules, } from './ability-factory';
|
|
8
|
+
export { createAbility, updateAbility, convertPermissionsToRules, ROLE_SUBJECT, } from './ability-factory';
|
|
9
9
|
export { PermissionClient } from './permission-client';
|
|
10
|
-
export { AuthProvider,
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
13
|
-
export type { CanRouteProps } from './CanRoute';
|
|
14
|
-
export { Ability, AbilityBuilder, type AbilityClass } from '@casl/ability';
|
|
10
|
+
export { AuthProvider, useAuth, useAuthAbility, Can, AbilityContext, } from './AuthProvider';
|
|
11
|
+
export type { AuthProviderProps } from './AuthProvider';
|
|
12
|
+
export { MongoAbility, AbilityBuilder, type AbilityClass } from '@casl/ability';
|
|
15
13
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,MAAM,EACN,OAAO,EACP,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,QAAQ,GACT,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,aAAa,EACb,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,MAAM,EACN,OAAO,EACP,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,QAAQ,GACT,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,aAAa,EACb,yBAAyB,EACzB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAE3B,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;AAGxD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { convertPermissionsToRules, createAbility, updateAbility } from "./ability-factory.js";
|
|
1
|
+
import { ROLE_SUBJECT, convertPermissionsToRules, createAbility, updateAbility } from "./ability-factory.js";
|
|
2
2
|
import { PermissionClient } from "./permission-client.js";
|
|
3
|
-
import { AbilityContext, AuthProvider, Can,
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export { Ability, AbilityBuilder, AbilityContext, AuthProvider, Can, CanPermission, CanRole, CanRoute, PermissionClient, convertPermissionsToRules, createAbility, updateAbility, useAbility, useAuth, useCanPermission, useCanRole, useNoPermissionPath, usePermissions, useRoles, useUserId };
|
|
3
|
+
import { AbilityContext, AuthProvider, Can, useAuth, useAuthAbility } from "./AuthProvider.js";
|
|
4
|
+
import { AbilityBuilder, MongoAbility } from "@casl/ability";
|
|
5
|
+
export { AbilityBuilder, AbilityContext, AuthProvider, Can, MongoAbility, PermissionClient, ROLE_SUBJECT, convertPermissionsToRules, createAbility, updateAbility, useAuth, useAuthAbility };
|
|
@@ -7,9 +7,8 @@ export declare class PermissionClient {
|
|
|
7
7
|
constructor(config?: PermissionApiConfig);
|
|
8
8
|
/**
|
|
9
9
|
* 获取用户权限数据
|
|
10
|
-
* @param userId - 用户ID(可选,用于向后兼容。如果端点包含 :userId,则会替换)
|
|
11
10
|
*/
|
|
12
|
-
fetchPermissions(
|
|
11
|
+
fetchPermissions(): Promise<PermissionApiResponse>;
|
|
13
12
|
/**
|
|
14
13
|
* 更新配置
|
|
15
14
|
*/
|
|
@@ -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;
|
|
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"}
|
package/lib/permission-client.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
const DEFAULT_CONFIG = {
|
|
2
2
|
baseUrl: '',
|
|
3
|
-
|
|
4
|
-
timeout: 5000,
|
|
5
|
-
withCredentials: true
|
|
3
|
+
timeout: 5000
|
|
6
4
|
};
|
|
7
5
|
class PermissionClient {
|
|
8
6
|
constructor(config){
|
|
@@ -11,16 +9,15 @@ class PermissionClient {
|
|
|
11
9
|
...config
|
|
12
10
|
};
|
|
13
11
|
}
|
|
14
|
-
async fetchPermissions(
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
const url =
|
|
12
|
+
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 = `/app/${appId}/runtime/api/v1/permissions/roles`;
|
|
19
17
|
const requestHeaders = {
|
|
20
18
|
'Content-Type': 'application/json',
|
|
21
19
|
...headers
|
|
22
20
|
};
|
|
23
|
-
if (apiToken) requestHeaders['Authorization'] = `Bearer ${apiToken}`;
|
|
24
21
|
const controller = new AbortController();
|
|
25
22
|
const timeoutId = setTimeout(()=>controller.abort(), timeout);
|
|
26
23
|
try {
|
|
@@ -28,18 +25,18 @@ class PermissionClient {
|
|
|
28
25
|
method: 'GET',
|
|
29
26
|
headers: requestHeaders,
|
|
30
27
|
signal: controller.signal,
|
|
31
|
-
credentials:
|
|
28
|
+
credentials: 'include'
|
|
32
29
|
});
|
|
33
30
|
clearTimeout(timeoutId);
|
|
34
31
|
if (!response.ok) throw new Error(`Permission API returned ${response.status}: ${response.statusText}`);
|
|
35
32
|
const data = await response.json();
|
|
36
33
|
return {
|
|
37
|
-
|
|
38
|
-
fetchedAt:
|
|
34
|
+
roles: data.data?.role_list || [],
|
|
35
|
+
fetchedAt: new Date()
|
|
39
36
|
};
|
|
40
37
|
} catch (error) {
|
|
41
38
|
clearTimeout(timeoutId);
|
|
42
|
-
if ('AbortError' === error.name) throw new Error(`Permission API request timeout after ${timeout}ms`);
|
|
39
|
+
if (error instanceof Error && 'AbortError' === error.name) throw new Error(`Permission API request timeout after ${timeout}ms`);
|
|
43
40
|
throw error;
|
|
44
41
|
}
|
|
45
42
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -27,9 +27,7 @@ export interface UserRole {
|
|
|
27
27
|
* 权限 API 响应数据
|
|
28
28
|
*/
|
|
29
29
|
export interface PermissionApiResponse {
|
|
30
|
-
|
|
31
|
-
roles: (string | UserRole)[];
|
|
32
|
-
permissions: Permission[];
|
|
30
|
+
roles: string[];
|
|
33
31
|
fetchedAt?: string | Date;
|
|
34
32
|
}
|
|
35
33
|
/**
|
|
@@ -41,11 +39,6 @@ export interface PermissionApiConfig {
|
|
|
41
39
|
* @default ''
|
|
42
40
|
*/
|
|
43
41
|
baseUrl?: string;
|
|
44
|
-
/**
|
|
45
|
-
* API 端点路径
|
|
46
|
-
* @default '/api/permissions'
|
|
47
|
-
*/
|
|
48
|
-
endpoint?: string;
|
|
49
42
|
/**
|
|
50
43
|
* 请求超时时间(毫秒)
|
|
51
44
|
* @default 5000
|
|
@@ -55,34 +48,20 @@ export interface PermissionApiConfig {
|
|
|
55
48
|
* 自定义请求头
|
|
56
49
|
*/
|
|
57
50
|
headers?: Record<string, string>;
|
|
58
|
-
/**
|
|
59
|
-
* API Token,会自动添加到 Authorization header
|
|
60
|
-
*/
|
|
61
|
-
apiToken?: string;
|
|
62
|
-
/**
|
|
63
|
-
* 是否携带凭证(cookies)
|
|
64
|
-
* @default true
|
|
65
|
-
*/
|
|
66
|
-
withCredentials?: boolean;
|
|
67
51
|
}
|
|
68
52
|
/**
|
|
69
53
|
* Auth SDK 配置选项
|
|
70
54
|
*/
|
|
71
55
|
export interface AuthSdkConfig {
|
|
72
|
-
/**
|
|
73
|
-
* 无权限路径
|
|
74
|
-
* @default '/no-access'
|
|
75
|
-
*/
|
|
76
|
-
noPermissionPath?: string;
|
|
77
56
|
/**
|
|
78
57
|
* 权限 API 配置
|
|
79
58
|
*/
|
|
80
59
|
permissionApi?: PermissionApiConfig;
|
|
81
60
|
/**
|
|
82
|
-
*
|
|
61
|
+
* 是否在初始化时使用获取权限
|
|
83
62
|
* @default false
|
|
84
63
|
*/
|
|
85
|
-
|
|
64
|
+
enable?: boolean;
|
|
86
65
|
/**
|
|
87
66
|
* 获取权限失败时的错误处理
|
|
88
67
|
*/
|
package/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,
|
|
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"}
|
package/package.json
CHANGED
package/lib/CanRoute.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* CanRoute 组件 Props
|
|
4
|
-
*/
|
|
5
|
-
export interface CanRouteProps {
|
|
6
|
-
permissions?: Array<{
|
|
7
|
-
action: string;
|
|
8
|
-
subject: string;
|
|
9
|
-
}>;
|
|
10
|
-
roles?: string[];
|
|
11
|
-
children: ReactNode;
|
|
12
|
-
redirectTo?: string;
|
|
13
|
-
fallback?: ReactNode;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* CanRoute 组件 - 路由级别的权限守卫
|
|
17
|
-
*
|
|
18
|
-
* 基于权限和角色进行路由级别的访问控制
|
|
19
|
-
* 当权限或角色检查失败时,会渲染 fallback 内容或返回 null
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* import { CanRoute } from '@lark-apaas/auth-sdk';
|
|
24
|
-
* import { Navigate, useLocation } from 'react-router-dom';
|
|
25
|
-
*
|
|
26
|
-
* function ProtectedRoute() {
|
|
27
|
-
* const location = useLocation();
|
|
28
|
-
*
|
|
29
|
-
* return (
|
|
30
|
-
* <CanRoute
|
|
31
|
-
* permissions={[{ action: 'read', subject: 'Task' }]}
|
|
32
|
-
* roles={['admin']}
|
|
33
|
-
* fallback={<Navigate to="/forbidden" replace state={{ from: location }} />}
|
|
34
|
-
* >
|
|
35
|
-
* <TaskList />
|
|
36
|
-
* </CanRoute>
|
|
37
|
-
* );
|
|
38
|
-
* }
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare function CanRoute({ permissions, roles, children, fallback, redirectTo }: CanRouteProps): import("react/jsx-runtime").JSX.Element | null;
|
|
42
|
-
//# sourceMappingURL=CanRoute.d.ts.map
|
package/lib/CanRoute.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CanRoute.d.ts","sourceRoot":"","sources":["../src/CanRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItC;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAe,EAAE,UAAe,EAAE,EAAE,aAAa,kDAezG"}
|
package/lib/CanRoute.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Navigate, useLocation } from "react-router-dom";
|
|
3
|
-
import { useCanPermission, useCanRole, useNoPermissionPath } from "./AuthProvider.js";
|
|
4
|
-
function CanRoute({ permissions, roles, children, fallback = null, redirectTo = '' }) {
|
|
5
|
-
const noPermissionPath = useNoPermissionPath();
|
|
6
|
-
const location = useLocation();
|
|
7
|
-
let allPass = true;
|
|
8
|
-
if (!useCanPermission({
|
|
9
|
-
permissions
|
|
10
|
-
})) allPass = false;
|
|
11
|
-
if (!useCanRole({
|
|
12
|
-
roles
|
|
13
|
-
})) allPass = false;
|
|
14
|
-
if (!allPass) return fallback ? /*#__PURE__*/ jsx(Fragment, {
|
|
15
|
-
children: fallback
|
|
16
|
-
}) : redirectTo || noPermissionPath ? /*#__PURE__*/ jsx(Navigate, {
|
|
17
|
-
to: redirectTo || noPermissionPath,
|
|
18
|
-
replace: true,
|
|
19
|
-
state: {
|
|
20
|
-
from: location
|
|
21
|
-
}
|
|
22
|
-
}) : null;
|
|
23
|
-
return /*#__PURE__*/ jsx(Fragment, {
|
|
24
|
-
children: children
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
export { CanRoute };
|
package/lib/RouteGuard.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* RouteGuard 组件 Props
|
|
4
|
-
*/
|
|
5
|
-
export interface RouteGuardProps {
|
|
6
|
-
permissions?: Array<{
|
|
7
|
-
action: string;
|
|
8
|
-
subject: string;
|
|
9
|
-
}>;
|
|
10
|
-
roles?: string[];
|
|
11
|
-
children: ReactNode;
|
|
12
|
-
redirectTo?: string;
|
|
13
|
-
fallback?: ReactNode;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* RouteGuard 组件 - 路由级别的权限守卫
|
|
17
|
-
*
|
|
18
|
-
* 基于权限和角色进行路由级别的访问控制
|
|
19
|
-
* 当权限或角色检查失败时,会渲染 fallback 内容或返回 null
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* import { RouteGuard } from '@lark-apaas/auth-sdk';
|
|
24
|
-
* import { Navigate, useLocation } from 'react-router-dom';
|
|
25
|
-
*
|
|
26
|
-
* function ProtectedRoute() {
|
|
27
|
-
* const location = useLocation();
|
|
28
|
-
*
|
|
29
|
-
* return (
|
|
30
|
-
* <RouteGuard
|
|
31
|
-
* permissions={[{ action: 'read', subject: 'Task' }]}
|
|
32
|
-
* roles={['admin']}
|
|
33
|
-
* fallback={<Navigate to="/forbidden" replace state={{ from: location }} />}
|
|
34
|
-
* >
|
|
35
|
-
* <TaskList />
|
|
36
|
-
* </RouteGuard>
|
|
37
|
-
* );
|
|
38
|
-
* }
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare function RouteGuard({ permissions, roles, children, fallback, redirectTo }: RouteGuardProps): import("react/jsx-runtime").JSX.Element | null;
|
|
42
|
-
//# sourceMappingURL=RouteGuard.d.ts.map
|
package/lib/RouteGuard.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RouteGuard.d.ts","sourceRoot":"","sources":["../src/RouteGuard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAe,EAAE,UAAe,EAAE,EAAE,eAAe,kDAe7G"}
|
package/lib/RouteGuard.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Navigate, useLocation } from "react-router-dom";
|
|
3
|
-
import { useCanPermission, useCanRole, useNoPermissionPath } from "./AuthProvider.js";
|
|
4
|
-
function RouteGuard({ permissions, roles, children, fallback = null, redirectTo = '' }) {
|
|
5
|
-
const noPermissionPath = useNoPermissionPath();
|
|
6
|
-
const location = useLocation();
|
|
7
|
-
let allPass = true;
|
|
8
|
-
if (!useCanPermission({
|
|
9
|
-
permissions
|
|
10
|
-
})) allPass = false;
|
|
11
|
-
if (!useCanRole({
|
|
12
|
-
roles
|
|
13
|
-
})) allPass = false;
|
|
14
|
-
if (!allPass) return fallback ? /*#__PURE__*/ jsx(Fragment, {
|
|
15
|
-
children: fallback
|
|
16
|
-
}) : redirectTo || noPermissionPath ? /*#__PURE__*/ jsx(Navigate, {
|
|
17
|
-
to: redirectTo || noPermissionPath,
|
|
18
|
-
replace: true,
|
|
19
|
-
state: {
|
|
20
|
-
from: location
|
|
21
|
-
}
|
|
22
|
-
}) : null;
|
|
23
|
-
return /*#__PURE__*/ jsx(Fragment, {
|
|
24
|
-
children: children
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
export { RouteGuard };
|