@lark-apaas/auth-sdk 0.1.2 → 0.1.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 CHANGED
@@ -16,7 +16,7 @@ yarn add @lark-apaas/auth-sdk
16
16
 
17
17
  ```tsx
18
18
  import React from 'react';
19
- import { AuthProvider, CanRole, useAuthAbility } from '@lark-apaas/auth-sdk';
19
+ import { AuthProvider, CanRole, useAuth } from '@lark-apaas/auth-sdk';
20
20
 
21
21
  export default function App() {
22
22
  return (
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { MongoAbility } from '@casl/ability';
3
- import type { AuthSdkConfig } from './types';
3
+ import type { AuthSdkConfig, PermissionPointData } from './types';
4
4
  /**
5
5
  * Ability Context - 用于在组件树中共享 Ability 实例
6
6
  */
@@ -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: PermissionPointData[];
14
15
  isLoading: boolean;
15
16
  error: Error | null;
16
17
  fetchPermissions: (userId?: string) => Promise<void>;
@@ -45,23 +46,15 @@ export interface AuthProviderProps {
45
46
  * ```
46
47
  */
47
48
  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>>;
55
49
  /**
56
50
  * useAuth Hook - 获取权限数据和加载状态
57
51
  *
58
52
  * @example
59
53
  * ```tsx
60
- * import { useAuth, useAbility } from '@lark-apaas/auth-sdk';
54
+ * import { useAuth } from '@lark-apaas/auth-sdk';
61
55
  *
62
56
  * function MyComponent() {
63
- * const { permissions, isLoading } = useAuth();
64
- * const ability = useAbility();
57
+ * const { ability, isLoading } = useAuth();
65
58
  *
66
59
  * if (isLoading) return <div>Loading...</div>;
67
60
  *
@@ -75,63 +68,72 @@ export declare function getAbility(permissionApiConfig: AuthSdkConfig['permissio
75
68
  */
76
69
  export declare function useAuth(): AuthStateContextValue;
77
70
  /**
78
- * useAuthAbility Hook - 获取 Ability 实例
71
+ * CanRole Component - 基于角色的条件渲染组件
72
+ *
73
+ * 支持 fallback prop,用于在权限加载期间显示自定义内容(如 Loading),
74
+ * 避免加载期间因 can() 返回 false 而误判为无权限。
79
75
  *
80
76
  * @example
81
77
  * ```tsx
82
- * import { useAuthAbility } from '@lark-apaas/auth-sdk';
78
+ * import { CanRole } from '@lark-apaas/auth-sdk';
83
79
  *
84
80
  * function MyComponent() {
85
- * const ability = useAuthAbility();
86
- *
87
81
  * return (
88
- * <button disabled={ability.cannot('create', 'Task')}>
89
- * Create Task
90
- * </button>
82
+ * <CanRole roles={['admin']} fallback={<Loading />}>
83
+ * <AdminPanel />
84
+ * </CanRole>
91
85
  * );
92
86
  * }
93
87
  * ```
94
88
  */
95
- export declare function useAuthAbility(): MongoAbility;
89
+ export declare function CanRole({ children, roles, fallback, }: {
90
+ children: React.ReactNode;
91
+ roles: string[];
92
+ fallback?: React.ReactNode;
93
+ }): import("react/jsx-runtime").JSX.Element | null;
94
+ /**
95
+ * useUserPermissions Hook - 获取当前用户的权限点位列表
96
+ */
97
+ export declare function useUserPermissions(): PermissionPointData[];
96
98
  /**
97
- * Can Component - 基于 Ability 实例的条件渲染组件
99
+ * useCan Hook - 通过 CASL Ability 判断用户是否拥有指定权限
98
100
  *
99
- * @example
100
- * ```tsx
101
- * import { Can } from '@lark-apaas/auth-sdk';
101
+ * @param action - 操作类型,如 'read', 'create'
102
+ * @param subject - 资源类型,如 'Task', 'Article';角色检查时传 '@role'
102
103
  *
103
- * function MyComponent() {
104
- * return (
105
- * <Can I="Admin" a="@role">
106
- * <TaskList />
107
- * </Can>
108
- * );
109
- * }
104
+ * @example
105
+ * ```ts
106
+ * const { allowed: canRead, isLoading } = useCan('read', 'Task');
107
+ * if (isLoading) return <Loading />;
108
+ * if (!canRead) return <NoAccess />;
110
109
  * ```
111
110
  */
