@lark-apaas/auth-sdk 0.1.0-alpha.7 → 0.1.0-alpha.70
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 +67 -81
- package/lib/AuthProvider.d.ts +30 -31
- package/lib/AuthProvider.d.ts.map +1 -1
- package/lib/AuthProvider.js +51 -15
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/permission-client.d.ts +1 -1
- package/lib/permission-client.d.ts.map +1 -1
- package/lib/permission-client.js +16 -14
- package/lib/types.d.ts +9 -3
- package/lib/types.d.ts.map +1 -1
- package/package.json +6 -4
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,
|
|
19
|
+
import { AuthProvider, CanRole, useAuth } 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 =
|
|
65
|
+
const ability = useContext(AbilityContext);
|
|
37
66
|
return (
|
|
38
67
|
<div>
|
|
39
|
-
|
|
68
|
+
{ability.can('role_admin', ROLE_SUBJECT) || ability.can('role_editor', ROLE_SUBJECT) ? (
|
|
40
69
|
<div>可见的仪表盘</div>
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
- `
|
|
147
|
-
- `
|
|
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
|
-
- **直接使用组件**:`
|
|
155
|
-
-
|
|
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
|
|
package/lib/AuthProvider.d.ts
CHANGED
|
@@ -33,10 +33,9 @@ export interface AuthProviderProps {
|
|
|
33
33
|
*
|
|
34
34
|
* function App() {
|
|
35
35
|
* return (
|
|
36
|
-
* <AuthProvider
|
|
36
|
+
* <AuthProvider config={{
|
|
37
37
|
* permissionApi: {
|
|
38
|
-
*
|
|
39
|
-
* endpoint: '/mock-api/users/:userId/permissions'
|
|
38
|
+
* url: '/app/app_xxx/__runtime__/api/v1/permissions/roles',
|
|
40
39
|
* }
|
|
41
40
|
* }}>
|
|
42
41
|
* <YourApp />
|
|
@@ -46,16 +45,22 @@ export interface AuthProviderProps {
|
|
|
46
45
|
* ```
|
|
47
46
|
*/
|
|
48
47
|
export declare function AuthProvider({ children, config }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
/**
|
|
49
|
+
* 获取 Ability 实例
|
|
50
|
+
*
|
|
51
|
+
* @param permissionApiConfig - 权限 API 配置
|
|
52
|
+
* @returns Ability 实例或错误
|
|
53
|
+
*/
|
|
54
|
+
export declare function getAbility(permissionApiConfig: AuthSdkConfig['permissionApi']): Promise<Error | MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
|
|
49
55
|
/**
|
|
50
56
|
* useAuth Hook - 获取权限数据和加载状态
|
|
51
57
|
*
|
|
52
58
|
* @example
|
|
53
59
|
* ```tsx
|
|
54
|
-
* import { useAuth
|
|
60
|
+
* import { useAuth } from '@lark-apaas/auth-sdk';
|
|
55
61
|
*
|
|
56
62
|
* function MyComponent() {
|
|
57
|
-
* const {
|
|
58
|
-
* const ability = useAbility();
|
|
63
|
+
* const { ability, isLoading } = useAuth();
|
|
59
64
|
*
|
|
60
65
|
* if (isLoading) return <div>Loading...</div>;
|
|
61
66
|
*
|
|
@@ -69,43 +74,36 @@ export declare function AuthProvider({ children, config }: AuthProviderProps): i
|
|
|
69
74
|
*/
|
|
70
75
|
export declare function useAuth(): AuthStateContextValue;
|
|
71
76
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* ```tsx
|
|
76
|
-
* import { useAuthAbility } from '@lark-apaas/auth-sdk';
|
|
77
|
-
*
|
|
78
|
-
* function MyComponent() {
|
|
79
|
-
* const ability = useAuthAbility();
|
|
80
|
-
*
|
|
81
|
-
* return (
|
|
82
|
-
* <button disabled={ability.cannot('create', 'Task')}>
|
|
83
|
-
* Create Task
|
|
84
|
-
* </button>
|
|
85
|
-
* );
|
|
86
|
-
* }
|
|
87
|
-
* ```
|
|
77
|
+
* CASL 原始 Can 组件(内部使用)
|
|
88
78
|
*/
|
|
89
|
-
|
|
79
|
+
declare const CaslCan: React.FunctionComponent<import("@casl/react").BoundCanProps<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
|
|
90
80
|
/**
|
|
91
81
|
* Can Component - 基于 Ability 实例的条件渲染组件
|
|
92
82
|
*
|
|
83
|
+
* 内置 isLoading 保护:权限加载期间渲染 fallback(默认为 null),
|
|
84
|
+
* 避免因 ability 未就绪而误判为无权限导致内容闪烁。
|
|
85
|
+
*
|
|
93
86
|
* @example
|
|
94
87
|
* ```tsx
|
|
95
88
|
* import { Can } from '@lark-apaas/auth-sdk';
|
|
96
89
|
*
|
|
97
90
|
* function MyComponent() {
|
|
98
91
|
* return (
|
|
99
|
-
* <Can I="Admin" a="@role">
|
|
92
|
+
* <Can I="Admin" a="@role" fallback={<Loading />}>
|
|
100
93
|
* <TaskList />
|
|
101
94
|
* </Can>
|
|
102
95
|
* );
|
|
103
96
|
* }
|
|
104
97
|
* ```
|
|
105
98
|
*/
|
|
106
|
-
export declare
|
|
99
|
+
export declare function Can({ fallback, ...props }: React.ComponentProps<typeof CaslCan> & {
|
|
100
|
+
fallback?: React.ReactNode;
|
|
101
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
107
102
|
/**
|
|
108
|
-
* CanRole Component -
|
|
103
|
+
* CanRole Component - 基于角色的条件渲染组件
|
|
104
|
+
*
|
|
105
|
+
* 支持 fallback prop,用于在权限加载期间显示自定义内容(如 Loading),
|
|
106
|
+
* 避免加载期间因 can() 返回 false 而误判为无权限。
|
|
109
107
|
*
|
|
110
108
|
* @example
|
|
111
109
|
* ```tsx
|
|
@@ -113,16 +111,17 @@ export declare const Can: React.FunctionComponent<import("@casl/react").BoundCan
|
|
|
113
111
|
*
|
|
114
112
|
* function MyComponent() {
|
|
115
113
|
* return (
|
|
116
|
-
* <CanRole
|
|
117
|
-
* <
|
|
114
|
+
* <CanRole roles={['admin']} fallback={<Loading />}>
|
|
115
|
+
* <AdminPanel />
|
|
118
116
|
* </CanRole>
|
|
119
117
|
* );
|
|
120
118
|
* }
|
|
121
119
|
* ```
|
|
122
120
|
*/
|
|
123
|
-
export declare
|
|
121
|
+
export declare function CanRole({ children, roles, fallback, }: {
|
|
124
122
|
children: React.ReactNode;
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
roles: string[];
|
|
124
|
+
fallback?: React.ReactNode;
|
|
125
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
127
126
|
export {};
|
|
128
127
|
//# 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
|
|
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;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CAiEnE;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,mBAAmB,EAAE,aAAa,CAAC,eAAe,CAAC,2GAmBpD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,IAAI,qBAAqB,CAQ/C;AAED;;GAEG;AACH,QAAA,MAAM,OAAO,sJAA+C,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,GAAG,CAAC,EAClB,QAAe,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,OAAO,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAKvE;AAoBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,KAAK,EACL,QAAe,GAChB,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,kDAKA"}
|
package/lib/AuthProvider.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
|
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({
|
|
@@ -9,18 +9,20 @@ const AbilityContext = /*#__PURE__*/ createContext(createAbility({
|
|
|
9
9
|
}));
|
|
10
10
|
const AuthStateContext = /*#__PURE__*/ createContext(null);
|
|
11
11
|
function AuthProvider({ children, config }) {
|
|
12
|
-
const [ability] = useState(()=>createAbility({}));
|
|
13
|
-
const [
|
|
12
|
+
const [ability, setAbility] = useState(()=>createAbility({}));
|
|
13
|
+
const [client] = useState(()=>config?.permissionApi ? new PermissionClient(config.permissionApi) : null);
|
|
14
|
+
const [isLoading, setIsLoading] = useState(()=>config?.enable !== false && null !== client);
|
|
14
15
|
const [error, setError] = useState(null);
|
|
15
|
-
const [client] = useState(()=>new PermissionClient(config?.permissionApi));
|
|
16
16
|
const fetchPermissions = useCallback(async ()=>{
|
|
17
|
+
if (!client) return;
|
|
17
18
|
setIsLoading(true);
|
|
18
19
|
setError(null);
|
|
19
20
|
try {
|
|
20
21
|
const data = await client.fetchPermissions();
|
|
21
|
-
|
|
22
|
+
const newAbility = createAbility({
|
|
22
23
|
roles: data.roles
|
|
23
24
|
});
|
|
25
|
+
setAbility(newAbility);
|
|
24
26
|
config?.onSuccess?.(data);
|
|
25
27
|
} catch (err) {
|
|
26
28
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -30,7 +32,6 @@ function AuthProvider({ children, config }) {
|
|
|
30
32
|
setIsLoading(false);
|
|
31
33
|
}
|
|
32
34
|
}, [
|
|
33
|
-
ability,
|
|
34
35
|
client,
|
|
35
36
|
config
|
|
36
37
|
]);
|
|
@@ -54,18 +55,53 @@ function AuthProvider({ children, config }) {
|
|
|
54
55
|
})
|
|
55
56
|
});
|
|
56
57
|
}
|
|
58
|
+
async function getAbility(permissionApiConfig) {
|
|
59
|
+
if (!permissionApiConfig) return new Error('permissionApi config is required');
|
|
60
|
+
const client = new PermissionClient(permissionApiConfig);
|
|
61
|
+
try {
|
|
62
|
+
const data = await client.fetchPermissions();
|
|
63
|
+
const ability = createAbility({
|
|
64
|
+
roles: data.roles
|
|
65
|
+
});
|
|
66
|
+
return ability;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
69
|
+
return error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
57
72
|
function useAuth() {
|
|
58
73
|
const context = useContext(AuthStateContext);
|
|
59
74
|
if (!context) throw new Error('useAuth must be used within an AuthProvider');
|
|
60
75
|
return context;
|
|
61
76
|
}
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
const CaslCan = createContextualCan(AbilityContext.Consumer);
|
|
78
|
+
function Can({ fallback = null, ...props }) {
|
|
79
|
+
const authState = useContext(AuthStateContext);
|
|
80
|
+
if (authState?.isLoading) return /*#__PURE__*/ jsx(Fragment, {
|
|
81
|
+
children: fallback
|
|
82
|
+
});
|
|
83
|
+
return /*#__PURE__*/ jsx(CaslCan, {
|
|
84
|
+
...props
|
|
85
|
+
});
|
|
64
86
|
}
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
function useCanRole({ roles }) {
|
|
88
|
+
const ability = useContext(AbilityContext);
|
|
89
|
+
const authState = useContext(AuthStateContext);
|
|
90
|
+
const allowed = !roles || 0 === roles.length || roles.length > 0 && roles.some((role)=>ability.can(role, ROLE_SUBJECT));
|
|
91
|
+
return {
|
|
92
|
+
allowed: !!allowed,
|
|
93
|
+
isLoading: authState?.isLoading ?? false
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function CanRole({ children, roles, fallback = null }) {
|
|
97
|
+
const { allowed, isLoading } = useCanRole({
|
|
98
|
+
roles
|
|
99
|
+
});
|
|
100
|
+
if (isLoading) return /*#__PURE__*/ jsx(Fragment, {
|
|
101
|
+
children: fallback
|
|
70
102
|
});
|
|
71
|
-
|
|
103
|
+
return allowed ? /*#__PURE__*/ jsx(Fragment, {
|
|
104
|
+
children: children
|
|
105
|
+
}) : null;
|
|
106
|
+
}
|
|
107
|
+
export { AbilityContext, AuthProvider, Can, CanRole, getAbility, useAuth };
|
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,6 @@
|
|
|
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,
|
|
10
|
+
export { AuthProvider, useAuth, CanRole, AbilityContext, } from './AuthProvider';
|
|
11
11
|
export type { AuthProviderProps } from './AuthProvider';
|
|
12
12
|
//# 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,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,
|
|
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,GACf,MAAM,gBAAgB,CAAC;AAExB,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,
|
|
4
|
-
export { AbilityContext, AuthProvider,
|
|
3
|
+
import { AbilityContext, AuthProvider, CanRole, useAuth } from "./AuthProvider.js";
|
|
4
|
+
export { AbilityContext, AuthProvider, CanRole, PermissionClient, ROLE_SUBJECT, useAuth };
|
|
@@ -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;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;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,22 +1,14 @@
|
|
|
1
|
-
|
|
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 =
|
|
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
|
-
|
|
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,17 @@ class PermissionClient {
|
|
|
36
28
|
};
|
|
37
29
|
} catch (error) {
|
|
38
30
|
clearTimeout(timeoutId);
|
|
39
|
-
if (error instanceof Error && 'AbortError' === error.name)
|
|
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
|
+
});
|
|
40
42
|
throw error;
|
|
41
43
|
}
|
|
42
44
|
}
|
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
|
*/
|
|
@@ -35,10 +42,9 @@ export interface PermissionApiResponse {
|
|
|
35
42
|
*/
|
|
36
43
|
export interface PermissionApiConfig {
|
|
37
44
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @default ''
|
|
45
|
+
* 权限接口完整 URL,由上层注入,不在 SDK 内部硬编码
|
|
40
46
|
*/
|
|
41
|
-
|
|
47
|
+
url: string;
|
|
42
48
|
/**
|
|
43
49
|
* 请求超时时间(毫秒)
|
|
44
50
|
* @default 5000
|
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,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
|
|
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,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,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.
|
|
3
|
+
"version": "0.1.0-alpha.70",
|
|
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.
|
|
22
|
-
"@casl/react": "^
|
|
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",
|