skyplatform-iam 1.0.5__py3-none-any.whl → 1.2.0__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.
- skyplatform_iam/__init__.py +73 -69
- skyplatform_iam/config.py +46 -180
- skyplatform_iam/connect_agenterra_iam.py +147 -161
- skyplatform_iam/middleware.py +69 -145
- {skyplatform_iam-1.0.5.dist-info → skyplatform_iam-1.2.0.dist-info}/METADATA +1 -1
- skyplatform_iam-1.2.0.dist-info/RECORD +8 -0
- skyplatform_iam/api.py +0 -366
- skyplatform_iam/global_manager.py +0 -272
- skyplatform_iam-1.0.5.dist-info/RECORD +0 -10
- {skyplatform_iam-1.0.5.dist-info → skyplatform_iam-1.2.0.dist-info}/WHEEL +0 -0
skyplatform_iam/middleware.py
CHANGED
|
@@ -11,12 +11,10 @@ import jwt
|
|
|
11
11
|
|
|
12
12
|
from .config import AuthConfig
|
|
13
13
|
from .connect_agenterra_iam import ConnectAgenterraIam
|
|
14
|
-
from .global_manager import get_global_manager
|
|
15
14
|
from .exceptions import (
|
|
16
15
|
AuthenticationError,
|
|
17
16
|
AuthorizationError,
|
|
18
|
-
ConfigurationError
|
|
19
|
-
IAMServiceError
|
|
17
|
+
ConfigurationError
|
|
20
18
|
)
|
|
21
19
|
|
|
22
20
|
logger = logging.getLogger(__name__)
|
|
@@ -26,72 +24,40 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
26
24
|
"""
|
|
27
25
|
认证中间件
|
|
28
26
|
自动拦截请求进行Token验证和权限检查
|
|
29
|
-
支持全局实例共享和延迟初始化
|
|
30
27
|
"""
|
|
31
28
|
|
|
32
29
|
def __init__(
|
|
33
30
|
self,
|
|
34
31
|
app,
|
|
35
|
-
config:
|
|
36
|
-
skip_validation: Optional[Callable[[Request], bool]] = None
|
|
37
|
-
use_global_manager: bool = True
|
|
32
|
+
config: AuthConfig,
|
|
33
|
+
skip_validation: Optional[Callable[[Request], bool]] = None
|
|
38
34
|
):
|
|
39
35
|
"""
|
|
40
36
|
初始化认证中间件
|
|
41
37
|
|
|
42
38
|
Args:
|
|
43
39
|
app: FastAPI应用实例
|
|
44
|
-
config:
|
|
40
|
+
config: 认证配置
|
|
45
41
|
skip_validation: 自定义跳过验证的函数
|
|
46
|
-
use_global_manager: 是否使用全局管理器(推荐)
|
|
47
42
|
"""
|
|
48
43
|
super().__init__(app)
|
|
49
|
-
self.
|
|
44
|
+
self.config = config
|
|
45
|
+
self.iam_client = ConnectAgenterraIam(config=config)
|
|
50
46
|
self.skip_validation = skip_validation
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
self.config
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else:
|
|
58
|
-
# 传统模式(向后兼容)
|
|
59
|
-
if config is None:
|
|
60
|
-
raise ConfigurationError("在非全局管理器模式下,config参数不能为None")
|
|
61
|
-
self.config = config
|
|
62
|
-
self.iam_client = ConnectAgenterraIam(config=config)
|
|
63
|
-
|
|
64
|
-
# 验证配置
|
|
65
|
-
try:
|
|
66
|
-
self.config.validate_config()
|
|
67
|
-
except ValueError as e:
|
|
68
|
-
raise ConfigurationError(str(e))
|
|
69
|
-
logger.info("AuthMiddleware使用传统模式")
|
|
70
|
-
|
|
71
|
-
def _get_config_and_client(self):
|
|
72
|
-
"""获取配置和客户端实例"""
|
|
73
|
-
if self.use_global_manager:
|
|
74
|
-
try:
|
|
75
|
-
manager = get_global_manager()
|
|
76
|
-
if not manager.is_initialized():
|
|
77
|
-
raise IAMServiceError("SkyPlatform IAM SDK未初始化,请先调用init_skyplatform_iam()")
|
|
78
|
-
return manager.get_config(), manager.get_client()
|
|
79
|
-
except Exception as e:
|
|
80
|
-
logger.error(f"从全局管理器获取配置和客户端失败: {str(e)}")
|
|
81
|
-
raise IAMServiceError(f"获取IAM配置失败: {str(e)}")
|
|
82
|
-
else:
|
|
83
|
-
return self.config, self.iam_client
|
|
47
|
+
|
|
48
|
+
# 验证配置
|
|
49
|
+
try:
|
|
50
|
+
self.config.validate_config()
|
|
51
|
+
except ValueError as e:
|
|
52
|
+
raise ConfigurationError(str(e))
|
|
84
53
|
|
|
85
54
|
def is_path_whitelisted(self, path: str) -> bool:
|
|
86
55
|
"""
|
|
87
56
|
检查路径是否在本地白名单中
|
|
88
57
|
"""
|
|
89
|
-
|
|
90
|
-
config, _ = self._get_config_and_client()
|
|
91
|
-
return config.is_path_whitelisted(path)
|
|
92
|
-
except Exception as e:
|
|
93
|
-
logger.error(f"检查白名单路径失败: {str(e)}")
|
|
58
|
+
if not self.config:
|
|
94
59
|
return False
|
|
60
|
+
return self.config.is_path_whitelisted(path)
|
|
95
61
|
|
|
96
62
|
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
97
63
|
"""
|
|
@@ -100,7 +66,14 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
100
66
|
try:
|
|
101
67
|
# 获取请求路径
|
|
102
68
|
api_path = request.url.path
|
|
103
|
-
|
|
69
|
+
method = request.method
|
|
70
|
+
|
|
71
|
+
# 检查是否为机机接口鉴权 来自其他服务
|
|
72
|
+
server_ak = request.headers.get('SERVER-AK')
|
|
73
|
+
server_sk = request.headers.get('SERVER-SK')
|
|
74
|
+
# 检查是否为人机接口鉴权 来自前端
|
|
75
|
+
# token = request.headers.get('Authorization')
|
|
76
|
+
|
|
104
77
|
# 首先检查路径是否在本地白名单中
|
|
105
78
|
if self.is_path_whitelisted(api_path):
|
|
106
79
|
logger.info(f"路径 {api_path} 在本地白名单中,跳过认证直接允许访问")
|
|
@@ -114,6 +87,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
114
87
|
|
|
115
88
|
# 提取Token(可能为空,白名单接口不需要token)
|
|
116
89
|
token = self._extract_token(request)
|
|
90
|
+
machine_token = self._extract_machine_token(request)
|
|
117
91
|
|
|
118
92
|
# 验证Token和权限(即使token为空也要调用IAM验证,因为可能是白名单接口)
|
|
119
93
|
user_info = await self._verify_token_and_permission(request, token)
|
|
@@ -131,6 +105,11 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
131
105
|
request.state.authenticated = False
|
|
132
106
|
request.state.is_whitelist = True
|
|
133
107
|
else:
|
|
108
|
+
if machine_token:
|
|
109
|
+
payload = jwt.decode(machine_token, algorithms=["HS256"], options={"verify_signature": False})
|
|
110
|
+
user_id = payload.get("sub", None)
|
|
111
|
+
request.state.user_id = user_id
|
|
112
|
+
|
|
134
113
|
# 正常认证接口,设置用户信息
|
|
135
114
|
request.state.user = user_info
|
|
136
115
|
request.state.authenticated = True
|
|
@@ -173,31 +152,37 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
173
152
|
"""
|
|
174
153
|
从请求中提取Token
|
|
175
154
|
"""
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
auth_header
|
|
181
|
-
if auth_header and auth_header.startswith(config.token_prefix):
|
|
182
|
-
return auth_header[len(config.token_prefix):].strip()
|
|
183
|
-
|
|
184
|
-
# 从查询参数提取(备选方案)
|
|
185
|
-
token = request.query_params.get("token")
|
|
186
|
-
if token:
|
|
187
|
-
return token
|
|
155
|
+
# 从Authorization头提取
|
|
156
|
+
auth_header = request.headers.get(self.config.token_header)
|
|
157
|
+
if auth_header and auth_header.startswith(self.config.token_prefix):
|
|
158
|
+
print("has auth_header: ", auth_header)
|
|
159
|
+
return auth_header[len(self.config.token_prefix):].strip()
|
|
188
160
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
161
|
+
# 从查询参数提取(备选方案)
|
|
162
|
+
token = request.query_params.get("token")
|
|
163
|
+
if token:
|
|
164
|
+
print("has token: ", token)
|
|
165
|
+
return token
|
|
166
|
+
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
def _extract_machine_token(self, request: Request) -> Optional[str]:
|
|
170
|
+
"""
|
|
171
|
+
从请求中提取Token
|
|
172
|
+
"""
|
|
173
|
+
# 从Authorization头提取
|
|
174
|
+
machine_token = request.headers.get("MACHINE-TOKEN")
|
|
175
|
+
if machine_token:
|
|
176
|
+
print("has MACHINE-TOKEN': ", machine_token)
|
|
177
|
+
return machine_token
|
|
178
|
+
|
|
179
|
+
return None
|
|
193
180
|
|
|
194
181
|
async def _verify_token_and_permission(self, request: Request, token: Optional[str]) -> Optional[Dict[str, Any]]:
|
|
195
182
|
"""
|
|
196
183
|
验证Token和权限
|
|
197
184
|
"""
|
|
198
185
|
try:
|
|
199
|
-
config, iam_client = self._get_config_and_client()
|
|
200
|
-
|
|
201
186
|
# 获取请求信息
|
|
202
187
|
api_path = request.url.path
|
|
203
188
|
method = request.method
|
|
@@ -205,14 +190,16 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
205
190
|
# 从请求头获取服务认证信息(可选)
|
|
206
191
|
server_ak = request.headers.get("SERVER-AK", "")
|
|
207
192
|
server_sk = request.headers.get("SERVER-SK", "")
|
|
193
|
+
machine_token = request.headers.get("MACHINE-TOKEN", "")
|
|
208
194
|
|
|
209
195
|
# 调用IAM验证接口(即使token为空也要调用,因为可能是白名单接口)
|
|
210
|
-
user_info = iam_client.verify_token(
|
|
196
|
+
user_info = self.iam_client.verify_token(
|
|
211
197
|
token=token or "", # 如果token为None,传递空字符串
|
|
212
198
|
api=api_path,
|
|
213
199
|
method=method,
|
|
214
200
|
server_ak=server_ak,
|
|
215
|
-
server_sk=server_sk
|
|
201
|
+
server_sk=server_sk,
|
|
202
|
+
machine_token=machine_token
|
|
216
203
|
)
|
|
217
204
|
|
|
218
205
|
return user_info
|
|
@@ -222,12 +209,8 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
222
209
|
raise
|
|
223
210
|
except Exception as e:
|
|
224
211
|
logger.error(f"Token验证异常: {str(e)}")
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if config.enable_debug:
|
|
228
|
-
logger.exception("详细异常信息:")
|
|
229
|
-
except:
|
|
230
|
-
pass
|
|
212
|
+
if self.config.enable_debug:
|
|
213
|
+
logger.exception("详细异常信息:")
|
|
231
214
|
return None
|
|
232
215
|
|
|
233
216
|
def _create_error_response(
|
|
@@ -279,22 +262,24 @@ class AuthService:
|
|
|
279
262
|
"""验证token和权限"""
|
|
280
263
|
# 通过token, server_ak, server_sk判断是否有权限
|
|
281
264
|
api_path = request.url.path
|
|
282
|
-
|
|
265
|
+
|
|
283
266
|
# 首先检查路径是否在白名单中
|
|
284
267
|
if self.is_path_whitelisted(api_path):
|
|
285
268
|
logger.info(f"路径 {api_path} 在白名单中,跳过IAM鉴权")
|
|
286
269
|
return True
|
|
287
|
-
|
|
270
|
+
|
|
288
271
|
credentials: HTTPAuthorizationCredentials = await self.security(request)
|
|
289
272
|
method = request.method
|
|
290
273
|
|
|
291
274
|
server_ak = request.headers.get("SERVER-AK", "")
|
|
292
275
|
server_sk = request.headers.get("SERVER-SK", "")
|
|
276
|
+
machine_token = request.headers.get("MACHINE-TOKEN", "")
|
|
277
|
+
print("248 machine_token:", machine_token)
|
|
293
278
|
|
|
294
279
|
token = ""
|
|
295
280
|
if credentials is not None:
|
|
296
281
|
token = credentials.credentials
|
|
297
|
-
user_info_by_iam = self.iam_client.verify_token(token, api_path, method, server_ak, server_sk)
|
|
282
|
+
user_info_by_iam = self.iam_client.verify_token(token, api_path, method, server_ak, server_sk, machine_token)
|
|
298
283
|
if user_info_by_iam:
|
|
299
284
|
return True
|
|
300
285
|
return False
|
|
@@ -310,7 +295,7 @@ class AuthService:
|
|
|
310
295
|
credentials: HTTPAuthorizationCredentials = await self.security(request)
|
|
311
296
|
if not credentials:
|
|
312
297
|
return None
|
|
313
|
-
|
|
298
|
+
|
|
314
299
|
token = credentials.credentials
|
|
315
300
|
|
|
316
301
|
# 直接解析JWT token获取payload
|
|
@@ -433,93 +418,32 @@ auth_service = None
|
|
|
433
418
|
|
|
434
419
|
def setup_auth_middleware(auth_config: AuthConfig) -> None:
|
|
435
420
|
"""
|
|
436
|
-
|
|
421
|
+
设置认证中间件配置
|
|
437
422
|
|
|
438
423
|
Args:
|
|
439
424
|
auth_config: 认证配置实例,包含白名单路径等配置
|
|
440
|
-
|
|
441
|
-
Deprecated:
|
|
442
|
-
请使用 init_skyplatform_iam() 替代
|
|
443
425
|
"""
|
|
444
426
|
global auth_service
|
|
445
427
|
auth_service = AuthService(auth_config)
|
|
446
|
-
logger.warning("setup_auth_middleware()已废弃,请使用init_skyplatform_iam()替代")
|
|
447
428
|
logger.info(f"认证中间件已配置,白名单路径数量: {len(auth_config.get_whitelist_paths())}")
|
|
448
429
|
|
|
449
430
|
|
|
450
|
-
def create_auth_middleware(
|
|
451
|
-
app,
|
|
452
|
-
config: Optional[AuthConfig] = None,
|
|
453
|
-
use_global_manager: bool = True
|
|
454
|
-
) -> AuthMiddleware:
|
|
455
|
-
"""
|
|
456
|
-
创建认证中间件实例
|
|
457
|
-
|
|
458
|
-
Args:
|
|
459
|
-
app: FastAPI应用实例
|
|
460
|
-
config: 认证配置,如果为None且use_global_manager=True,则从全局管理器获取
|
|
461
|
-
use_global_manager: 是否使用全局管理器(推荐)
|
|
462
|
-
|
|
463
|
-
Returns:
|
|
464
|
-
AuthMiddleware: 认证中间件实例
|
|
465
|
-
|
|
466
|
-
Example:
|
|
467
|
-
# 使用全局管理器(推荐)
|
|
468
|
-
middleware = create_auth_middleware(app)
|
|
469
|
-
|
|
470
|
-
# 传统模式(向后兼容)
|
|
471
|
-
middleware = create_auth_middleware(app, config, use_global_manager=False)
|
|
472
|
-
"""
|
|
473
|
-
return AuthMiddleware(app, config, use_global_manager=use_global_manager)
|
|
474
|
-
|
|
475
|
-
|
|
476
431
|
# 便捷的依赖函数
|
|
477
432
|
async def get_current_user(request: Request) -> Dict:
|
|
478
|
-
"""
|
|
479
|
-
获取当前用户的依赖函数
|
|
480
|
-
优先使用全局管理器,向后兼容传统模式
|
|
481
|
-
"""
|
|
482
|
-
try:
|
|
483
|
-
# 尝试使用全局管理器
|
|
484
|
-
manager = get_global_manager()
|
|
485
|
-
if manager.is_initialized():
|
|
486
|
-
user_info = await manager.get_current_user_info(request)
|
|
487
|
-
if user_info is None:
|
|
488
|
-
raise HTTPException(
|
|
489
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
490
|
-
detail="需要登录认证",
|
|
491
|
-
headers={"WWW-Authenticate": "Bearer"},
|
|
492
|
-
)
|
|
493
|
-
return user_info
|
|
494
|
-
except IAMServiceError:
|
|
495
|
-
pass # 全局管理器未初始化,尝试传统模式
|
|
496
|
-
|
|
497
|
-
# 传统模式(向后兼容)
|
|
433
|
+
"""获取当前用户的依赖函数"""
|
|
498
434
|
if auth_service is None:
|
|
499
435
|
raise HTTPException(
|
|
500
436
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
501
|
-
detail="认证服务未初始化,请先调用
|
|
437
|
+
detail="认证服务未初始化,请先调用setup_auth_middleware函数进行配置"
|
|
502
438
|
)
|
|
503
439
|
return await auth_service.require_auth(request)
|
|
504
440
|
|
|
505
441
|
|
|
506
442
|
async def get_optional_user(request: Request) -> Optional[Dict]:
|
|
507
|
-
"""
|
|
508
|
-
获取可选当前用户的依赖函数
|
|
509
|
-
优先使用全局管理器,向后兼容传统模式
|
|
510
|
-
"""
|
|
511
|
-
try:
|
|
512
|
-
# 尝试使用全局管理器
|
|
513
|
-
manager = get_global_manager()
|
|
514
|
-
if manager.is_initialized():
|
|
515
|
-
return await manager.get_current_user_info(request)
|
|
516
|
-
except IAMServiceError:
|
|
517
|
-
pass # 全局管理器未初始化,尝试传统模式
|
|
518
|
-
|
|
519
|
-
# 传统模式(向后兼容)
|
|
443
|
+
"""获取可选当前用户的依赖函数"""
|
|
520
444
|
if auth_service is None:
|
|
521
445
|
raise HTTPException(
|
|
522
446
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
523
|
-
detail="认证服务未初始化,请先调用
|
|
447
|
+
detail="认证服务未初始化,请先调用setup_auth_middleware函数进行配置"
|
|
524
448
|
)
|
|
525
449
|
return await auth_service.optional_auth(request)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: skyplatform-iam
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: SkyPlatform IAM认证SDK,提供FastAPI中间件和认证路由
|
|
5
5
|
Project-URL: Homepage, https://github.com/xinmayoujiang12621/agenterra_iam
|
|
6
6
|
Project-URL: Documentation, https://skyplatform-iam.readthedocs.io/
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
skyplatform_iam/__init__.py,sha256=N4acauQXv5CUHNgaeYc3wCTi9zlwt9xvZW5szZ-ahkc,4664
|
|
2
|
+
skyplatform_iam/config.py,sha256=4EOzjevFtQ25tZPK7wpU58W6hD-B_XVQF7VCNQzdVOM,4429
|
|
3
|
+
skyplatform_iam/connect_agenterra_iam.py,sha256=ixnk45JQoILfwVVKdPllLO-xUqGb8moOh0G6sAIvrVU,40712
|
|
4
|
+
skyplatform_iam/exceptions.py,sha256=Rt55QIzVK1F_kn6yzKQKKakD6PZDFdPLCGaCphKKms8,2166
|
|
5
|
+
skyplatform_iam/middleware.py,sha256=e_YNRWXmDVKeKwpAcFYptTV4WZboAF-ICC5p9rvLSE0,17069
|
|
6
|
+
skyplatform_iam-1.2.0.dist-info/METADATA,sha256=T7kVHT53D8WXPJTomoWc6nDNIS3ZM2SYGVDX_r26JGY,12658
|
|
7
|
+
skyplatform_iam-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
+
skyplatform_iam-1.2.0.dist-info/RECORD,,
|