112
- export declare const Can: React.FunctionComponent<import("@casl/react").BoundCanProps<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
113
- export declare const useCanRole: ({ roles }: {
114
- roles: string[];
115
- }) => boolean;
111
+ export declare function useCan(action: string, subject: string): {
112
+ allowed: boolean;
113
+ isLoading: boolean;
114
+ };
116
115
  /**
117
- * CanRole Component - 基于 Ability 实例的角色条件渲染组件
116
+ * Can Component - 基于 CASL Ability 的条件渲染组件
117
+ * 前后端统一:后端 @Can('read', 'Task'),前端 <Can action="read" subject="Task">
118
118
  *
119
119
  * @example
120
120
  * ```tsx
121
- * import { CanRole } from '@lark-apaas/auth-sdk';
121
+ * import { Can } from '@lark-apaas/auth-sdk';
122
122
  *
123
- * function MyComponent() {
124
- * return (
125
- * <CanRole roles={['role_admin', 'role_editor']}>
126
- * <TaskList />
127
- * </CanRole>
128
- * );
129
- * }
123
+ * <Can action="read" subject="Task">
124
+ * <TaskList />
125
+ * </Can>
126
+ *
127
+ * <Can action="delete" subject="Task" fallback={<Skeleton />}>
128
+ * <DeleteButton />
129
+ * </Can>
130
130
  * ```
131
131
  */
