huace-aigc-auth-client 1.1.4__py3-none-any.whl → 1.1.5__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/webhook.py +128 -0
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.5.dist-info}/METADATA +1 -1
- huace_aigc_auth_client-1.1.5.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.5.dist-info}/WHEEL +0 -0
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.5.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.5"
|
|
@@ -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=IEHy6LKacmhQnnaUHIKg5YyUf2aNCYMj4_0p3NNOU90,2332
|
|
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/webhook.py,sha256=m-mjXNNqrUClrNpOHRBzty0XKFDBuZp4e_PgLd2t0aA,4070
|
|
5
|
+
huace_aigc_auth_client-1.1.5.dist-info/licenses/LICENSE,sha256=z7dgC7KljhBLNvKjN15391nMj3aLt0gbud8-Yf1F8EQ,1063
|
|
6
|
+
huace_aigc_auth_client-1.1.5.dist-info/METADATA,sha256=d8z-VO8lkrZO7JYuSqHyfezrJzOxlYaYqwPfk9zijao,20084
|
|
7
|
+
huace_aigc_auth_client-1.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
huace_aigc_auth_client-1.1.5.dist-info/top_level.txt,sha256=kbv0nQ6PQ0JVneWPH7O2AbtlJnP7AjvFJ6JjM6ZEBxo,23
|
|
9
|
+
huace_aigc_auth_client-1.1.5.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.5.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{huace_aigc_auth_client-1.1.4.dist-info → huace_aigc_auth_client-1.1.5.dist-info}/top_level.txt
RENAMED
|
File without changes
|