skyplatform-iam 1.1.0__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 +1 -1
- skyplatform_iam/config.py +30 -9
- skyplatform_iam/connect_agenterra_iam.py +59 -34
- skyplatform_iam/middleware.py +37 -6
- {skyplatform_iam-1.1.0.dist-info → skyplatform_iam-1.2.0.dist-info}/METADATA +1 -1
- skyplatform_iam-1.2.0.dist-info/RECORD +8 -0
- skyplatform_iam-1.1.0.dist-info/RECORD +0 -8
- {skyplatform_iam-1.1.0.dist-info → skyplatform_iam-1.2.0.dist-info}/WHEEL +0 -0
skyplatform_iam/__init__.py
CHANGED
skyplatform_iam/config.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
2
|
SkyPlatform IAM SDK 配置模块
|
|
3
3
|
"""
|
|
4
|
+
import logging
|
|
4
5
|
import os
|
|
5
6
|
import fnmatch
|
|
6
|
-
from typing import Optional,
|
|
7
|
+
from typing import List, Optional, Dict
|
|
8
|
+
|
|
9
|
+
import jwt
|
|
7
10
|
from pydantic import BaseModel, Field
|
|
8
11
|
from dotenv import load_dotenv
|
|
9
12
|
|
|
10
13
|
# 加载环境变量
|
|
11
14
|
load_dotenv()
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
class AuthConfig(BaseModel):
|
|
@@ -20,6 +24,7 @@ class AuthConfig(BaseModel):
|
|
|
20
24
|
agenterra_iam_host: str
|
|
21
25
|
server_name: str
|
|
22
26
|
access_key: str
|
|
27
|
+
machine_token: str
|
|
23
28
|
|
|
24
29
|
# Token配置
|
|
25
30
|
token_header: str = "Authorization"
|
|
@@ -44,6 +49,7 @@ class AuthConfig(BaseModel):
|
|
|
44
49
|
server_name=os.environ.get('AGENTERRA_SERVER_NAME', ''),
|
|
45
50
|
access_key=os.environ.get('AGENTERRA_ACCESS_KEY', ''),
|
|
46
51
|
enable_debug=os.environ.get('AGENTERRA_ENABLE_DEBUG', 'false').lower() == 'true',
|
|
52
|
+
machine_token=os.environ.get('MACHINE_TOKEN', ''),
|
|
47
53
|
whitelist_paths=[] # 初始化空的白名单路径列表
|
|
48
54
|
)
|
|
49
55
|
|
|
@@ -63,15 +69,15 @@ class AuthConfig(BaseModel):
|
|
|
63
69
|
"""
|
|
64
70
|
if not path:
|
|
65
71
|
return path
|
|
66
|
-
|
|
72
|
+
|
|
67
73
|
# 确保路径以 / 开头
|
|
68
74
|
if not path.startswith('/'):
|
|
69
75
|
path = '/' + path
|
|
70
|
-
|
|
76
|
+
|
|
71
77
|
# 移除重复的斜杠
|
|
72
78
|
while '//' in path:
|
|
73
79
|
path = path.replace('//', '/')
|
|
74
|
-
|
|
80
|
+
|
|
75
81
|
return path
|
|
76
82
|
|
|
77
83
|
def add_whitelist_path(self, path: str) -> None:
|
|
@@ -80,7 +86,7 @@ class AuthConfig(BaseModel):
|
|
|
80
86
|
"""
|
|
81
87
|
if not path:
|
|
82
88
|
return
|
|
83
|
-
|
|
89
|
+
|
|
84
90
|
normalized_path = self._normalize_path(path)
|
|
85
91
|
if normalized_path not in self.whitelist_paths:
|
|
86
92
|
self.whitelist_paths.append(normalized_path)
|
|
@@ -98,7 +104,7 @@ class AuthConfig(BaseModel):
|
|
|
98
104
|
"""
|
|
99
105
|
if not path:
|
|
100
106
|
return
|
|
101
|
-
|
|
107
|
+
|
|
102
108
|
normalized_path = self._normalize_path(path)
|
|
103
109
|
if normalized_path in self.whitelist_paths:
|
|
104
110
|
self.whitelist_paths.remove(normalized_path)
|
|
@@ -121,12 +127,27 @@ class AuthConfig(BaseModel):
|
|
|
121
127
|
"""
|
|
122
128
|
if not path:
|
|
123
129
|
return False
|
|
124
|
-
|
|
130
|
+
|
|
125
131
|
normalized_path = self._normalize_path(path)
|
|
126
|
-
|
|
132
|
+
|
|
127
133
|
for whitelist_path in self.whitelist_paths:
|
|
128
134
|
# 支持通配符匹配
|
|
129
135
|
if fnmatch.fnmatch(normalized_path, whitelist_path):
|
|
130
136
|
return True
|
|
131
|
-
|
|
137
|
+
|
|
132
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
|
|
@@ -5,6 +5,8 @@ import copy
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from fastapi import HTTPException, status
|
|
7
7
|
|
|
8
|
+
from .config import decode_jwt_token
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
class CredentialTypeEnum(str, Enum):
|
|
10
12
|
"""凭证类型枚举,与后端API保持一致"""
|
|
@@ -17,7 +19,7 @@ class CredentialTypeEnum(str, Enum):
|
|
|
17
19
|
class ConnectAgenterraIam(object):
|
|
18
20
|
_instance = None
|
|
19
21
|
_initialized = False
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
def __new__(cls, config=None, logger_name="skyplatform_iam", log_level=logging.INFO):
|
|
22
24
|
"""
|
|
23
25
|
单例模式实现
|
|
@@ -26,11 +28,11 @@ class ConnectAgenterraIam(object):
|
|
|
26
28
|
if cls._instance is None:
|
|
27
29
|
cls._instance = super(ConnectAgenterraIam, cls).__new__(cls)
|
|
28
30
|
return cls._instance
|
|
29
|
-
|
|
31
|
+
|
|
30
32
|
def __init__(self, config=None, logger_name="skyplatform_iam", log_level=logging.INFO):
|
|
31
33
|
"""
|
|
32
34
|
初始化AgenterraIAM连接器
|
|
33
|
-
|
|
35
|
+
|
|
34
36
|
参数:
|
|
35
37
|
- config: AuthConfig配置对象,如果为None则从环境变量读取
|
|
36
38
|
- logger_name: 日志记录器名称
|
|
@@ -39,7 +41,7 @@ class ConnectAgenterraIam(object):
|
|
|
39
41
|
# 防止重复初始化
|
|
40
42
|
if self._initialized:
|
|
41
43
|
return
|
|
42
|
-
|
|
44
|
+
|
|
43
45
|
# 配置日志记录器
|
|
44
46
|
self.logger = logging.getLogger(logger_name)
|
|
45
47
|
if not self.logger.handlers:
|
|
@@ -50,16 +52,17 @@ class ConnectAgenterraIam(object):
|
|
|
50
52
|
handler.setFormatter(formatter)
|
|
51
53
|
self.logger.addHandler(handler)
|
|
52
54
|
self.logger.setLevel(log_level)
|
|
53
|
-
|
|
55
|
+
|
|
54
56
|
# 必须传入config参数,不再支持从环境变量读取
|
|
55
57
|
if config is None:
|
|
56
58
|
raise ValueError("必须传入AuthConfig配置对象,不再支持从环境变量读取配置")
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
self.agenterra_iam_host = config.agenterra_iam_host
|
|
59
61
|
self.server_name = config.server_name
|
|
60
62
|
self.access_key = config.access_key
|
|
63
|
+
self.machine_token = config.machine_token
|
|
61
64
|
self.logger.info("使用传入的AuthConfig配置")
|
|
62
|
-
|
|
65
|
+
|
|
63
66
|
# 验证必要的配置
|
|
64
67
|
if not self.agenterra_iam_host:
|
|
65
68
|
self.logger.warning("AGENTERRA_IAM_HOST 配置未设置")
|
|
@@ -67,9 +70,10 @@ class ConnectAgenterraIam(object):
|
|
|
67
70
|
self.logger.warning("AGENTERRA_SERVER_NAME 配置未设置")
|
|
68
71
|
if not self.access_key:
|
|
69
72
|
self.logger.warning("AGENTERRA_ACCESS_KEY 配置未设置")
|
|
70
|
-
|
|
71
|
-
self.logger.info(
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
self.logger.info(
|
|
75
|
+
f"初始化AgenterraIAM连接器 - Host: {self.agenterra_iam_host}, Server: {self._mask_sensitive(self.server_name)}")
|
|
76
|
+
|
|
73
77
|
self.headers = {
|
|
74
78
|
"Content-Type": "application/json",
|
|
75
79
|
"SERVER-AK": self.server_name,
|
|
@@ -77,9 +81,10 @@ class ConnectAgenterraIam(object):
|
|
|
77
81
|
}
|
|
78
82
|
self.body = {
|
|
79
83
|
"server_name": self.server_name,
|
|
80
|
-
"access_key": self.access_key
|
|
84
|
+
"access_key": self.access_key,
|
|
85
|
+
"machine_token": self.machine_token
|
|
81
86
|
}
|
|
82
|
-
|
|
87
|
+
|
|
83
88
|
# 标记为已初始化
|
|
84
89
|
self._initialized = True
|
|
85
90
|
|
|
@@ -87,20 +92,20 @@ class ConnectAgenterraIam(object):
|
|
|
87
92
|
"""
|
|
88
93
|
重新加载配置
|
|
89
94
|
用于在运行时更新配置
|
|
90
|
-
|
|
95
|
+
|
|
91
96
|
参数:
|
|
92
97
|
- config: AuthConfig配置对象
|
|
93
98
|
"""
|
|
94
99
|
if config is None:
|
|
95
100
|
raise ValueError("必须传入AuthConfig配置对象")
|
|
96
|
-
|
|
101
|
+
|
|
97
102
|
self.logger.info("重新加载配置")
|
|
98
|
-
|
|
103
|
+
|
|
99
104
|
# 更新配置
|
|
100
105
|
self.agenterra_iam_host = config.agenterra_iam_host
|
|
101
106
|
self.server_name = config.server_name
|
|
102
107
|
self.access_key = config.access_key
|
|
103
|
-
|
|
108
|
+
|
|
104
109
|
# 验证必要的配置
|
|
105
110
|
if not self.agenterra_iam_host:
|
|
106
111
|
self.logger.warning("AGENTERRA_IAM_HOST 配置未设置")
|
|
@@ -108,7 +113,7 @@ class ConnectAgenterraIam(object):
|
|
|
108
113
|
self.logger.warning("AGENTERRA_SERVER_NAME 配置未设置")
|
|
109
114
|
if not self.access_key:
|
|
110
115
|
self.logger.warning("AGENTERRA_ACCESS_KEY 配置未设置")
|
|
111
|
-
|
|
116
|
+
|
|
112
117
|
# 更新headers和body
|
|
113
118
|
self.headers = {
|
|
114
119
|
"Content-Type": "application/json",
|
|
@@ -117,63 +122,65 @@ class ConnectAgenterraIam(object):
|
|
|
117
122
|
}
|
|
118
123
|
self.body = {
|
|
119
124
|
"server_name": self.server_name,
|
|
120
|
-
"access_key": self.access_key
|
|
125
|
+
"access_key": self.access_key,
|
|
126
|
+
"machine_token": self.machine_token,
|
|
121
127
|
}
|
|
122
|
-
|
|
123
|
-
self.logger.info(
|
|
128
|
+
|
|
129
|
+
self.logger.info(
|
|
130
|
+
f"配置重新加载完成 - Host: {self.agenterra_iam_host}, Server: {self._mask_sensitive(self.server_name)}")
|
|
124
131
|
|
|
125
132
|
def _mask_sensitive(self, value, mask_char="*", show_chars=4):
|
|
126
133
|
"""
|
|
127
134
|
脱敏处理敏感信息
|
|
128
|
-
|
|
135
|
+
|
|
129
136
|
参数:
|
|
130
137
|
- value: 要脱敏的值
|
|
131
138
|
- mask_char: 脱敏字符
|
|
132
139
|
- show_chars: 显示的字符数量
|
|
133
|
-
|
|
140
|
+
|
|
134
141
|
返回: 脱敏后的字符串
|
|
135
142
|
"""
|
|
136
143
|
if not value or not isinstance(value, str):
|
|
137
144
|
return str(value) if value else "None"
|
|
138
|
-
|
|
145
|
+
|
|
139
146
|
if len(value) <= show_chars:
|
|
140
147
|
return mask_char * len(value)
|
|
141
|
-
|
|
148
|
+
|
|
142
149
|
return value[:show_chars] + mask_char * (len(value) - show_chars)
|
|
143
150
|
|
|
144
151
|
def _sanitize_log_data(self, data):
|
|
145
152
|
"""
|
|
146
153
|
清理日志数据,脱敏敏感信息
|
|
147
|
-
|
|
154
|
+
|
|
148
155
|
参数:
|
|
149
156
|
- data: 要清理的数据(字典或其他类型)
|
|
150
|
-
|
|
157
|
+
|
|
151
158
|
返回: 清理后的数据
|
|
152
159
|
"""
|
|
153
160
|
if not isinstance(data, dict):
|
|
154
161
|
return data
|
|
155
|
-
|
|
162
|
+
|
|
156
163
|
# 需要脱敏的字段列表
|
|
157
164
|
sensitive_fields = [
|
|
158
|
-
'password', 'access_key', 'token', 'refresh_token',
|
|
165
|
+
'password', 'access_key', 'token', 'refresh_token',
|
|
159
166
|
'SERVER-SK', 'new_password', 'server_sk'
|
|
160
167
|
]
|
|
161
|
-
|
|
168
|
+
|
|
162
169
|
sanitized = copy.deepcopy(data)
|
|
163
|
-
|
|
170
|
+
|
|
164
171
|
for key, value in sanitized.items():
|
|
165
172
|
if key.lower() in [field.lower() for field in sensitive_fields]:
|
|
166
173
|
sanitized[key] = self._mask_sensitive(str(value))
|
|
167
174
|
elif isinstance(value, dict):
|
|
168
175
|
sanitized[key] = self._sanitize_log_data(value)
|
|
169
|
-
|
|
176
|
+
|
|
170
177
|
return sanitized
|
|
171
178
|
|
|
172
179
|
def _log_request(self, method_name, url, headers, body):
|
|
173
180
|
"""记录请求信息"""
|
|
174
181
|
sanitized_headers = self._sanitize_log_data(headers)
|
|
175
182
|
sanitized_body = self._sanitize_log_data(body)
|
|
176
|
-
|
|
183
|
+
|
|
177
184
|
self.logger.info(f"[{method_name}] 发送请求 - URL: {url}")
|
|
178
185
|
self.logger.info(f"[{method_name}] 请求头: {sanitized_headers}")
|
|
179
186
|
self.logger.info(f"[{method_name}] 请求体: {sanitized_body}")
|
|
@@ -346,6 +353,11 @@ class ConnectAgenterraIam(object):
|
|
|
346
353
|
|
|
347
354
|
if response.status_code == 200:
|
|
348
355
|
self.logger.info(f"[{method_name}] 密码登录成功")
|
|
356
|
+
data = response.json()["data"]
|
|
357
|
+
access_token = data["access_token"]
|
|
358
|
+
payload = decode_jwt_token(access_token)
|
|
359
|
+
self.machine_token = payload["machine_access_token"]
|
|
360
|
+
|
|
349
361
|
return response
|
|
350
362
|
else:
|
|
351
363
|
self.logger.warning(f"[{method_name}] 密码登录失败 - 状态码: {response.status_code}")
|
|
@@ -452,9 +464,12 @@ class ConnectAgenterraIam(object):
|
|
|
452
464
|
# 记录请求信息
|
|
453
465
|
self._log_request(method_name, url, self.headers, body)
|
|
454
466
|
|
|
467
|
+
headers = self.headers.copy()
|
|
468
|
+
headers['MACHINE-TOKEN'] = self.machine_token
|
|
469
|
+
|
|
455
470
|
response = requests.post(
|
|
456
471
|
url=url,
|
|
457
|
-
headers=
|
|
472
|
+
headers=headers,
|
|
458
473
|
json=body,
|
|
459
474
|
verify=False
|
|
460
475
|
)
|
|
@@ -474,7 +489,7 @@ class ConnectAgenterraIam(object):
|
|
|
474
489
|
self.logger.error(f"[{method_name}] 异常堆栈: {traceback.format_exc()}")
|
|
475
490
|
return False
|
|
476
491
|
|
|
477
|
-
def verify_token(self, token, api, method, server_ak="", server_sk=""):
|
|
492
|
+
def verify_token(self, token, api, method, server_ak="", server_sk="", machine_token=""):
|
|
478
493
|
"""
|
|
479
494
|
请求iam进行鉴权
|
|
480
495
|
server_name: 服务名称
|
|
@@ -494,6 +509,7 @@ class ConnectAgenterraIam(object):
|
|
|
494
509
|
body = {
|
|
495
510
|
"server_name": self.server_name,
|
|
496
511
|
"access_key": self.access_key,
|
|
512
|
+
"machine_token": machine_token if machine_token else self.machine_token,
|
|
497
513
|
"token": token,
|
|
498
514
|
"api": api,
|
|
499
515
|
"method": method,
|
|
@@ -600,6 +616,7 @@ class ConnectAgenterraIam(object):
|
|
|
600
616
|
body = {
|
|
601
617
|
"server_name": self.server_name,
|
|
602
618
|
"access_key": self.access_key,
|
|
619
|
+
"machine_token": self.machine_token,
|
|
603
620
|
"user_id": user_id,
|
|
604
621
|
"new_password": new_password
|
|
605
622
|
}
|
|
@@ -647,6 +664,7 @@ class ConnectAgenterraIam(object):
|
|
|
647
664
|
body = {
|
|
648
665
|
"server_name": self.server_name,
|
|
649
666
|
"access_key": self.access_key,
|
|
667
|
+
"machine_token": self.machine_token,
|
|
650
668
|
"refresh_token": refresh_token
|
|
651
669
|
}
|
|
652
670
|
uri = "/api/v2/service/refresh_token"
|
|
@@ -694,6 +712,7 @@ class ConnectAgenterraIam(object):
|
|
|
694
712
|
body = {
|
|
695
713
|
"server_name": self.server_name,
|
|
696
714
|
"access_key": self.access_key,
|
|
715
|
+
"machine_token": self.machine_token,
|
|
697
716
|
"user_id": user_id,
|
|
698
717
|
"role_id": role_id
|
|
699
718
|
}
|
|
@@ -736,6 +755,7 @@ class ConnectAgenterraIam(object):
|
|
|
736
755
|
body = {
|
|
737
756
|
"server_name": self.server_name,
|
|
738
757
|
"access_key": self.access_key,
|
|
758
|
+
"machine_token": self.machine_token,
|
|
739
759
|
"token": token,
|
|
740
760
|
}
|
|
741
761
|
uri = "/api/v2/service/token"
|
|
@@ -788,6 +808,7 @@ class ConnectAgenterraIam(object):
|
|
|
788
808
|
body = {
|
|
789
809
|
"server_name": self.server_name,
|
|
790
810
|
"access_key": self.access_key,
|
|
811
|
+
"machine_token": self.machine_token,
|
|
791
812
|
"user_id": user_id,
|
|
792
813
|
"config_name": config_name
|
|
793
814
|
}
|
|
@@ -844,6 +865,7 @@ class ConnectAgenterraIam(object):
|
|
|
844
865
|
body = {
|
|
845
866
|
"server_name": self.server_name,
|
|
846
867
|
"access_key": self.access_key,
|
|
868
|
+
"machine_token": self.machine_token,
|
|
847
869
|
"user_id": user_id
|
|
848
870
|
}
|
|
849
871
|
|
|
@@ -896,6 +918,7 @@ class ConnectAgenterraIam(object):
|
|
|
896
918
|
body = {
|
|
897
919
|
"server_name": self.server_name,
|
|
898
920
|
"access_key": self.access_key,
|
|
921
|
+
"machine_token": self.machine_token,
|
|
899
922
|
"user_id": user_id,
|
|
900
923
|
"config_name": config_name
|
|
901
924
|
}
|
|
@@ -957,6 +980,7 @@ class ConnectAgenterraIam(object):
|
|
|
957
980
|
body = {
|
|
958
981
|
"server_name": self.server_name,
|
|
959
982
|
"access_key": self.access_key,
|
|
983
|
+
"machine_token": self.machine_token,
|
|
960
984
|
"target_user_id": target_user_id,
|
|
961
985
|
"cred_type": cred_type.value,
|
|
962
986
|
"cred_value": cred_value
|
|
@@ -1021,6 +1045,7 @@ class ConnectAgenterraIam(object):
|
|
|
1021
1045
|
body = {
|
|
1022
1046
|
"server_name": self.server_name,
|
|
1023
1047
|
"access_key": self.access_key,
|
|
1048
|
+
"machine_token": self.machine_token,
|
|
1024
1049
|
"cred_type": cred_type.value,
|
|
1025
1050
|
"cred_value": cred_value
|
|
1026
1051
|
}
|
skyplatform_iam/middleware.py
CHANGED
|
@@ -66,7 +66,14 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
66
66
|
try:
|
|
67
67
|
# 获取请求路径
|
|
68
68
|
api_path = request.url.path
|
|
69
|
-
|
|
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
|
+
|
|
70
77
|
# 首先检查路径是否在本地白名单中
|
|
71
78
|
if self.is_path_whitelisted(api_path):
|
|
72
79
|
logger.info(f"路径 {api_path} 在本地白名单中,跳过认证直接允许访问")
|
|
@@ -80,6 +87,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
80
87
|
|
|
81
88
|
# 提取Token(可能为空,白名单接口不需要token)
|
|
82
89
|
token = self._extract_token(request)
|
|
90
|
+
machine_token = self._extract_machine_token(request)
|
|
83
91
|
|
|
84
92
|
# 验证Token和权限(即使token为空也要调用IAM验证,因为可能是白名单接口)
|
|
85
93
|
user_info = await self._verify_token_and_permission(request, token)
|
|
@@ -97,6 +105,11 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
97
105
|
request.state.authenticated = False
|
|
98
106
|
request.state.is_whitelist = True
|
|
99
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
|
+
|
|
100
113
|
# 正常认证接口,设置用户信息
|
|
101
114
|
request.state.user = user_info
|
|
102
115
|
request.state.authenticated = True
|
|
@@ -142,15 +155,29 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
142
155
|
# 从Authorization头提取
|
|
143
156
|
auth_header = request.headers.get(self.config.token_header)
|
|
144
157
|
if auth_header and auth_header.startswith(self.config.token_prefix):
|
|
158
|
+
print("has auth_header: ", auth_header)
|
|
145
159
|
return auth_header[len(self.config.token_prefix):].strip()
|
|
146
160
|
|
|
147
161
|
# 从查询参数提取(备选方案)
|
|
148
162
|
token = request.query_params.get("token")
|
|
149
163
|
if token:
|
|
164
|
+
print("has token: ", token)
|
|
150
165
|
return token
|
|
151
166
|
|
|
152
167
|
return None
|
|
153
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
|
|
180
|
+
|
|
154
181
|
async def _verify_token_and_permission(self, request: Request, token: Optional[str]) -> Optional[Dict[str, Any]]:
|
|
155
182
|
"""
|
|
156
183
|
验证Token和权限
|
|
@@ -163,6 +190,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
163
190
|
# 从请求头获取服务认证信息(可选)
|
|
164
191
|
server_ak = request.headers.get("SERVER-AK", "")
|
|
165
192
|
server_sk = request.headers.get("SERVER-SK", "")
|
|
193
|
+
machine_token = request.headers.get("MACHINE-TOKEN", "")
|
|
166
194
|
|
|
167
195
|
# 调用IAM验证接口(即使token为空也要调用,因为可能是白名单接口)
|
|
168
196
|
user_info = self.iam_client.verify_token(
|
|
@@ -170,7 +198,8 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
170
198
|
api=api_path,
|
|
171
199
|
method=method,
|
|
172
200
|
server_ak=server_ak,
|
|
173
|
-
server_sk=server_sk
|
|
201
|
+
server_sk=server_sk,
|
|
202
|
+
machine_token=machine_token
|
|
174
203
|
)
|
|
175
204
|
|
|
176
205
|
return user_info
|
|
@@ -233,22 +262,24 @@ class AuthService:
|
|
|
233
262
|
"""验证token和权限"""
|
|
234
263
|
# 通过token, server_ak, server_sk判断是否有权限
|
|
235
264
|
api_path = request.url.path
|
|
236
|
-
|
|
265
|
+
|
|
237
266
|
# 首先检查路径是否在白名单中
|
|
238
267
|
if self.is_path_whitelisted(api_path):
|
|
239
268
|
logger.info(f"路径 {api_path} 在白名单中,跳过IAM鉴权")
|
|
240
269
|
return True
|
|
241
|
-
|
|
270
|
+
|
|
242
271
|
credentials: HTTPAuthorizationCredentials = await self.security(request)
|
|
243
272
|
method = request.method
|
|
244
273
|
|
|
245
274
|
server_ak = request.headers.get("SERVER-AK", "")
|
|
246
275
|
server_sk = request.headers.get("SERVER-SK", "")
|
|
276
|
+
machine_token = request.headers.get("MACHINE-TOKEN", "")
|
|
277
|
+
print("248 machine_token:", machine_token)
|
|
247
278
|
|
|
248
279
|
token = ""
|
|
249
280
|
if credentials is not None:
|
|
250
281
|
token = credentials.credentials
|
|
251
|
-
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)
|
|
252
283
|
if user_info_by_iam:
|
|
253
284
|
return True
|
|
254
285
|
return False
|
|
@@ -264,7 +295,7 @@ class AuthService:
|
|
|
264
295
|
credentials: HTTPAuthorizationCredentials = await self.security(request)
|
|
265
296
|
if not credentials:
|
|
266
297
|
return None
|
|
267
|
-
|
|
298
|
+
|
|
268
299
|
token = credentials.credentials
|
|
269
300
|
|
|
270
301
|
# 直接解析JWT token获取payload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: skyplatform-iam
|
|
3
|
-
Version: 1.
|
|
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,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
skyplatform_iam/__init__.py,sha256=JfCqvHdzjiwkEVhgzlB45pKKs0VkxKg8yrMMQ4NpWpY,4664
|
|
2
|
-
skyplatform_iam/config.py,sha256=s4tctVpguKZv4O1Fhf7_Fo7zELNX6KYviMjkE1WPbQM,3715
|
|
3
|
-
skyplatform_iam/connect_agenterra_iam.py,sha256=9SnZkYmZ1VewzIz6a3-uOdOLzHrhPyJPIofZHgImJCM,39785
|
|
4
|
-
skyplatform_iam/exceptions.py,sha256=Rt55QIzVK1F_kn6yzKQKKakD6PZDFdPLCGaCphKKms8,2166
|
|
5
|
-
skyplatform_iam/middleware.py,sha256=XNJxvjw3O55TW-ff_uORK-C9Wy4BTAfkcnNjy1SQkx0,15721
|
|
6
|
-
skyplatform_iam-1.1.0.dist-info/METADATA,sha256=K3G-22HfmCKyzHOK_zvrpGDzez_gpEvrqon7nsvG4wg,12658
|
|
7
|
-
skyplatform_iam-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
-
skyplatform_iam-1.1.0.dist-info/RECORD,,
|
|
File without changes
|