appmesh 1.6.8__py3-none-any.whl → 1.6.9__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.
appmesh/client_http.py
CHANGED
@@ -7,7 +7,10 @@ import locale
|
|
7
7
|
import logging
|
8
8
|
import os
|
9
9
|
import sys
|
10
|
+
import threading
|
10
11
|
import time
|
12
|
+
import requests
|
13
|
+
import http.cookiejar as cookiejar
|
11
14
|
from datetime import datetime
|
12
15
|
from enum import Enum, unique
|
13
16
|
from http import HTTPStatus
|
@@ -16,7 +19,6 @@ from typing import Optional, Tuple, Union
|
|
16
19
|
from urllib import parse
|
17
20
|
import aniso8601
|
18
21
|
import jwt
|
19
|
-
import requests
|
20
22
|
from .app import App
|
21
23
|
from .app_run import AppRun
|
22
24
|
from .app_output import AppOutput
|
@@ -74,7 +76,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
74
76
|
- wait_for_async_run()
|
75
77
|
- run_app_sync()
|
76
78
|
- run_task()
|
77
|
-
-
|
79
|
+
- cancel_task()
|
78
80
|
|
79
81
|
# System Management
|
80
82
|
- forward_to
|
@@ -191,7 +193,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
191
193
|
rest_ssl_verify=DEFAULT_SSL_CA_CERT_PATH if os.path.exists(DEFAULT_SSL_CA_CERT_PATH) else False,
|
192
194
|
rest_ssl_client_cert=(DEFAULT_SSL_CLIENT_CERT_PATH, DEFAULT_SSL_CLIENT_KEY_PATH) if os.path.exists(DEFAULT_SSL_CLIENT_CERT_PATH) else None,
|
193
195
|
rest_timeout=(60, 300),
|
194
|
-
jwt_token=None,
|
196
|
+
jwt_token: Optional[str] = None,
|
197
|
+
rest_cookie_file: Optional[str] = None,
|
195
198
|
auto_refresh_token=False,
|
196
199
|
):
|
197
200
|
"""Initialize an App Mesh HTTP client for interacting with the App Mesh server via secure HTTPS.
|
@@ -213,13 +216,14 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
213
216
|
rest_timeout (tuple, optional): HTTP connection timeouts for API requests, as `(connect_timeout, read_timeout)`.
|
214
217
|
The default is `(60, 300)`, where `60` seconds is the maximum time to establish a connection and `300` seconds for the maximum read duration.
|
215
218
|
|
219
|
+
rest_cookie_file (str, optional): Path to a file for storing session cookies. If provided, cookies will be saved to and loaded from this file to maintain session state across client instances.
|
220
|
+
|
216
221
|
jwt_token (str, optional): JWT token for API authentication, used in headers to authorize requests where required.
|
217
222
|
auto_refresh_token (bool, optional): Enable automatic token refresh before expiration.
|
218
223
|
When enabled, a background timer will monitor token expiration and attempt to refresh
|
219
224
|
the token before it expires. This works with both native App Mesh tokens and Keycloak tokens.
|
220
225
|
"""
|
221
226
|
self._ensure_logging_configured()
|
222
|
-
self.session = requests.Session()
|
223
227
|
self.auth_server_url = rest_url
|
224
228
|
self._jwt_token = jwt_token
|
225
229
|
self.ssl_verify = rest_ssl_verify
|
@@ -227,6 +231,20 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
227
231
|
self.rest_timeout = rest_timeout
|
228
232
|
self._forward_to = None
|
229
233
|
|
234
|
+
# Session and cookie management
|
235
|
+
self._lock = threading.Lock()
|
236
|
+
self.session = requests.Session()
|
237
|
+
self.cookie_file = rest_cookie_file
|
238
|
+
if self.cookie_file:
|
239
|
+
self.session.cookies = cookiejar.MozillaCookieJar(self.cookie_file)
|
240
|
+
if os.path.exists(self.cookie_file):
|
241
|
+
self.session.cookies.load(ignore_discard=True, ignore_expires=True)
|
242
|
+
else:
|
243
|
+
os.makedirs(os.path.dirname(self.cookie_file), exist_ok=True)
|
244
|
+
self.session.cookies.save(ignore_discard=True, ignore_expires=True)
|
245
|
+
if os.name == "posix":
|
246
|
+
os.chmod(self.cookie_file, 0o600) # User read/write only
|
247
|
+
|
230
248
|
# Token auto-refresh
|
231
249
|
self._token_refresh_timer = None
|
232
250
|
self._auto_refresh_token = auto_refresh_token
|
@@ -398,6 +416,11 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
398
416
|
self._token_refresh_timer.cancel()
|
399
417
|
self._token_refresh_timer = None
|
400
418
|
|
419
|
+
# handle session
|
420
|
+
with self._lock:
|
421
|
+
if self.cookie_file:
|
422
|
+
self.session.cookies.save(ignore_discard=True, ignore_expires=True)
|
423
|
+
|
401
424
|
@property
|
402
425
|
def forward_to(self) -> str:
|
403
426
|
"""Get the target host address for request forwarding in a cluster setup.
|
@@ -444,44 +467,6 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
444
467
|
|
445
468
|
self._forward_to = host
|
446
469
|
|
447
|
-
def _normalize_totp_secret(self, secret: str) -> str:
|
448
|
-
"""
|
449
|
-
Normalize a TOTP secret to a valid RFC 4648 Base32 format.
|
450
|
-
This includes:
|
451
|
-
- Removing all whitespace
|
452
|
-
- Uppercasing
|
453
|
-
- Replacing visually similar or Crockford-like digits:
|
454
|
-
'1' -> 'I', '0' -> 'O', '8' -> 'B', '9' -> 'G'
|
455
|
-
- Adding RFC 4648 padding
|
456
|
-
"""
|
457
|
-
if not secret:
|
458
|
-
raise ValueError("Empty TOTP secret")
|
459
|
-
|
460
|
-
# Remove all whitespace and uppercase
|
461
|
-
secret = "".join(secret.split()).upper()
|
462
|
-
|
463
|
-
# Crockford/Base32-friendly replacements
|
464
|
-
replacements = {
|
465
|
-
"1": "I",
|
466
|
-
"0": "O",
|
467
|
-
"8": "B",
|
468
|
-
"9": "G",
|
469
|
-
}
|
470
|
-
|
471
|
-
for old, new in replacements.items():
|
472
|
-
secret = secret.replace(old, new)
|
473
|
-
|
474
|
-
# Validate that only RFC 4648 Base32 characters remain
|
475
|
-
valid_chars = set("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
|
476
|
-
if not set(secret).issubset(valid_chars):
|
477
|
-
raise ValueError("Invalid characters in TOTP secret")
|
478
|
-
|
479
|
-
# Add padding to make length a multiple of 8 (RFC 4648)
|
480
|
-
padding = (8 - (len(secret) % 8)) % 8
|
481
|
-
secret += "=" * padding
|
482
|
-
|
483
|
-
return secret
|
484
|
-
|
485
470
|
########################################
|
486
471
|
# Security
|
487
472
|
########################################
|
@@ -648,20 +633,15 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
648
633
|
|
649
634
|
def get_totp_secret(self) -> str:
|
650
635
|
"""
|
651
|
-
Generate TOTP secret for the current user and return a
|
636
|
+
Generate TOTP secret for the current user and return a secret.
|
652
637
|
|
653
638
|
Returns:
|
654
|
-
str:
|
639
|
+
str: TOTP secret string
|
655
640
|
"""
|
656
641
|
resp = self._request_http(method=AppMeshClient.Method.POST, path="/appmesh/totp/secret")
|
657
642
|
if resp.status_code == HTTPStatus.OK:
|
658
643
|
totp_uri = base64.b64decode(resp.json()["mfa_uri"]).decode()
|
659
|
-
|
660
|
-
if not secret:
|
661
|
-
raise ValueError("TOTP secret missing in response")
|
662
|
-
|
663
|
-
# Normalize to standard Base32
|
664
|
-
return self._normalize_totp_secret(secret)
|
644
|
+
return self._parse_totp_uri(totp_uri).get("secret")
|
665
645
|
|
666
646
|
raise Exception(resp.text)
|
667
647
|
|
@@ -1282,7 +1262,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1282
1262
|
|
1283
1263
|
return resp.text
|
1284
1264
|
|
1285
|
-
def
|
1265
|
+
def cancel_task(self, app_name: str) -> bool:
|
1286
1266
|
"""Client cancle a running task to a App Mesh application.
|
1287
1267
|
|
1288
1268
|
Args:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: appmesh
|
3
|
-
Version: 1.6.
|
3
|
+
Version: 1.6.9
|
4
4
|
Summary: Client SDK for App Mesh
|
5
5
|
Home-page: https://github.com/laoshanxi/app-mesh
|
6
6
|
Author: laoshanxi
|
@@ -145,7 +145,6 @@ Refer to the [Installation doc](https://app-mesh.readthedocs.io/en/latest/Instal
|
|
145
145
|
- [log4cpp](http://log4cpp.sourceforge.net)
|
146
146
|
- [Crypto++](https://www.cryptopp.com)
|
147
147
|
- [ldap-cpp](https://github.com/AndreyBarmaley/ldap-cpp)
|
148
|
-
- [OATH Toolkit](http://www.nongnu.org/oath-toolkit/liboath-api)
|
149
148
|
|
150
149
|
[language.url]: https://isocpp.org/
|
151
150
|
[language.badge]: https://img.shields.io/badge/language-C++-blue.svg
|
@@ -3,14 +3,14 @@ appmesh/app.py,sha256=crD4DRFZJuHtZMfSsz7C-EwvjPmGZbFXYXvA_wCdvdI,10734
|
|
3
3
|
appmesh/app_output.py,sha256=vfn322AyixblI8DbXds08h6L_ybObiaRSifsA1-Xcoo,1035
|
4
4
|
appmesh/app_run.py,sha256=aYq852a29OThIi32Xtx5s0sTXZ97T0lHD5WXH8yfPoc,2018
|
5
5
|
appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
|
6
|
-
appmesh/client_http.py,sha256=
|
6
|
+
appmesh/client_http.py,sha256=OiYM4CXn21S4e_jx71X5_31krLes6UuHHueEYk6UIu4,58690
|
7
7
|
appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
|
8
8
|
appmesh/client_tcp.py,sha256=aq6UUzytZA4ibE9WQMMWdo1uW8sHETEhJjsbM6IYSno,11457
|
9
9
|
appmesh/server_http.py,sha256=rBIYO9rbR-r3x1Jcry440Sp--IM-OWKRaOhNpGdkxh8,4299
|
10
10
|
appmesh/server_tcp.py,sha256=-CU5tw97WJmDcUNsNPWqpdZ0wxRzRD6kUP3XyNZUTHc,1444
|
11
11
|
appmesh/tcp_messages.py,sha256=H9S_iCy0IuufY2v50_SUgRvcyQmJsySG65tBe_xb3Ko,1878
|
12
12
|
appmesh/tcp_transport.py,sha256=0hRSp5fpL9wKB05JIyIRIuyBC8w1IdokryhMDHqtN4M,8946
|
13
|
-
appmesh-1.6.
|
14
|
-
appmesh-1.6.
|
15
|
-
appmesh-1.6.
|
16
|
-
appmesh-1.6.
|
13
|
+
appmesh-1.6.9.dist-info/METADATA,sha256=aT2BPT5BM_5FkkFKhR-h_NBWMQMeVBP3-Wo2QwA-j6Y,11763
|
14
|
+
appmesh-1.6.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
+
appmesh-1.6.9.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
+
appmesh-1.6.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|