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
- - cancle_task()
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 normalized secret.
636
+ Generate TOTP secret for the current user and return a secret.
652
637
 
653
638
  Returns:
654
- str: Normalized TOTP secret string
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
- secret = self._parse_totp_uri(totp_uri).get("secret")
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 cancle_task(self, app_name: str) -> bool:
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.8
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=l8eUmJxXIDjowPvPKIppCLfwlk2kjztU-FXoaOac_tc,59003
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.8.dist-info/METADATA,sha256=bDxN-Vm4004kC7FdM52yMx-YcmNcFzexBDYBr3p6db4,11828
14
- appmesh-1.6.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- appmesh-1.6.8.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
16
- appmesh-1.6.8.dist-info/RECORD,,
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,,