skyplatform-iam 1.0.5__tar.gz → 1.2.0__tar.gz
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-1.0.5 → skyplatform_iam-1.2.0}/PKG-INFO +1 -1
- {skyplatform_iam-1.0.5 → skyplatform_iam-1.2.0}/pyproject.toml +1 -1
- skyplatform_iam-1.2.0/skyplatform_iam/__init__.py +161 -0
- skyplatform_iam-1.2.0/skyplatform_iam/config.py +153 -0
- {skyplatform_iam-1.0.5 → skyplatform_iam-1.2.0}/skyplatform_iam/connect_agenterra_iam.py +147 -161
- {skyplatform_iam-1.0.5 → skyplatform_iam-1.2.0}/skyplatform_iam/middleware.py +69 -145
- skyplatform_iam-1.0.5/skyplatform_iam/__init__.py +0 -157
- skyplatform_iam-1.0.5/skyplatform_iam/api.py +0 -366
- skyplatform_iam-1.0.5/skyplatform_iam/config.py +0 -287
- skyplatform_iam-1.0.5/skyplatform_iam/global_manager.py +0 -272
- {skyplatform_iam-1.0.5 → skyplatform_iam-1.2.0}/README.md +0 -0
- {skyplatform_iam-1.0.5 → skyplatform_iam-1.2.0}/skyplatform_iam/exceptions.py +0 -0
|
@@ -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,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SkyPlatform IAM SDK
|
|
3
|
+
提供FastAPI认证中间件和IAM服务连接功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .config import AuthConfig
|
|
7
|
+
from .middleware import AuthMiddleware, AuthService, setup_auth_middleware, get_current_user, get_optional_user
|
|
8
|
+
from .connect_agenterra_iam import ConnectAgenterraIam
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
SkyPlatformAuthException,
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
AuthorizationError,
|
|
13
|
+
TokenExpiredError,
|
|
14
|
+
TokenInvalidError,
|
|
15
|
+
ConfigurationError,
|
|
16
|
+
IAMServiceError,
|
|
17
|
+
NetworkError
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__version__ = "1.2.0"
|
|
21
|
+
__author__ = "x9"
|
|
22
|
+
__description__ = "SkyPlatform IAM认证SDK,提供FastAPI中间件和IAM服务连接功能"
|
|
23
|
+
|
|
24
|
+
# 全局IAM客户端实例
|
|
25
|
+
_global_iam_client = None
|
|
26
|
+
|
|
27
|
+
# 导出主要类和函数
|
|
28
|
+
__all__ = [
|
|
29
|
+
# 配置
|
|
30
|
+
"AuthConfig",
|
|
31
|
+
|
|
32
|
+
# 中间件
|
|
33
|
+
"AuthMiddleware",
|
|
34
|
+
"AuthService",
|
|
35
|
+
"setup_auth_middleware",
|
|
36
|
+
"get_current_user",
|
|
37
|
+
"get_optional_user",
|
|
38
|
+
|
|
39
|
+
# 客户端
|
|
40
|
+
"ConnectAgenterraIam",
|
|
41
|
+
"get_iam_client",
|
|
42
|
+
|
|
43
|
+
# 异常
|
|
44
|
+
"SkyPlatformAuthException",
|
|
45
|
+
"AuthenticationError",
|
|
46
|
+
"AuthorizationError",
|
|
47
|
+
"TokenExpiredError",
|
|
48
|
+
"TokenInvalidError",
|
|
49
|
+
"ConfigurationError",
|
|
50
|
+
"IAMServiceError",
|
|
51
|
+
"NetworkError",
|
|
52
|
+
|
|
53
|
+
# 版本信息
|
|
54
|
+
"__version__",
|
|
55
|
+
"__author__",
|
|
56
|
+
"__description__"
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def create_auth_middleware(config: AuthConfig = None, **kwargs) -> AuthMiddleware:
|
|
61
|
+
"""
|
|
62
|
+
创建认证中间件的便捷函数
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
config: 认证配置,如果为None则从环境变量创建
|
|
66
|
+
**kwargs: 其他中间件参数
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
AuthMiddleware: 认证中间件实例
|
|
70
|
+
|
|
71
|
+
Note:
|
|
72
|
+
此函数用于创建中间件实例,用于请求拦截和鉴权。
|
|
73
|
+
客户端应用需要自己实现具体的业务接口。
|
|
74
|
+
"""
|
|
75
|
+
if config is None:
|
|
76
|
+
config = AuthConfig.from_env()
|
|
77
|
+
|
|
78
|
+
return AuthMiddleware(config=config, **kwargs)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def init_skyplatform_iam(app, config: AuthConfig = None):
|
|
82
|
+
"""
|
|
83
|
+
一键设置认证中间件的便捷函数
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
app: FastAPI应用实例
|
|
87
|
+
config: 认证配置,如果为None则从环境变量创建
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
AuthMiddleware: 认证中间件实例
|
|
91
|
+
|
|
92
|
+
Note:
|
|
93
|
+
此函数只设置认证中间件,不包含预制路由。
|
|
94
|
+
客户端应用需要根据业务需求自己实现认证相关的API接口。
|
|
95
|
+
建议传入完整的AuthConfig对象以避免环境变量配置问题。
|
|
96
|
+
"""
|
|
97
|
+
if config is None:
|
|
98
|
+
config = AuthConfig.from_env()
|
|
99
|
+
|
|
100
|
+
# 验证配置的完整性
|
|
101
|
+
config.validate_config()
|
|
102
|
+
|
|
103
|
+
# 初始化全局认证服务
|
|
104
|
+
setup_auth_middleware(config)
|
|
105
|
+
|
|
106
|
+
# 添加中间件
|
|
107
|
+
middleware = AuthMiddleware(app=app, config=config)
|
|
108
|
+
app.add_middleware(AuthMiddleware, config=config)
|
|
109
|
+
|
|
110
|
+
return middleware
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_iam_client(config: AuthConfig = None) -> ConnectAgenterraIam:
|
|
114
|
+
"""
|
|
115
|
+
获取全局IAM客户端实例
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
config: 认证配置,如果为None则从环境变量创建
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
ConnectAgenterraIam: IAM客户端实例
|
|
122
|
+
|
|
123
|
+
Note:
|
|
124
|
+
此函数使用单例模式,确保整个应用中只有一个IAM客户端实例。
|
|
125
|
+
第一次调用时会创建实例,后续调用会返回同一个实例。
|
|
126
|
+
如果需要更新配置,可以传入新的config参数。
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
# 使用默认配置(从环境变量)
|
|
130
|
+
iam_client = get_iam_client()
|
|
131
|
+
|
|
132
|
+
# 使用自定义配置
|
|
133
|
+
config = AuthConfig(
|
|
134
|
+
agenterra_iam_host="https://iam.example.com",
|
|
135
|
+
server_name="my_server",
|
|
136
|
+
access_key="my_access_key"
|
|
137
|
+
)
|
|
138
|
+
iam_client = get_iam_client(config)
|
|
139
|
+
|
|
140
|
+
# 调用IAM服务方法
|
|
141
|
+
result = iam_client.register(
|
|
142
|
+
cred_type="username",
|
|
143
|
+
cred_value="test_user",
|
|
144
|
+
password="password123"
|
|
145
|
+
)
|
|
146
|
+
"""
|
|
147
|
+
global _global_iam_client
|
|
148
|
+
|
|
149
|
+
# 如果传入了新的配置,或者实例不存在,则创建/更新实例
|
|
150
|
+
if config is not None:
|
|
151
|
+
if _global_iam_client is None:
|
|
152
|
+
_global_iam_client = ConnectAgenterraIam(config=config)
|
|
153
|
+
else:
|
|
154
|
+
# 重新加载配置
|
|
155
|
+
_global_iam_client.reload_config(config)
|
|
156
|
+
elif _global_iam_client is None:
|
|
157
|
+
# 使用默认配置创建实例
|
|
158
|
+
default_config = AuthConfig.from_env()
|
|
159
|
+
_global_iam_client = ConnectAgenterraIam(config=default_config)
|
|
160
|
+
|
|
161
|
+
return _global_iam_client
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SkyPlatform IAM SDK 配置模块
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import fnmatch
|
|
7
|
+
from typing import List, Optional, Dict
|
|
8
|
+
|
|
9
|
+
import jwt
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
from dotenv import load_dotenv
|
|
12
|
+
|
|
13
|
+
# 加载环境变量
|
|
14
|
+
load_dotenv()
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AuthConfig(BaseModel):
|
|
19
|
+
"""
|
|
20
|
+
认证配置类
|
|
21
|
+
支持环境变量和代码配置
|
|
22
|
+
"""
|
|
23
|
+
# IAM服务配置
|
|
24
|
+
agenterra_iam_host: str
|
|
25
|
+
server_name: str
|
|
26
|
+
access_key: str
|
|
27
|
+
machine_token: str
|
|
28
|
+
|
|
29
|
+
# Token配置
|
|
30
|
+
token_header: str = "Authorization"
|
|
31
|
+
token_prefix: str = "Bearer "
|
|
32
|
+
|
|
33
|
+
# 错误处理配置
|
|
34
|
+
enable_debug: bool = False
|
|
35
|
+
|
|
36
|
+
# 白名单路径配置(实例变量)
|
|
37
|
+
whitelist_paths: List[str] = Field(default_factory=list)
|
|
38
|
+
|
|
39
|
+
class Config:
|
|
40
|
+
env_prefix = "AGENTERRA_"
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_env(cls) -> "AuthConfig":
|
|
44
|
+
"""
|
|
45
|
+
从环境变量创建配置
|
|
46
|
+
"""
|
|
47
|
+
return cls(
|
|
48
|
+
agenterra_iam_host=os.environ.get('AGENTERRA_IAM_HOST', ''),
|
|
49
|
+
server_name=os.environ.get('AGENTERRA_SERVER_NAME', ''),
|
|
50
|
+
access_key=os.environ.get('AGENTERRA_ACCESS_KEY', ''),
|
|
51
|
+
enable_debug=os.environ.get('AGENTERRA_ENABLE_DEBUG', 'false').lower() == 'true',
|
|
52
|
+
machine_token=os.environ.get('MACHINE_TOKEN', ''),
|
|
53
|
+
whitelist_paths=[] # 初始化空的白名单路径列表
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def validate_config(self) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
验证配置是否完整
|
|
59
|
+
"""
|
|
60
|
+
required_fields = ['agenterra_iam_host', 'server_name', 'access_key']
|
|
61
|
+
for field in required_fields:
|
|
62
|
+
if not getattr(self, field):
|
|
63
|
+
raise ValueError(f"配置项 {field} 不能为空")
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
def _normalize_path(self, path: str) -> str:
|
|
67
|
+
"""
|
|
68
|
+
标准化路径格式
|
|
69
|
+
"""
|
|
70
|
+
if not path:
|
|
71
|
+
return path
|
|
72
|
+
|
|
73
|
+
# 确保路径以 / 开头
|
|
74
|
+
if not path.startswith('/'):
|
|
75
|
+
path = '/' + path
|
|
76
|
+
|
|
77
|
+
# 移除重复的斜杠
|
|
78
|
+
while '//' in path:
|
|
79
|
+
path = path.replace('//', '/')
|
|
80
|
+
|
|
81
|
+
return path
|
|
82
|
+
|
|
83
|
+
def add_whitelist_path(self, path: str) -> None:
|
|
84
|
+
"""
|
|
85
|
+
添加白名单路径
|
|
86
|
+
"""
|
|
87
|
+
if not path:
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
normalized_path = self._normalize_path(path)
|
|
91
|
+
if normalized_path not in self.whitelist_paths:
|
|
92
|
+
self.whitelist_paths.append(normalized_path)
|
|
93
|
+
|
|
94
|
+
def add_whitelist_paths(self, paths: List[str]) -> None:
|
|
95
|
+
"""
|
|
96
|
+
批量添加白名单路径
|
|
97
|
+
"""
|
|
98
|
+
for path in paths:
|
|
99
|
+
self.add_whitelist_path(path)
|
|
100
|
+
|
|
101
|
+
def remove_whitelist_path(self, path: str) -> None:
|
|
102
|
+
"""
|
|
103
|
+
移除白名单路径
|
|
104
|
+
"""
|
|
105
|
+
if not path:
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
normalized_path = self._normalize_path(path)
|
|
109
|
+
if normalized_path in self.whitelist_paths:
|
|
110
|
+
self.whitelist_paths.remove(normalized_path)
|
|
111
|
+
|
|
112
|
+
def clear_whitelist_paths(self) -> None:
|
|
113
|
+
"""
|
|
114
|
+
清空所有白名单路径
|
|
115
|
+
"""
|
|
116
|
+
self.whitelist_paths.clear()
|
|
117
|
+
|
|
118
|
+
def get_whitelist_paths(self) -> List[str]:
|
|
119
|
+
"""
|
|
120
|
+
获取所有白名单路径
|
|
121
|
+
"""
|
|
122
|
+
return self.whitelist_paths.copy()
|
|
123
|
+
|
|
124
|
+
def is_path_whitelisted(self, path: str) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
检查路径是否在白名单中(支持通配符匹配)
|
|
127
|
+
"""
|
|
128
|
+
if not path:
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
normalized_path = self._normalize_path(path)
|
|
132
|
+
|
|
133
|
+
for whitelist_path in self.whitelist_paths:
|
|
134
|
+
# 支持通配符匹配
|
|
135
|
+
if fnmatch.fnmatch(normalized_path, whitelist_path):
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def decode_jwt_token(token: str) -> Optional[Dict]:
|
|
142
|
+
"""直接解析JWT token获取payload"""
|
|
143
|
+
try:
|
|
144
|
+
# 不验证签名,只解析payload(因为token已经通过verify_token验证过)
|
|
145
|
+
decoded_payload = jwt.decode(token, options={"verify_signature": False})
|
|
146
|
+
logger.debug(f"JWT token解析成功: {decoded_payload}")
|
|
147
|
+
return decoded_payload
|
|
148
|
+
except jwt.InvalidTokenError as e:
|
|
149
|
+
logger.error(f"JWT token解析失败: {str(e)}")
|
|
150
|
+
return None
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.error(f"JWT token解析异常: {str(e)}")
|
|
153
|
+
return None
|