huace-aigc-auth-client 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl
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.
- huace_aigc_auth_client/__init__.py +9 -1
- huace_aigc_auth_client/legacy_adapter.py +5 -2
- huace_aigc_auth_client/webhook.py +128 -0
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.6.dist-info}/METADATA +1 -1
- huace_aigc_auth_client-1.1.6.dist-info/RECORD +9 -0
- huace_aigc_auth_client-1.1.4.dist-info/RECORD +0 -8
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.6.dist-info}/WHEEL +0 -0
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.6.dist-info}/top_level.txt +0 -0
|
@@ -68,6 +68,11 @@ from .legacy_adapter import (
|
|
|
68
68
|
create_default_field_mappings,
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
+
from .webhook import (
|
|
72
|
+
register_webhook_router,
|
|
73
|
+
verify_webhook_signature,
|
|
74
|
+
)
|
|
75
|
+
|
|
71
76
|
__all__ = [
|
|
72
77
|
# 核心类
|
|
73
78
|
"AigcAuthClient",
|
|
@@ -89,5 +94,8 @@ __all__ = [
|
|
|
89
94
|
"SyncResult",
|
|
90
95
|
"create_sync_config",
|
|
91
96
|
"create_default_field_mappings",
|
|
97
|
+
# Webhook 接收
|
|
98
|
+
"register_webhook_router",
|
|
99
|
+
"verify_webhook_signature",
|
|
92
100
|
]
|
|
93
|
-
__version__ = "1.1.
|
|
101
|
+
__version__ = "1.1.6"
|
|
@@ -195,14 +195,14 @@ class LegacySystemAdapter(ABC):
|
|
|
195
195
|
"""
|
|
196
196
|
raise NotImplementedError("Subclass must implement get_all_users_async method")
|
|
197
197
|
|
|
198
|
-
async def upsert_user_async(self, user_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
198
|
+
async def upsert_user_async(self, user_data: Dict[str, Any], auth_data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
199
199
|
"""异步创建或更新用户(存在则更新,不存在则新增)
|
|
200
200
|
|
|
201
201
|
这是一个默认实现,子类可以选择性覆盖以优化性能。
|
|
202
202
|
|
|
203
203
|
Args:
|
|
204
204
|
user_data: 用户数据字典(必须包含 username)
|
|
205
|
-
|
|
205
|
+
auth_data: 鉴权系统用户数据字典(可选)
|
|
206
206
|
Returns:
|
|
207
207
|
Dict: 操作结果 {"created": bool, "user_id": Any}
|
|
208
208
|
"""
|
|
@@ -215,6 +215,9 @@ class LegacySystemAdapter(ABC):
|
|
|
215
215
|
|
|
216
216
|
if existing:
|
|
217
217
|
# 用户存在,执行更新
|
|
218
|
+
if not auth_data or auth_data.get("updatedFields") is None or len(auth_data.get("updatedFields")) == 0:
|
|
219
|
+
# 如果没有提供 auth_data 或 updatedFields,则不更新
|
|
220
|
+
return {"created": False, "user_id": existing.get("id")}
|
|
218
221
|
await self._update_user_async(username, user_data)
|
|
219
222
|
return {"created": False, "user_id": existing.get("id")}
|
|
220
223
|
else:
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Webhook 接收模块
|
|
4
|
+
|
|
5
|
+
提供通用的 webhook 接收功能,用于接收 aigc-auth 的用户变更通知
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import hmac
|
|
9
|
+
import hashlib
|
|
10
|
+
import os
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Callable, Awaitable, Dict, Any, Optional
|
|
13
|
+
from fastapi import APIRouter, Request, HTTPException
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
|
|
19
|
+
"""
|
|
20
|
+
验证 webhook 签名
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
payload: 请求体(bytes)
|
|
24
|
+
signature: 签名字符串
|
|
25
|
+
secret: 密钥
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
bool: 签名是否有效
|
|
29
|
+
"""
|
|
30
|
+
if not secret or not signature:
|
|
31
|
+
return False # 如果未配置密钥,则签名无效
|
|
32
|
+
|
|
33
|
+
expected = hmac.new(
|
|
34
|
+
secret.encode('utf-8'),
|
|
35
|
+
payload, # payload 已经是 bytes 类型
|
|
36
|
+
hashlib.sha256
|
|
37
|
+
).hexdigest()
|
|
38
|
+
|
|
39
|
+
return hmac.compare_digest(expected, signature)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def register_webhook_router(
|
|
43
|
+
api_router: APIRouter,
|
|
44
|
+
handler: Callable[[Dict[str, Any]], Awaitable[Dict[str, Any]]],
|
|
45
|
+
prefix: str = "/webhook",
|
|
46
|
+
secret_env_key: str = "AIGC_AUTH_WEBHOOK_SECRET",
|
|
47
|
+
tags: Optional[list] = None
|
|
48
|
+
) -> APIRouter:
|
|
49
|
+
"""
|
|
50
|
+
注册 webhook 路由到现有的 API Router
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
api_router: FastAPI APIRouter 实例
|
|
54
|
+
handler: 异步处理函数,接收 webhook 数据并返回结果
|
|
55
|
+
函数签名: async def handler(data: Dict[str, Any]) -> Dict[str, Any]
|
|
56
|
+
prefix: webhook 路由前缀,默认 "/webhook"
|
|
57
|
+
secret_env_key: webhook 密钥的环境变量名,默认 "AIGC_AUTH_WEBHOOK_SECRET"
|
|
58
|
+
tags: OpenAPI 标签列表,默认 ["Webhook"]
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
APIRouter: 创建的 webhook router
|
|
62
|
+
|
|
63
|
+
使用示例:
|
|
64
|
+
from fastapi import APIRouter
|
|
65
|
+
from huace_aigc_auth_client.webhook import register_webhook_router
|
|
66
|
+
|
|
67
|
+
api_router = APIRouter()
|
|
68
|
+
|
|
69
|
+
async def my_webhook_handler(data: dict) -> dict:
|
|
70
|
+
event = data.get("event")
|
|
71
|
+
if event == "user.created":
|
|
72
|
+
# 处理用户创建事件
|
|
73
|
+
pass
|
|
74
|
+
return {"status": "ok"}
|
|
75
|
+
|
|
76
|
+
register_webhook_router(api_router, my_webhook_handler)
|
|
77
|
+
"""
|
|
78
|
+
if tags is None:
|
|
79
|
+
tags = ["Webhook"]
|
|
80
|
+
|
|
81
|
+
webhook_router = APIRouter()
|
|
82
|
+
|
|
83
|
+
@webhook_router.post("/auth")
|
|
84
|
+
async def receive_auth_webhook(request: Request):
|
|
85
|
+
"""
|
|
86
|
+
接收 aigc-auth 用户变更通知
|
|
87
|
+
|
|
88
|
+
当 aigc-auth 中创建或更新用户时,会发送 webhook 通知到此端点,
|
|
89
|
+
本系统接收后会调用自定义的处理函数进行处理。
|
|
90
|
+
|
|
91
|
+
支持的事件:
|
|
92
|
+
- user.created: 用户创建
|
|
93
|
+
- user.updated: 用户更新
|
|
94
|
+
"""
|
|
95
|
+
# 获取请求体
|
|
96
|
+
body = await request.body()
|
|
97
|
+
|
|
98
|
+
# 验证签名
|
|
99
|
+
signature = request.headers.get("X-Webhook-Signature", "")
|
|
100
|
+
secret = os.getenv(secret_env_key, "")
|
|
101
|
+
|
|
102
|
+
if not verify_webhook_signature(body, signature, secret):
|
|
103
|
+
logger.warning("Webhook signature verification failed")
|
|
104
|
+
raise HTTPException(status_code=401, detail="Invalid signature")
|
|
105
|
+
|
|
106
|
+
# 解析请求数据
|
|
107
|
+
try:
|
|
108
|
+
data = await request.json()
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.error(f"Failed to parse webhook payload: {e}")
|
|
111
|
+
raise HTTPException(status_code=400, detail="Invalid JSON payload")
|
|
112
|
+
|
|
113
|
+
# 记录日志
|
|
114
|
+
event = data.get("event", "unknown")
|
|
115
|
+
logger.info(f"Received webhook event: {event}")
|
|
116
|
+
|
|
117
|
+
# 调用用户提供的处理函数
|
|
118
|
+
try:
|
|
119
|
+
result = await handler(data)
|
|
120
|
+
return result
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.exception(f"Failed to handle webhook: {e}")
|
|
123
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
124
|
+
|
|
125
|
+
# 将 webhook router 注册到主 router
|
|
126
|
+
api_router.include_router(webhook_router, prefix=prefix, tags=tags)
|
|
127
|
+
|
|
128
|
+
return webhook_router
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
huace_aigc_auth_client/__init__.py,sha256=YVvtav7BwwubneYePnJQf4PM3jUZ1e9P6wH6vJJdTlI,2332
|
|
2
|
+
huace_aigc_auth_client/legacy_adapter.py,sha256=eNoyaCtTMdeNfzDlB66Fu-6WAfGYGk3DioIBAAZOL6A,21871
|
|
3
|
+
huace_aigc_auth_client/sdk.py,sha256=4Azj2TPOCLWv6dTaAqw--_Uh-Gzpp0KhpxOEBGONG48,22749
|
|
4
|
+
huace_aigc_auth_client/webhook.py,sha256=m-mjXNNqrUClrNpOHRBzty0XKFDBuZp4e_PgLd2t0aA,4070
|
|
5
|
+
huace_aigc_auth_client-1.1.6.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
|
|
6
|
+
huace_aigc_auth_client-1.1.6.dist-info/METADATA,sha256=jX7_jDBTrRnLwqG06w_zRJm6CL-pxD0eFfUX6FiFMAQ,20084
|
|
7
|
+
huace_aigc_auth_client-1.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
huace_aigc_auth_client-1.1.6.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
|
|
9
|
+
huace_aigc_auth_client-1.1.6.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
huace_aigc_auth_client/__init__.py,sha256=isMBleAvICiYwHXv0fm6qf-nrvdIMIePGZbrrG_rQws,2163
|
|
2
|
-
huace_aigc_auth_client/legacy_adapter.py,sha256=lCxpwROHZd0RG0dVp6AidQQRpFv6H8DvVzjrYHRDZFg,21515
|
|
3
|
-
huace_aigc_auth_client/sdk.py,sha256=4Azj2TPOCLWv6dTaAqw--_Uh-Gzpp0KhpxOEBGONG48,22749
|
|
4
|
-
huace_aigc_auth_client-1.1.4.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
|
|
5
|
-
huace_aigc_auth_client-1.1.4.dist-info/METADATA,sha256=ly5bJH-amh0jsbg_ArABrq8mvqOfCQ0nYz1UtdD0Ka4,20084
|
|
6
|
-
huace_aigc_auth_client-1.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
huace_aigc_auth_client-1.1.4.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
|
|
8
|
-
huace_aigc_auth_client-1.1.4.dist-info/RECORD,,
|
|
File without changes
|
{huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.6.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.6.dist-info}/top_level.txt
RENAMED
|
File without changes
|