@lark-apaas/auth-sdk 0.1.3 → 0.1.5-alpha.0
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/lib/AuthProvider.d.ts +31 -53
- package/lib/AuthProvider.d.ts.map +1 -1
- package/lib/AuthProvider.js +32 -48
- package/lib/ability-factory.d.ts +7 -7
- package/lib/ability-factory.d.ts.map +1 -1
- package/lib/ability-factory.js +9 -9
- package/lib/account/AccountClient.d.ts +22 -0
- package/lib/account/AccountClient.d.ts.map +1 -0
- package/lib/account/AccountClient.js +10 -0
- package/lib/account/BaseAccountService.d.ts +25 -0
- package/lib/account/BaseAccountService.d.ts.map +1 -0
- package/lib/account/BaseAccountService.js +106 -0
- package/lib/account/index.d.ts +5 -0
- package/lib/account/index.d.ts.map +1 -0
- package/lib/account/index.js +2 -0
- package/lib/account/internal/brand.d.ts +8 -0
- package/lib/account/internal/brand.d.ts.map +1 -0
- package/lib/account/internal/brand.js +3 -0
- package/lib/account/internal/helpers.d.ts +7 -0
- package/lib/account/internal/helpers.d.ts.map +1 -0
- package/lib/account/internal/helpers.js +14 -0
- package/lib/account/internal/logger.d.ts +15 -0
- package/lib/account/internal/logger.d.ts.map +1 -0
- package/lib/account/internal/logger.js +18 -0
- package/lib/account/internal/slardar.d.ts +2 -0
- package/lib/account/internal/slardar.d.ts.map +1 -0
- package/lib/account/internal/slardar.js +16 -0
- package/lib/account/internal/trace-id.d.ts +12 -0
- package/lib/account/internal/trace-id.d.ts.map +1 -0
- package/lib/account/internal/trace-id.js +20 -0
- package/lib/account/internal/types.d.ts +46 -0
- package/lib/account/internal/types.d.ts.map +1 -0
- package/lib/account/internal/types.js +20 -0
- package/lib/account/services/session-service/index.d.ts +34 -0
- package/lib/account/services/session-service/index.d.ts.map +1 -0
- package/lib/account/services/session-service/index.js +137 -0
- package/lib/account/services/session-service/types.d.ts +30 -0
- package/lib/account/services/session-service/types.d.ts.map +1 -0
- package/lib/account/services/session-service/types.js +0 -0
- package/lib/account/services/user-service/index.d.ts +9 -0
- package/lib/account/services/user-service/index.d.ts.map +1 -0
- package/lib/account/services/user-service/index.js +60 -0
- package/lib/account/services/user-service/types.d.ts +51 -0
- package/lib/account/services/user-service/types.d.ts.map +1 -0
- package/lib/account/services/user-service/types.js +0 -0
- package/lib/index.d.ts +10 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -2
- package/lib/permission-client.d.ts +4 -3
- package/lib/permission-client.d.ts.map +1 -1
- package/lib/permission-client.js +22 -14
- package/lib/types.d.ts +7 -11
- package/lib/types.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/AuthProvider.d.ts
CHANGED
|
@@ -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,7 +11,7 @@ export declare const AbilityContext: React.Context<MongoAbility<import("@casl/ab
|
|
|
11
11
|
*/
|
|
12
12
|
interface AuthStateContextValue {
|
|
13
13
|
ability: MongoAbility;
|
|
14
|
-
permissions:
|
|
14
|
+
permissions: PermissionPointData[];
|
|
15
15
|
isLoading: boolean;
|
|
16
16
|
error: Error | null;
|
|
17
17
|
fetchPermissions: (userId?: string) => Promise<void>;
|
|
@@ -46,13 +46,6 @@ export interface AuthProviderProps {
|
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
48
|
export declare function AuthProvider({ children, config }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
49
|
-
/**
|
|
50
|
-
* 获取 Ability 实例
|
|
51
|
-
*
|
|
52
|
-
* @param permissionApiConfig - 权限 API 配置
|
|
53
|
-
* @returns Ability 实例或错误
|
|
54
|
-
*/
|
|
55
|
-
export declare function getAbility(permissionApiConfig: AuthSdkConfig['permissionApi']): Promise<Error | MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
|
|
56
49
|
/**
|
|
57
50
|
* useAuth Hook - 获取权限数据和加载状态
|
|
58
51
|
*
|
|
@@ -74,32 +67,6 @@ export declare function getAbility(permissionApiConfig: AuthSdkConfig['permissio
|
|
|
74
67
|
* ```
|
|
75
68
|
*/
|
|
76
69
|
export declare function useAuth(): AuthStateContextValue;
|
|
77
|
-
/**
|
|
78
|
-
* CASL 原始 Can 组件(内部使用)
|
|
79
|
-
*/
|
|
80
|
-
declare const CaslCan: React.FunctionComponent<import("@casl/react").BoundCanProps<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
|
|
81
|
-
/**
|
|
82
|
-
* Can Component - 基于 Ability 实例的条件渲染组件
|
|
83
|
-
*
|
|
84
|
-
* 内置 isLoading 保护:权限加载期间渲染 fallback(默认为 null),
|
|
85
|
-
* 避免因 ability 未就绪而误判为无权限导致内容闪烁。
|
|
86
|
-
*
|
|
87
|
-
* @example
|
|
88
|
-
* ```tsx
|
|
89
|
-
* import { Can } from '@lark-apaas/auth-sdk';
|
|
90
|
-
*
|
|
91
|
-
* function MyComponent() {
|
|
92
|
-
* return (
|
|
93
|
-
* <Can I="Admin" a="@role" fallback={<Loading />}>
|
|
94
|
-
* <TaskList />
|
|
95
|
-
* </Can>
|
|
96
|
-
* );
|
|
97
|
-
* }
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
export declare function Can({ fallback, ...props }: React.ComponentProps<typeof CaslCan> & {
|
|
101
|
-
fallback?: React.ReactNode;
|
|
102
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
103
70
|
/**
|
|
104
71
|
* CanRole Component - 基于角色的条件渲染组件
|
|
105
72
|
*
|
|
@@ -127,35 +94,46 @@ export declare function CanRole({ children, roles, fallback, }: {
|
|
|
127
94
|
/**
|
|
128
95
|
* useUserPermissions Hook - 获取当前用户的权限点位列表
|
|
129
96
|
*/
|
|
130
|
-
export declare function useUserPermissions():
|
|
97
|
+
export declare function useUserPermissions(): PermissionPointData[];
|
|
131
98
|
/**
|
|
132
|
-
*
|
|
99
|
+
* useCan Hook - 通过 CASL Ability 判断用户是否拥有指定权限
|
|
133
100
|
*
|
|
134
|
-
* @param
|
|
135
|
-
* @param
|
|
101
|
+
* @param action - 操作类型,如 'read', 'create'
|
|
102
|
+
* @param subject - 资源类型,如 'Task', 'Article';角色检查时传 '@role'
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const { allowed: canRead, isLoading } = useCan('read', 'Task');
|
|
107
|
+
* if (isLoading) return <Loading />;
|
|
108
|
+
* if (!canRead) return <NoAccess />;
|
|
109
|
+
* ```
|
|
136
110
|
*/
|
|
137
|
-
export declare function
|
|
111
|
+
export declare function useCan(action: string, subject: string): {
|
|
112
|
+
allowed: boolean;
|
|
113
|
+
isLoading: boolean;
|
|
114
|
+
};
|
|
138
115
|
/**
|
|
139
|
-
*
|
|
116
|
+
* Can Component - 基于 CASL Ability 的条件渲染组件
|
|
117
|
+
* 前后端统一:后端 @Can('read', 'Task'),前端 <Can action="read" subject="Task">
|
|
140
118
|
*
|
|
141
119
|
* @example
|
|
142
120
|
* ```tsx
|
|
143
|
-
* import {
|
|
121
|
+
* import { Can } from '@lark-apaas/auth-sdk';
|
|
144
122
|
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
123
|
+
* <Can action="read" subject="Task">
|
|
124
|
+
* <TaskList />
|
|
125
|
+
* </Can>
|
|
126
|
+
*
|
|
127
|
+
* <Can action="delete" subject="Task" fallback={<Skeleton />}>
|
|
128
|
+
* <DeleteButton />
|
|
129
|
+
* </Can>
|
|
152
130
|
* ```
|
|
153
131
|
*/
|
|
154
|
-
export declare function
|
|
132
|
+
export declare function Can({ children, action, subject, fallback, }: {
|
|
155
133
|
children: React.ReactNode;
|
|
156
|
-
|
|
157
|
-
|
|
134
|
+
action: string;
|
|
135
|
+
subject: string;
|
|
158
136
|
fallback?: React.ReactNode;
|
|
159
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
137
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
160
138
|
export {};
|
|
161
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;
|
|
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"}
|
package/lib/AuthProvider.js
CHANGED
|
@@ -2,10 +2,9 @@ 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
|
-
|
|
8
|
-
|
|
6
|
+
roles: [],
|
|
7
|
+
permissionPoints: []
|
|
9
8
|
}));
|
|
10
9
|
const PermissionsContext = /*#__PURE__*/ createContext([]);
|
|
11
10
|
const AuthStateContext = /*#__PURE__*/ createContext(null);
|
|
@@ -16,20 +15,24 @@ function AuthProvider({ children, config }) {
|
|
|
16
15
|
const [isLoading, setIsLoading] = useState(()=>config?.enable !== false && null !== client);
|
|
17
16
|
const [error, setError] = useState(null);
|
|
18
17
|
const fetchPermissions = useCallback(async ()=>{
|
|
19
|
-
if (!client) return;
|
|
20
18
|
setIsLoading(true);
|
|
21
19
|
setError(null);
|
|
22
20
|
try {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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);
|
|
27
34
|
setAbility(newAbility);
|
|
28
|
-
|
|
29
|
-
const userPermissions = await client.fetchUserPermissions(config.permissionPointsApi);
|
|
30
|
-
setPermissions(userPermissions);
|
|
31
|
-
} catch {}
|
|
32
|
-
config?.onSuccess?.(data);
|
|
35
|
+
config?.onSuccess?.(rawConfig);
|
|
33
36
|
} catch (err) {
|
|
34
37
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
35
38
|
setError(error);
|
|
@@ -65,35 +68,11 @@ function AuthProvider({ children, config }) {
|
|
|
65
68
|
})
|
|
66
69
|
});
|
|
67
70
|
}
|
|
68
|
-
async function getAbility(permissionApiConfig) {
|
|
69
|
-
if (!permissionApiConfig) return new Error('permissionApi config is required');
|
|
70
|
-
const client = new PermissionClient(permissionApiConfig);
|
|
71
|
-
try {
|
|
72
|
-
const data = await client.fetchPermissions();
|
|
73
|
-
const ability = createAbility({
|
|
74
|
-
roles: data.roles
|
|
75
|
-
});
|
|
76
|
-
return ability;
|
|
77
|
-
} catch (err) {
|
|
78
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
79
|
-
return error;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
71
|
function useAuth() {
|
|
83
72
|
const context = useContext(AuthStateContext);
|
|
84
73
|
if (!context) throw new Error('useAuth must be used within an AuthProvider');
|
|
85
74
|
return context;
|
|
86
75
|
}
|
|
87
|
-
const CaslCan = createContextualCan(AbilityContext.Consumer);
|
|
88
|
-
function Can({ fallback = null, ...props }) {
|
|
89
|
-
const authState = useContext(AuthStateContext);
|
|
90
|
-
if (authState?.isLoading) return /*#__PURE__*/ jsx(Fragment, {
|
|
91
|
-
children: fallback
|
|
92
|
-
});
|
|
93
|
-
return /*#__PURE__*/ jsx(CaslCan, {
|
|
94
|
-
...props
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
76
|
function useCanRole({ roles }) {
|
|
98
77
|
const ability = useContext(AbilityContext);
|
|
99
78
|
const authState = useContext(AuthStateContext);
|
|
@@ -117,18 +96,23 @@ function CanRole({ children, roles, fallback = null }) {
|
|
|
117
96
|
function useUserPermissions() {
|
|
118
97
|
return useContext(PermissionsContext);
|
|
119
98
|
}
|
|
120
|
-
function
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
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
|
+
};
|
|
125
108
|
}
|
|
126
|
-
function
|
|
127
|
-
const allowed =
|
|
128
|
-
|
|
129
|
-
children: children
|
|
130
|
-
}) : /*#__PURE__*/ jsx(Fragment, {
|
|
109
|
+
function Can({ children, action, subject, fallback = null }) {
|
|
110
|
+
const { allowed, isLoading } = useCan(action, subject);
|
|
111
|
+
if (isLoading) return /*#__PURE__*/ jsx(Fragment, {
|
|
131
112
|
children: fallback
|
|
132
113
|
});
|
|
114
|
+
return allowed ? /*#__PURE__*/ jsx(Fragment, {
|
|
115
|
+
children: children
|
|
116
|
+
}) : null;
|
|
133
117
|
}
|
|
134
|
-
export { AbilityContext, AuthProvider, Can,
|
|
118
|
+
export { AbilityContext, AuthProvider, Can, CanRole, useAuth, useCan, useUserPermissions };
|
package/lib/ability-factory.d.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { MongoAbility as Ability } from '@casl/ability';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CaslRule, PermissionPointData } from './types';
|
|
3
3
|
export declare const ROLE_SUBJECT = "@role";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* 将角色和权限点位转换为 CASL 规则
|
|
6
6
|
*/
|
|
7
|
-
export declare function convertPermissionsToRules(
|
|
7
|
+
export declare function convertPermissionsToRules(roles: string[], permissionPoints?: PermissionPointData[]): CaslRule[];
|
|
8
8
|
/**
|
|
9
9
|
* 创建 MongoAbility 实例
|
|
10
10
|
*/
|
|
11
|
-
export declare function createAbility({
|
|
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, {
|
|
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,
|
|
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"}
|
package/lib/ability-factory.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
2
2
|
const ROLE_SUBJECT = '@role';
|
|
3
|
-
function convertPermissionsToRules(
|
|
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({
|
|
15
|
+
function createAbility({ roles, permissionPoints }) {
|
|
16
16
|
const { build, can } = new AbilityBuilder(createMongoAbility);
|
|
17
|
-
const rules = convertPermissionsToRules(
|
|
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, {
|
|
23
|
-
const rules = convertPermissionsToRules(
|
|
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 };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import SessionService from './services/session-service';
|
|
2
|
+
import UserService from './services/user-service';
|
|
3
|
+
/**
|
|
4
|
+
* 妙搭账户/会话能力 SDK,零参构造。
|
|
5
|
+
*
|
|
6
|
+
* 业务调用方应通过 `authClient` singleton 使用(见 src/index.ts):
|
|
7
|
+
* import { authClient } from '@lark-apaas/auth-sdk';
|
|
8
|
+
* await authClient.user.search({ name: '张三' });
|
|
9
|
+
*
|
|
10
|
+
* 直接 `new AccountClient()` 仅用于测试或特殊场景(多实例隔离)。
|
|
11
|
+
*
|
|
12
|
+
* 构造行为:
|
|
13
|
+
* - `appId` 从 `globalThis.appId` 读取(与现有 toolkit 的 `getAppId()` 一致)
|
|
14
|
+
* - HTTP 走相对路径 + `credentials: 'include'`,使用浏览器本域 session cookie
|
|
15
|
+
* - 不接受任何外部配置(baseUrl/token/brandName/fetch/log/errorHook 全部内化或默认化)
|
|
16
|
+
*/
|
|
17
|
+
export declare class AccountClient {
|
|
18
|
+
readonly user: UserService;
|
|
19
|
+
readonly session: SessionService;
|
|
20
|
+
constructor();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=AccountClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccountClient.d.ts","sourceRoot":"","sources":["../../src/account/AccountClient.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,4BAA4B,CAAC;AACxD,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;;CAOlC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import session_service from "./services/session-service/index.js";
|
|
2
|
+
import user_service from "./services/user-service/index.js";
|
|
3
|
+
class AccountClient {
|
|
4
|
+
constructor(){
|
|
5
|
+
const appId = globalThis.appId ?? '';
|
|
6
|
+
this.user = new user_service(appId);
|
|
7
|
+
this.session = new session_service(appId);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export { AccountClient };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AccountServiceResponse, type ExtraInfo, type Fetch, type FetchOptions, ServiceName } from './internal/types';
|
|
2
|
+
/**
|
|
3
|
+
* Base for AC services. Owns the fetch pipeline:
|
|
4
|
+
* - Injects X-Suda-Csrf-Token from cookie
|
|
5
|
+
* - Sends body as JSON with credentials: 'include' (session cookie auth)
|
|
6
|
+
* - Normalizes responses into AccountServiceResponse<T>
|
|
7
|
+
* - Logs requests/responses in development
|
|
8
|
+
*
|
|
9
|
+
* 不暴露 baseUrl/token/brandName/fetch 参数(C5 二轮评审决议)。
|
|
10
|
+
*/
|
|
11
|
+
export default abstract class BaseAccountService {
|
|
12
|
+
protected baseUrl: string;
|
|
13
|
+
protected headers: {
|
|
14
|
+
[key: string]: string;
|
|
15
|
+
};
|
|
16
|
+
protected fetch: Fetch;
|
|
17
|
+
protected logger: boolean;
|
|
18
|
+
protected serviceName: ServiceName;
|
|
19
|
+
protected brandId: string;
|
|
20
|
+
protected bizSource: string;
|
|
21
|
+
protected appId: string;
|
|
22
|
+
constructor(appId: string);
|
|
23
|
+
fetchData<T>(url: string, options: FetchOptions, extras?: ExtraInfo): Promise<AccountServiceResponse<T>>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=BaseAccountService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseAccountService.d.ts","sourceRoot":"","sources":["../../src/account/BaseAccountService.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,SAAS,EACd,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,kBAAkB;IAC9C,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAC7C,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;IACvB,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC;IACnC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEZ,KAAK,EAAE,MAAM;IAenB,SAAS,CAAC,CAAC,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,EACrB,MAAM,CAAC,EAAE,SAAS,GACjB,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;CA8GtC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { BIZ_SOURCE, BRAND_ID } from "./internal/brand.js";
|
|
2
|
+
import { getCookieByName } from "./internal/helpers.js";
|
|
3
|
+
import { logRequest, logResponse } from "./internal/logger.js";
|
|
4
|
+
import { traceIdGenerator } from "./internal/trace-id.js";
|
|
5
|
+
import { ServiceName } from "./internal/types.js";
|
|
6
|
+
class BaseAccountService {
|
|
7
|
+
constructor(appId){
|
|
8
|
+
this.baseUrl = 'undefined' != typeof window ? window.location.origin : '';
|
|
9
|
+
this.headers = {
|
|
10
|
+
'X-Suda-Csrf-Token': getCookieByName('suda-csrf-token'),
|
|
11
|
+
'Content-Type': 'application/json'
|
|
12
|
+
};
|
|
13
|
+
this.logger = 'production' !== process.env.NODE_ENV;
|
|
14
|
+
this.serviceName = ServiceName.DEFAULT;
|
|
15
|
+
this.brandId = BRAND_ID;
|
|
16
|
+
this.bizSource = BIZ_SOURCE;
|
|
17
|
+
this.appId = appId;
|
|
18
|
+
this.fetch = globalThis.fetch.bind(globalThis);
|
|
19
|
+
}
|
|
20
|
+
async fetchData(url, options, extras) {
|
|
21
|
+
const fetchUrl = `${this.baseUrl}${url}`;
|
|
22
|
+
let traceId = -1;
|
|
23
|
+
if (this.logger) {
|
|
24
|
+
traceId = traceIdGenerator.next();
|
|
25
|
+
logRequest({
|
|
26
|
+
traceId,
|
|
27
|
+
url: fetchUrl,
|
|
28
|
+
method: options.method,
|
|
29
|
+
body: options.body
|
|
30
|
+
}, extras?.psmName);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const res = await this.fetch(fetchUrl, {
|
|
34
|
+
method: options.method,
|
|
35
|
+
mode: 'cors',
|
|
36
|
+
redirect: 'manual',
|
|
37
|
+
credentials: 'include',
|
|
38
|
+
headers: this.headers,
|
|
39
|
+
body: JSON.stringify(options.body)
|
|
40
|
+
});
|
|
41
|
+
let error = null;
|
|
42
|
+
let data = null;
|
|
43
|
+
let status = res.status;
|
|
44
|
+
let statusText = res.statusText;
|
|
45
|
+
if (res.ok) {
|
|
46
|
+
const body = await res.text();
|
|
47
|
+
const parsedBody = JSON.parse(body);
|
|
48
|
+
if (parsedBody.status_code && parsedBody.error_msg) {
|
|
49
|
+
data = null;
|
|
50
|
+
error = {
|
|
51
|
+
code: parsedBody.status_code || res.status,
|
|
52
|
+
details: '',
|
|
53
|
+
hint: '',
|
|
54
|
+
message: parsedBody.error_msg || body
|
|
55
|
+
};
|
|
56
|
+
} else {
|
|
57
|
+
data = parsedBody.data;
|
|
58
|
+
error = null;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
const body = await res.text();
|
|
62
|
+
let parsedBody = {};
|
|
63
|
+
try {
|
|
64
|
+
parsedBody = JSON.parse(body);
|
|
65
|
+
} catch {}
|
|
66
|
+
error = 429 === res.status ? {
|
|
67
|
+
code: 429,
|
|
68
|
+
message: 'Too many requests received within 5 seconds.',
|
|
69
|
+
details: '',
|
|
70
|
+
hint: 'Retry after 5 seconds.'
|
|
71
|
+
} : {
|
|
72
|
+
code: parsedBody.status_code || res.status,
|
|
73
|
+
message: parsedBody.error_msg || body,
|
|
74
|
+
details: '',
|
|
75
|
+
hint: ''
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const serviceResponse = {
|
|
79
|
+
data,
|
|
80
|
+
error,
|
|
81
|
+
status,
|
|
82
|
+
statusText
|
|
83
|
+
};
|
|
84
|
+
if (this.logger) logResponse(traceId, serviceResponse, extras?.psmName);
|
|
85
|
+
return serviceResponse;
|
|
86
|
+
} catch (e) {
|
|
87
|
+
const internalError = {
|
|
88
|
+
error: {
|
|
89
|
+
code: 500,
|
|
90
|
+
message: e instanceof Error ? e.message : String(e),
|
|
91
|
+
details: '',
|
|
92
|
+
hint: ''
|
|
93
|
+
},
|
|
94
|
+
status: 500,
|
|
95
|
+
statusText: 'Account Internal Error'
|
|
96
|
+
};
|
|
97
|
+
const serviceResponse = {
|
|
98
|
+
data: null,
|
|
99
|
+
...internalError
|
|
100
|
+
};
|
|
101
|
+
if (this.logger) logResponse(traceId, serviceResponse, extras?.psmName);
|
|
102
|
+
return serviceResponse;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export { BaseAccountService as default };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AccountClient } from './AccountClient';
|
|
2
|
+
export type { AccountServiceResponse, AccountServiceError, AccountStatus, UserStatus, } from './internal/types';
|
|
3
|
+
export type { SearchParams, User, UserResponse, I18n as UserI18n, } from './services/user-service/types';
|
|
4
|
+
export type { SignInRedirectionOptions, UserBaseInfo, UserInfoResponse, Avatar, I18n as SessionI18n, I18ns as SessionI18ns, } from './services/session-service/types';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/account/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,UAAU,GACX,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,YAAY,EACZ,IAAI,EACJ,YAAY,EACZ,IAAI,IAAI,QAAQ,GACjB,MAAM,+BAA+B,CAAC;AAGvC,YAAY,EACV,wBAAwB,EACxB,YAAY,EACZ,gBAAgB,EAChB,MAAM,EACN,IAAI,IAAI,WAAW,EACnB,KAAK,IAAI,YAAY,GACtB,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 妙搭 (Miaoda) brand constants — hard-coded after C5 review.
|
|
3
|
+
* 历史上 `brandName` 是参数,serializeSessionBrandId('miaoda') === '1'。
|
|
4
|
+
* 现 auth-sdk 仅服务妙搭场景,相关常量直接内化。
|
|
5
|
+
*/
|
|
6
|
+
export declare const BIZ_SOURCE = "miaoda";
|
|
7
|
+
export declare const BRAND_ID = "1";
|
|
8
|
+
//# sourceMappingURL=brand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../../src/account/internal/brand.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,UAAU,WAAW,CAAC;AACnC,eAAO,MAAM,QAAQ,MAAM,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AccountStatus, UserStatus } from './types';
|
|
2
|
+
import type { I18n } from '../services/user-service/types';
|
|
3
|
+
export declare const isBrowser: () => boolean;
|
|
4
|
+
export declare const getCookieByName: (name: string) => string;
|
|
5
|
+
export declare const retrieveNameFromI18n: (i18ns?: I18n) => string;
|
|
6
|
+
export declare const convertUserStatus: (status?: AccountStatus) => UserStatus;
|
|
7
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/account/internal/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AAE3D,eAAO,MAAM,SAAS,eAC4C,CAAC;AAEnE,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MAW9C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,IAAI,WAEhD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,aAAa,KAAG,UAY1D,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AccountStatus, UserStatus } from "./types.js";
|
|
2
|
+
const isBrowser = ()=>'undefined' != typeof window && 'undefined' != typeof document;
|
|
3
|
+
const getCookieByName = (name)=>{
|
|
4
|
+
if (!isBrowser()) return '';
|
|
5
|
+
const cookies = document.cookie.split(';');
|
|
6
|
+
return cookies.find((cookie)=>cookie.trim().startsWith(`${name}=`))?.split('=')[1] ?? '';
|
|
7
|
+
};
|
|
8
|
+
const retrieveNameFromI18n = (i18ns)=>i18ns?.zh_cn || i18ns?.en_us || i18ns?.ja_jp || '';
|
|
9
|
+
const convertUserStatus = (status)=>{
|
|
10
|
+
if (status === AccountStatus.Active || status === AccountStatus.Inactive) return UserStatus.active;
|
|
11
|
+
if (status === AccountStatus.Disabled || status === AccountStatus.Terminated) return UserStatus.inactive;
|
|
12
|
+
return UserStatus.active;
|
|
13
|
+
};
|
|
14
|
+
export { convertUserStatus, getCookieByName, isBrowser, retrieveNameFromI18n };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FetchMethod } from './types';
|
|
2
|
+
type LogParams = {
|
|
3
|
+
traceId: number;
|
|
4
|
+
url: string;
|
|
5
|
+
method: FetchMethod;
|
|
6
|
+
body?: unknown;
|
|
7
|
+
};
|
|
8
|
+
export declare const logRequest: (params: LogParams, psmName?: string) => void;
|
|
9
|
+
export declare const logResponse: (traceId: number, response: {
|
|
10
|
+
data: unknown;
|
|
11
|
+
error: unknown;
|
|
12
|
+
status: number;
|
|
13
|
+
}, psmName?: string) => void;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/account/internal/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,KAAK,SAAS,GAAG;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,SAAS,EAAE,UAAS,MAAkB,SAuBxE,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,SAAS,MAAM,EACf,UAAU;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC3D,UAAS,MAAkB,SA0B5B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const logRequest = (params, psmName = 'Service')=>{
|
|
2
|
+
const { traceId, url, method, body } = params;
|
|
3
|
+
const decodedParams = Array.from(new URL(url, 'http://temp.invalid').searchParams.entries()).filter(([key])=>![
|
|
4
|
+
'count',
|
|
5
|
+
'tx',
|
|
6
|
+
'missing',
|
|
7
|
+
'resolution',
|
|
8
|
+
'column'
|
|
9
|
+
].includes(key)).map(([key, value])=>`${key}=${value}`);
|
|
10
|
+
console.log(`[Account][${psmName}][Request]`, 'trace id:', traceId, 'method:', method, 'params:', decodedParams.join(','), 'body:', body);
|
|
11
|
+
};
|
|
12
|
+
const logResponse = (traceId, response, psmName = 'Service')=>{
|
|
13
|
+
const { data, error, status } = response;
|
|
14
|
+
const hasError = !!error;
|
|
15
|
+
if (hasError) console.error(`[Account][${psmName}][Response Failed]`, 'trace id:', traceId, 'statusCode:', status, 'error:', error);
|
|
16
|
+
else console.log(`[Account][${psmName}][Response Success]`, 'trace id:', traceId, 'statusCode:', status, 'data:', data);
|
|
17
|
+
};
|
|
18
|
+
export { logRequest, logResponse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slardar.d.ts","sourceRoot":"","sources":["../../../src/account/internal/slardar.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,sBAAsB,GACjC,OAAO,OAAO,EACd,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAa/B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { slardar } from "@lark-apaas/internal-slardar";
|
|
2
|
+
const toRecordString = (record)=>Object.fromEntries(Object.entries(record).filter(([, value])=>null != value).map(([key, value])=>[
|
|
3
|
+
key,
|
|
4
|
+
String(value)
|
|
5
|
+
]));
|
|
6
|
+
const reportAccountException = (error, extra)=>{
|
|
7
|
+
try {
|
|
8
|
+
const categories = toRecordString({
|
|
9
|
+
source: 'auth-sdk',
|
|
10
|
+
module: 'account',
|
|
11
|
+
...extra
|
|
12
|
+
});
|
|
13
|
+
slardar.captureException(error, categories);
|
|
14
|
+
} catch {}
|
|
15
|
+
};
|
|
16
|
+
export { reportAccountException };
|