cnhkmcp 1.1.6__py3-none-any.whl → 1.1.7__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.
- cnhkmcp/__init__.py +1 -1
- cnhkmcp/untracked/platform_functions.py +111 -4
- {cnhkmcp-1.1.6.dist-info → cnhkmcp-1.1.7.dist-info}/METADATA +1 -1
- cnhkmcp-1.1.7.dist-info/RECORD +10 -0
- cnhkmcp-1.1.6.dist-info/RECORD +0 -10
- {cnhkmcp-1.1.6.dist-info → cnhkmcp-1.1.7.dist-info}/WHEEL +0 -0
- {cnhkmcp-1.1.6.dist-info → cnhkmcp-1.1.7.dist-info}/entry_points.txt +0 -0
- {cnhkmcp-1.1.6.dist-info → cnhkmcp-1.1.7.dist-info}/licenses/LICENSE +0 -0
- {cnhkmcp-1.1.6.dist-info → cnhkmcp-1.1.7.dist-info}/top_level.txt +0 -0
cnhkmcp/__init__.py
CHANGED
|
@@ -81,8 +81,8 @@ class BrainApiClient:
|
|
|
81
81
|
print(f"[{level}] {message}", file=sys.stderr)
|
|
82
82
|
|
|
83
83
|
async def authenticate(self, email: str, password: str) -> Dict[str, Any]:
|
|
84
|
-
"""Authenticate with WorldQuant BRAIN platform
|
|
85
|
-
self.log("🔐 Starting
|
|
84
|
+
"""Authenticate with WorldQuant BRAIN platform with biometric support."""
|
|
85
|
+
self.log("🔐 Starting Authentication process...", "INFO")
|
|
86
86
|
|
|
87
87
|
try:
|
|
88
88
|
# Store credentials for potential re-authentication
|
|
@@ -106,7 +106,7 @@ class BrainApiClient:
|
|
|
106
106
|
|
|
107
107
|
# Check for successful authentication (status code 201)
|
|
108
108
|
if response.status_code == 201:
|
|
109
|
-
self.log("✅
|
|
109
|
+
self.log("✅ Authentication successful", "SUCCESS")
|
|
110
110
|
|
|
111
111
|
# Check if JWT token was automatically stored by session
|
|
112
112
|
jwt_token = self.session.cookies.get('t')
|
|
@@ -120,10 +120,26 @@ class BrainApiClient:
|
|
|
120
120
|
'user': {'email': email},
|
|
121
121
|
'status': 'authenticated',
|
|
122
122
|
'permissions': ['read', 'write'],
|
|
123
|
-
'message': '
|
|
123
|
+
'message': 'Authentication successful',
|
|
124
124
|
'status_code': response.status_code,
|
|
125
125
|
'has_jwt': jwt_token is not None
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
# Check if biometric authentication is required (401 with persona)
|
|
129
|
+
elif response.status_code == 401:
|
|
130
|
+
www_auth = response.headers.get("WWW-Authenticate")
|
|
131
|
+
location = response.headers.get("Location")
|
|
132
|
+
|
|
133
|
+
if www_auth == "persona" and location:
|
|
134
|
+
self.log("🔴 Biometric authentication required", "INFO")
|
|
135
|
+
|
|
136
|
+
# Handle biometric authentication
|
|
137
|
+
from urllib.parse import urljoin
|
|
138
|
+
biometric_url = urljoin(response.url, location)
|
|
139
|
+
|
|
140
|
+
return await self._handle_biometric_auth(biometric_url, email)
|
|
141
|
+
else:
|
|
142
|
+
raise Exception("Incorrect email or password")
|
|
127
143
|
else:
|
|
128
144
|
raise Exception(f"Authentication failed with status code: {response.status_code}")
|
|
129
145
|
|
|
@@ -134,6 +150,97 @@ class BrainApiClient:
|
|
|
134
150
|
self.log(f"❌ Authentication failed: {str(e)}", "ERROR")
|
|
135
151
|
raise
|
|
136
152
|
|
|
153
|
+
async def _handle_biometric_auth(self, biometric_url: str, email: str) -> Dict[str, Any]:
|
|
154
|
+
"""Handle biometric authentication using browser automation."""
|
|
155
|
+
self.log("🌐 Starting biometric authentication...", "INFO")
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
# Import selenium for browser automation
|
|
159
|
+
from selenium import webdriver
|
|
160
|
+
from selenium.webdriver.chrome.options import Options
|
|
161
|
+
import time
|
|
162
|
+
|
|
163
|
+
# Setup Chrome options
|
|
164
|
+
options = Options()
|
|
165
|
+
options.add_argument('--no-sandbox')
|
|
166
|
+
options.add_argument('--disable-dev-shm-usage')
|
|
167
|
+
|
|
168
|
+
driver = None
|
|
169
|
+
try:
|
|
170
|
+
# Open browser with timeout
|
|
171
|
+
driver = webdriver.Chrome(options=options)
|
|
172
|
+
# Set a short timeout so it doesn't wait forever
|
|
173
|
+
driver.set_page_load_timeout(80) # Only wait 5 seconds
|
|
174
|
+
|
|
175
|
+
self.log("🌐 Opening browser for biometric authentication...", "INFO")
|
|
176
|
+
|
|
177
|
+
# Try to open the URL but handle timeout
|
|
178
|
+
try:
|
|
179
|
+
driver.get(biometric_url)
|
|
180
|
+
self.log("✅ Browser page loaded successfully", "SUCCESS")
|
|
181
|
+
except Exception as timeout_error:
|
|
182
|
+
self.log(f"⚠️ Page load timeout (expected): {str(timeout_error)[:50]}...", "WARNING")
|
|
183
|
+
self.log("✅ Browser window is open for biometric authentication", "INFO")
|
|
184
|
+
|
|
185
|
+
# Print instructions
|
|
186
|
+
print("\n" + "="*60, file=sys.stderr)
|
|
187
|
+
print("🔒 BIOMETRIC AUTHENTICATION REQUIRED", file=sys.stderr)
|
|
188
|
+
print("="*60, file=sys.stderr)
|
|
189
|
+
print("🌐 Browser window is open with biometric authentication page", file=sys.stderr)
|
|
190
|
+
print("🔧 Complete the biometric authentication in the browser", file=sys.stderr)
|
|
191
|
+
print("⏳ The system will automatically check when you're done...", file=sys.stderr)
|
|
192
|
+
print("="*60, file=sys.stderr)
|
|
193
|
+
|
|
194
|
+
# Keep checking until authentication is complete
|
|
195
|
+
max_attempts = 60 # 5 minutes maximum (60 * 5 seconds)
|
|
196
|
+
attempt = 0
|
|
197
|
+
|
|
198
|
+
while attempt < max_attempts:
|
|
199
|
+
time.sleep(5) # Check every 5 seconds
|
|
200
|
+
attempt += 1
|
|
201
|
+
|
|
202
|
+
# Check if authentication completed
|
|
203
|
+
check_response = self.session.post(biometric_url)
|
|
204
|
+
self.log(f"🔄 Checking authentication status (attempt {attempt}/{max_attempts}): {check_response.status_code}", "INFO")
|
|
205
|
+
|
|
206
|
+
if check_response.status_code == 201:
|
|
207
|
+
self.log("✅ Biometric authentication successful!", "SUCCESS")
|
|
208
|
+
|
|
209
|
+
# Close browser
|
|
210
|
+
driver.quit()
|
|
211
|
+
|
|
212
|
+
# Check JWT token
|
|
213
|
+
jwt_token = self.session.cookies.get('t')
|
|
214
|
+
if jwt_token:
|
|
215
|
+
self.log("✅ JWT token received", "SUCCESS")
|
|
216
|
+
|
|
217
|
+
# Return success response
|
|
218
|
+
return {
|
|
219
|
+
'user': {'email': email},
|
|
220
|
+
'status': 'authenticated',
|
|
221
|
+
'permissions': ['read', 'write'],
|
|
222
|
+
'message': 'Biometric authentication successful',
|
|
223
|
+
'status_code': check_response.status_code,
|
|
224
|
+
'has_jwt': jwt_token is not None
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# If we get here, authentication timed out
|
|
228
|
+
if driver:
|
|
229
|
+
driver.quit()
|
|
230
|
+
raise Exception("Biometric authentication timed out")
|
|
231
|
+
|
|
232
|
+
except Exception as driver_error:
|
|
233
|
+
if driver:
|
|
234
|
+
try:
|
|
235
|
+
driver.quit()
|
|
236
|
+
except:
|
|
237
|
+
pass
|
|
238
|
+
raise Exception(f"Browser automation error: {driver_error}")
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
self.log(f"❌ Biometric authentication failed: {str(e)}", "ERROR")
|
|
242
|
+
raise
|
|
243
|
+
|
|
137
244
|
async def is_authenticated(self) -> bool:
|
|
138
245
|
"""Check if currently authenticated using JWT token."""
|
|
139
246
|
try:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
cnhkmcp/__init__.py,sha256=zA4Je1BkR8_9NorDUU5n_QEozwqBquxVGsUrGk3yHjc,2758
|
|
2
|
+
cnhkmcp/untracked/forum_functions.py,sha256=78wzvN_UYWwbWU40q8_FJNSFPJnND6W9ZRey6MSSiEk,45516
|
|
3
|
+
cnhkmcp/untracked/platform_functions.py,sha256=sDW-tOhWUT41YuO-A3ugimkrNEj0ShMEUYp4EFVQpeA,77441
|
|
4
|
+
cnhkmcp/untracked/user_config.json,sha256=_INn1X1qIsITrmEno-BRlQOAGm9wnNCw-6B333DEvnk,695
|
|
5
|
+
cnhkmcp-1.1.7.dist-info/licenses/LICENSE,sha256=QLxO2eNMnJQEdI_R1UV2AOD-IvuA8zVrkHWA4D9gtoc,1081
|
|
6
|
+
cnhkmcp-1.1.7.dist-info/METADATA,sha256=sNnIQ4NvGvk6qScb5HBmfhUiP9-OiDG1hsGQLn-RazA,5171
|
|
7
|
+
cnhkmcp-1.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
cnhkmcp-1.1.7.dist-info/entry_points.txt,sha256=lTQieVyIvjhSMK4fT-XwnccY-JBC1H4vVQ3V9dDM-Pc,70
|
|
9
|
+
cnhkmcp-1.1.7.dist-info/top_level.txt,sha256=x--ibUcSgOS9Z_RWK2Qc-vfs7DaXQN-WMaaxEETJ1Bw,8
|
|
10
|
+
cnhkmcp-1.1.7.dist-info/RECORD,,
|
cnhkmcp-1.1.6.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
cnhkmcp/__init__.py,sha256=nmpcykQd12FFDQYnKS368uGeZXoKT3sMHH7SFulZGLA,2758
|
|
2
|
-
cnhkmcp/untracked/forum_functions.py,sha256=78wzvN_UYWwbWU40q8_FJNSFPJnND6W9ZRey6MSSiEk,45516
|
|
3
|
-
cnhkmcp/untracked/platform_functions.py,sha256=IpqBv8q7xo8iYwPgreNjcsk_k0k9_4zV1i4afPFssBI,72253
|
|
4
|
-
cnhkmcp/untracked/user_config.json,sha256=_INn1X1qIsITrmEno-BRlQOAGm9wnNCw-6B333DEvnk,695
|
|
5
|
-
cnhkmcp-1.1.6.dist-info/licenses/LICENSE,sha256=QLxO2eNMnJQEdI_R1UV2AOD-IvuA8zVrkHWA4D9gtoc,1081
|
|
6
|
-
cnhkmcp-1.1.6.dist-info/METADATA,sha256=4NPDEazaUY0jyaGsFEm1g9SHwcLAHe7HGX3w8NZ-eek,5171
|
|
7
|
-
cnhkmcp-1.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
cnhkmcp-1.1.6.dist-info/entry_points.txt,sha256=lTQieVyIvjhSMK4fT-XwnccY-JBC1H4vVQ3V9dDM-Pc,70
|
|
9
|
-
cnhkmcp-1.1.6.dist-info/top_level.txt,sha256=x--ibUcSgOS9Z_RWK2Qc-vfs7DaXQN-WMaaxEETJ1Bw,8
|
|
10
|
-
cnhkmcp-1.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|