132
- export declare function CanRole({ children, roles, }: {
132
+ export declare function Can({ children, action, subject, fallback, }: {
133
133
  children: React.ReactNode;
134
- roles: string[];
134
+ action: string;
135
+ subject: string;
136
+ fallback?: React.ReactNode;
135
137
  }): import("react/jsx-runtime").JSX.Element | null;
136
138
  export {};
137
139
  //# 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;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA8DnE;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"}
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,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAIlE;;GAEG;AACH,eAAO,MAAM,cAAc,uGAE1B,CAAC;AAOF;;;GAGG;AACH,UAAU,qBAAqB;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,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,2CAuFnE;AAGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,IAAI,qBAAqB,CAQ/C;AAqBD;;;;;;;;;;;;;;;;;;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;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,mBAAmB,EAAE,CAE1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAM1C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,MAAM,EACN,OAAO,EACP,QAAe,GAChB,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,kDAIA"}
@@ -2,28 +2,37 @@ import { Fragment, jsx } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useState } from "react";
3
3
  import { ROLE_SUBJECT, createAbility } from "./ability-factory.js";
4
4
  import { PermissionClient } from "./permission-client.js";
5
- import { createContextualCan } from "@casl/react";
6
5
  const AbilityContext = /*#__PURE__*/ createContext(createAbility({
7
- permissions: [],
8
- roles: []
6
+ roles: [],
7
+ permissionPoints: []
9
8
  }));
9
+ const PermissionsContext = /*#__PURE__*/ createContext([]);
10
10
  const AuthStateContext = /*#__PURE__*/ createContext(null);
11
11
  function AuthProvider({ children, config }) {
12
12
  const [ability, setAbility] = useState(()=>createAbility({}));
13
- const [isLoading, setIsLoading] = useState(false);
14
- const [error, setError] = useState(null);
13
+ const [permissions, setPermissions] = useState([]);
15
14
  const [client] = useState(()=>config?.permissionApi ? new PermissionClient(config.permissionApi) : null);
15
+ const [isLoading, setIsLoading] = useState(()=>config?.enable !== false && null !== client);
16
+ const [error, setError] = useState(null);
16
17
  const fetchPermissions = useCallback(async ()=>{
17
- if (!client) return;
18
18
  setIsLoading(true);
19
19
  setError(null);
20
20
  try {
21
- const data = await client.fetchPermissions();
22
- const newAbility = createAbility({
23
- roles: data.roles
24
- });
21
+ const [rolesResult, permissionPointsResult] = await Promise.allSettled([
22
+ client ? client.fetchPermissions() : Promise.resolve(null),
23
+ PermissionClient.fetchPermissionPoints()
24
+ ]);
25
+ const data = 'fulfilled' === rolesResult.status ? rolesResult.value : null;
26
+ const permissionPoints = 'fulfilled' === permissionPointsResult.status ? permissionPointsResult.value : void 0;
27
+ if (!data && !permissionPoints) throw 'rejected' === rolesResult.status ? rolesResult.reason : new Error('No permission data');
28
+ if (permissionPoints) setPermissions(permissionPoints);
29
+ const rawConfig = {
30
+ roles: data?.roles || [],
31
+ permissionPoints
32
+ };
33
+ const newAbility = createAbility(rawConfig);
25
34
  setAbility(newAbility);
26
- config?.onSuccess?.(data);
35
+ config?.onSuccess?.(rawConfig);
27
36
  } catch (err) {
28
37
  const error = err instanceof Error ? err : new Error(String(err));
29
38
  setError(error);
@@ -43,52 +52,67 @@ function AuthProvider({ children, config }) {
43
52
  ]);
44
53
  const stateContextValue = {
45
54
  ability,
55
+ permissions,
46
56
  isLoading,
47
57
  error,
48
58
  fetchPermissions
49
59
  };
50
60
  return /*#__PURE__*/ jsx(AbilityContext.Provider, {
51
61
  value: ability,
52
- children: /*#__PURE__*/ jsx(AuthStateContext.Provider, {
53
- value: stateContextValue,
54
- children: children
62
+ children: /*#__PURE__*/ jsx(PermissionsContext.Provider, {
63
+ value: permissions,
64
+ children: /*#__PURE__*/ jsx(AuthStateContext.Provider, {
65
+ value: stateContextValue,
66
+ children: children
67
+ })
55
68
  })
56
69
  });
57
70
  }
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
- }
72
71
  function useAuth() {
73
72
  const context = useContext(AuthStateContext);
74
73
  if (!context) throw new Error('useAuth must be used within an AuthProvider');
75
74
  return context;
76
75
  }
77
- function useAuthAbility() {
78
- return useContext(AbilityContext);
79
- }
80
- const Can = createContextualCan(AbilityContext.Consumer);
81
- const useCanRole = function({ roles }) {
76
+ function useCanRole({ roles }) {
82
77
  const ability = useContext(AbilityContext);
78
+ const authState = useContext(AuthStateContext);
83
79
  const allowed = !roles || 0 === roles.length || roles.length > 0 && roles.some((role)=>ability.can(role, ROLE_SUBJECT));
84
- return !!allowed;
85
- };
86
- function CanRole({ children, roles }) {
87
- const allowed = useCanRole({
80
+ return {
81
+ allowed: !!allowed,
82
+ isLoading: authState?.isLoading ?? false
83
+ };
84
+ }
85
+ function CanRole({ children, roles, fallback = null }) {
86
+ const { allowed, isLoading } = useCanRole({
88
87
  roles
89
88
  });
89
+ if (isLoading) return /*#__PURE__*/ jsx(Fragment, {
90
+ children: fallback
91
+ });
92
+ return allowed ? /*#__PURE__*/ jsx(Fragment, {
93
+ children: children
94
+ }) : null;
95
+ }
96
+ function useUserPermissions() {
97
+ return useContext(PermissionsContext);
98
+ }
99
+ function useCan(action, subject) {
100
+ const ability = useContext(AbilityContext);
101
+ const authState = useContext(AuthStateContext);
102
+ const isLoading = authState?.isLoading ?? false;
103
+ const allowed = !isLoading && ability.can(action, subject);
104
+ return {
105
+ allowed,
106
+ isLoading
107
+ };
108
+ }
109
+ function Can({ children, action, subject, fallback = null }) {
110
+ const { allowed, isLoading } = useCan(action, subject);
111
+ if (isLoading) return /*#__PURE__*/ jsx(Fragment, {
112
+ children: fallback
113
+ });
90
114
  return allowed ? /*#__PURE__*/ jsx(Fragment, {
91
115
  children: children
92
116
  }) : null;
93
117
  }
94
- export { AbilityContext, AuthProvider, Can, CanRole, getAbility, useAuth, useAuthAbility, useCanRole };
118
+ export { AbilityContext, AuthProvider, Can, CanRole, useAuth, useCan, useUserPermissions };
@@ -1,22 +1,22 @@
1
1
  import { MongoAbility as Ability } from '@casl/ability';
2
- import type { Permission, CaslRule } from './types';
2
+ import type { CaslRule, PermissionPointData } from './types';
3
3
  export declare const ROLE_SUBJECT = "@role";
4
4
  /**
5
- * 将权限数据转换为 CASL 规则
5
+ * 将角色和权限点位转换为 CASL 规则
6
6
  */
7
- export declare function convertPermissionsToRules(permissions: Permission[], roles: string[]): CaslRule[];
7
+ export declare function convertPermissionsToRules(roles: string[], permissionPoints?: PermissionPointData[]): CaslRule[];
8
8
  /**
9
9
  * 创建 MongoAbility 实例
10
10
  */
11
- export declare function createAbility({ permissions, roles, }: {
12
- permissions?: Permission[];
11
+ export declare function createAbility({ roles, permissionPoints, }: {
13
12
  roles?: string[];
13
+ permissionPoints?: PermissionPointData[];
14
14
  }): Ability;
15
15
  /**
16
16
  * 更新已存在的 Ability 实例
17
17
  */
18
- export declare function updateAbility(ability: Ability, { permissions, roles }: {
19
- permissions?: Permission[];
18
+ export declare function updateAbility(ability: Ability, { roles, permissionPoints }: {
20
19
  roles?: string[];
20
+ permissionPoints?: PermissionPointData[];
21
21
  }): void;
22
22
  //# sourceMappingURL=ability-factory.d.ts.map
@@ -1 +1 @@
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"}
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,QAAQ,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE7D,eAAO,MAAM,YAAY,UAAU,CAAC;AACpC;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,GACvC,QAAQ,EAAE,CAkBZ;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,gBAAgB,GACjB,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC1C,GAAG,OAAO,CAiBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAA;CAAE,GAC1F,IAAI,CAGN"}
@@ -1,26 +1,26 @@
1
1
  import { AbilityBuilder, createMongoAbility } from "@casl/ability";
2
2
  const ROLE_SUBJECT = '@role';
3
- function convertPermissionsToRules(permissions, roles) {
3
+ function convertPermissionsToRules(roles, permissionPoints) {
4
4
  const rules = [];
5
- for (const permission of permissions)for (const action of permission.actions)rules.push({
6
- action,
7
- subject: permission.sub
8
- });
9
5
  for (const role of roles)rules.push({
10
6
  action: role,
11
7
  subject: ROLE_SUBJECT
12
8
  });
9
+ if (permissionPoints) for (const { action, subject } of permissionPoints)rules.push({
10
+ action,
11
+ subject
12
+ });
13
13
  return rules;
14
14
  }
15
- function createAbility({ permissions, roles }) {
15
+ function createAbility({ roles, permissionPoints }) {
16
16
  const { build, can } = new AbilityBuilder(createMongoAbility);
17
- const rules = convertPermissionsToRules(permissions || [], roles || []);
17
+ const rules = convertPermissionsToRules(roles || [], permissionPoints);
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);
20
20
  return build();
21
21
  }
22
- function updateAbility(ability, { permissions, roles }) {
23
- const rules = convertPermissionsToRules(permissions || [], roles || []);
22
+ function updateAbility(ability, { roles, permissionPoints }) {
23
+ const rules = convertPermissionsToRules(roles || [], permissionPoints);
24
24
  ability.update(rules);
25
25
  }
26
26
  export { ROLE_SUBJECT, convertPermissionsToRules, createAbility, updateAbility };
package/lib/index.d.ts CHANGED
@@ -4,9 +4,9 @@
4
4
  * 基于 CASL 的前端鉴权 SDK
5
5
  * 封装了权限数据获取和 Ability 初始化逻辑
6
6
  */
7
- export type { PermissionApiResponse, PermissionApiConfig, AuthSdkConfig, } from './types';
7
+ export type { PermissionApiResponse, PermissionApiConfig, PermissionPointData, AuthSdkConfig, } from './types';
8
8
  export { ROLE_SUBJECT } from './ability-factory';
9
9
  export { PermissionClient } from './permission-client';
10
- export { AuthProvider, useAuth, CanRole, AbilityContext } from './AuthProvider';
10
+ export { AuthProvider, useAuth, Can, CanRole, useCan, AbilityContext, useUserPermissions, } from './AuthProvider';
11
11
  export type { AuthProviderProps } from './AuthProvider';
12
12
  //# 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,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhF,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,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,GAAG,EACH,OAAO,EACP,MAAM,EACN,cAAc,EACd,kBAAkB,GACnB,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, CanRole, useAuth } from "./AuthProvider.js";
4
- export { AbilityContext, AuthProvider, CanRole, PermissionClient, ROLE_SUBJECT, useAuth };
3
+ import { AbilityContext, AuthProvider, Can, CanRole, useAuth, useCan, useUserPermissions } from "./AuthProvider.js";
4
+ export { AbilityContext, AuthProvider, Can, CanRole, PermissionClient, ROLE_SUBJECT, useAuth, useCan, useUserPermissions };
@@ -1,4 +1,4 @@
1
- import type { PermissionApiConfig, PermissionApiResponse } from './types';
1
+ import type { PermissionApiConfig, PermissionApiResponse, PermissionPointData } from './types';
2
2
  /**
3
3
  * 权限数据获取客户端
4
4
  */
@@ -9,6 +9,11 @@ export declare class PermissionClient {
9
9
  * 获取用户权限数据
10
10
  */
11
11
  fetchPermissions(): Promise<PermissionApiResponse>;
12
+ /**
13
+ * 获取用户权限点位
14
+ * 请求内置的权限点位查询端点
15
+ */
16
+ static fetchPermissionPoints(): Promise<PermissionPointData[]>;
12
17
  /**
13
18
  * 更新配置
14
19
  */
@@ -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;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"}
1
+ {"version":3,"file":"permission-client.d.ts","sourceRoot":"","sources":["../src/permission-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAG/F;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,EAAE,mBAAmB;IAIvC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAqDxD;;;OAGG;WACU,qBAAqB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAwCpE;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;IAOxD;;OAEG;IACH,SAAS,IAAI,mBAAmB;CAGjC"}
@@ -42,6 +42,40 @@ class PermissionClient {
42
42
  throw error;
43
43
  }
44
44
  }
45
+ static async fetchPermissionPoints() {
46
+ const appId = window.appId || '';
47
+ const url = `/app/${appId}/api/sdk_innerapi/permission/user-points`;
48
+ const controller = new AbortController();
49
+ const timeoutId = setTimeout(()=>controller.abort(), 5000);
50
+ try {
51
+ const response = await fetch(url, {
52
+ method: 'GET',
53
+ headers: {
54
+ 'Content-Type': 'application/json',
55
+ 'X-Suda-Csrf-Token': window.csrfToken || ''
56
+ },
57
+ signal: controller.signal,
58
+ credentials: 'include'
59
+ });
60
+ clearTimeout(timeoutId);
61
+ if (!response.ok) throw new Error(`Permission Points API returned ${response.status}: ${response.statusText}`);
62
+ return await response.json();
63
+ } catch (error) {
64
+ clearTimeout(timeoutId);
65
+ if (error instanceof Error && 'AbortError' === error.name) {
66
+ slardar.captureException(error, {
67
+ source: 'auth-sdk',
68
+ type: 'timeout'
69
+ });
70
+ throw new Error('Permission Points API request timeout');
71
+ }
72
+ slardar.captureException(error, {
73
+ source: 'auth-sdk',
74
+ type: 'fetch'
75
+ });
76
+ throw error;
77
+ }
78
+ }
45
79
  updateConfig(config) {
46
80
  this.config = {
47
81
  ...this.config,
package/lib/types.d.ts CHANGED
@@ -14,13 +14,14 @@ export type Action = 'create' | 'read' | 'update' | 'delete' | 'manage' | string
14
14
  */
15
15
  export type Subject = string;
16
16
  /**
17
- * 单个权限定义
17
+ * 权限点位数据(action + subject 分离模式)
18
+ * 由内置 user-points 端点返回,注册到 CASL Ability
18
19
  */
19
- export interface Permission {
20
- id: string;
21
- name: string;
22
- sub: Subject;
23
- actions: Action[];
20
+ export interface PermissionPointData {
21
+ id?: string;
22
+ action: string;
23
+ subject: string;
24
+ description?: string;
24
25
  }
25
26
  /**
26
27
  * 用户角色定义
@@ -35,6 +36,8 @@ export interface UserRole {
35
36
  */
36
37
  export interface PermissionApiResponse {
37
38
  roles: string[];
39
+ /** 用户的有效权限点位列表 */
40
+ permissions?: string[];
38
41
  fetchedAt?: string | Date;
39
42
  }
40
43
  /**
@@ -1 +1 @@
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"}
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;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;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,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.2",
3
+ "version": "0.1.4",
4
4
  "description": "基于 CASL 的前端鉴权 SDK",
5
5
  "types": "./lib/index.d.ts",
6
6
  "main": "./lib/index.js",