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.
@@ -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.4"
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: huace-aigc-auth-client
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: 华策AIGC Auth Client - 提供 Token 验证、用户信息获取、权限检查、旧系统接入等功能
5
5
  Author-email: Huace <support@huace.com>
6
6
  License: MIT
@@ -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,,