appmesh 1.4.4__py3-none-any.whl → 1.4.6__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/http_client.py CHANGED
@@ -8,9 +8,9 @@ from datetime import datetime
8
8
  from enum import Enum, unique
9
9
  from http import HTTPStatus
10
10
  from typing import Optional, Tuple, Union
11
- from urllib import parse
12
11
  import aniso8601
13
12
  import requests
13
+ import urllib
14
14
  from .app import App
15
15
  from .app_run import AppRun
16
16
  from .app_output import AppOutput
@@ -279,27 +279,46 @@ class AppMeshClient(metaclass=abc.ABCMeta):
279
279
  if resp.status_code == HTTPStatus.OK:
280
280
  if "Access-Token" in resp.json():
281
281
  self.jwt_token = resp.json()["Access-Token"]
282
- elif resp.status_code == HTTPStatus.UNAUTHORIZED and "Totp-Challenge" in resp.json():
282
+ elif resp.status_code == HTTPStatus.PRECONDITION_REQUIRED and "Totp-Challenge" in resp.json():
283
283
  challenge = resp.json()["Totp-Challenge"]
284
- resp = self._request_http(
285
- AppMeshClient.Method.POST,
286
- path="/appmesh/totp/validate",
287
- header={
288
- "Username": base64.b64encode(user_name.encode()).decode(),
289
- "Totp-Challenge": base64.b64encode(challenge.encode()).decode(),
290
- "Totp": totp_code,
291
- "Expire-Seconds": self._parse_duration(timeout_seconds),
292
- },
293
- )
294
- if resp.status_code == HTTPStatus.OK:
295
- if "Access-Token" in resp.json():
296
- self.jwt_token = resp.json()["Access-Token"]
297
- else:
298
- raise Exception(resp.text)
284
+ self.validate_totp(user_name, challenge, totp_code, timeout_seconds)
299
285
  else:
300
286
  raise Exception(resp.text)
301
287
  return self.jwt_token
302
288
 
289
+ def validate_totp(self, username: str, challenge: str, code: str, timeout: Union[int, str] = DURATION_ONE_WEEK_ISO) -> str:
290
+ """Validate TOTP challenge and obtain a new JWT token.
291
+
292
+ Args:
293
+ username (str): Username to validate
294
+ challenge (str): Challenge string from server
295
+ code (str): TOTP code to validate
296
+ timeout (Union[int, str], optional): Token expiry timeout.
297
+ Accepts ISO 8601 duration format (e.g., 'P1Y2M3DT4H5M6S', 'P1W') or seconds.
298
+ Defaults to DURATION_ONE_WEEK_ISO.
299
+
300
+ Returns:
301
+ str: New JWT token if validation succeeds
302
+
303
+ Raises:
304
+ Exception: If validation fails or server returns error
305
+ """
306
+ resp = self._request_http(
307
+ AppMeshClient.Method.POST,
308
+ path="/appmesh/totp/validate",
309
+ header={
310
+ "Username": base64.b64encode(username.encode()).decode(),
311
+ "Totp": code,
312
+ "Totp-Challenge": base64.b64encode(challenge.encode()).decode(),
313
+ "Expire-Seconds": self._parse_duration(timeout),
314
+ },
315
+ )
316
+ if resp.status_code == HTTPStatus.OK:
317
+ if "Access-Token" in resp.json():
318
+ self.jwt_token = resp.json()["Access-Token"]
319
+ return self.jwt_token
320
+ raise Exception(resp.text)
321
+
303
322
  def logoff(self) -> bool:
304
323
  """Log out of the current session from the server.
305
324
 
@@ -377,7 +396,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
377
396
  return self._parse_totp_uri(totp_uri).get("secret")
378
397
  raise Exception(resp.text)
379
398
 
380
- def setup_totp(self, totp_code: str) -> bool:
399
+ def setup_totp(self, totp_code: str) -> str:
381
400
  """Set up 2FA for the current user.
382
401
 
383
402
  Args:
@@ -426,13 +445,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
426
445
  dict: eextract parameters
427
446
  """
428
447
  parsed_info = {}
429
- parsed_uri = parse.urlparse(totp_uri)
448
+ parsed_uri = urllib.parse.urlparse(totp_uri)
430
449
 
431
450
  # Extract label from the path
432
451
  parsed_info["label"] = parsed_uri.path[1:] # Remove the leading slash
433
452
 
434
453
  # Extract parameters from the query string
435
- query_params = parse.parse_qs(parsed_uri.query)
454
+ query_params = urllib.parse.parse_qs(parsed_uri.query)
436
455
  for key, value in query_params.items():
437
456
  parsed_info[key] = value[0]
438
457
  return parsed_info
@@ -934,7 +953,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
934
953
 
935
954
  with open(file=local_file, mode="rb") as fp:
936
955
  encoder = MultipartEncoder(fields={"filename": os.path.basename(remote_file), "file": ("filename", fp, "application/octet-stream")})
937
- header = {"File-Path": remote_file, "Content-Type": encoder.content_type}
956
+ header = {"File-Path": urllib.parse.quote(remote_file), "Content-Type": encoder.content_type}
938
957
 
939
958
  # Include file attributes (permissions, owner, group) if requested
940
959
  if apply_file_attributes:
@@ -1101,7 +1120,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1101
1120
  Returns:
1102
1121
  requests.Response: HTTP response
1103
1122
  """
1104
- rest_url = parse.urljoin(self.server_url, path)
1123
+ rest_url = urllib.parse.urljoin(self.server_url, path)
1105
1124
 
1106
1125
  header = {} if header is None else header
1107
1126
  if self.jwt_token:
@@ -1110,7 +1129,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1110
1129
  if ":" in self.forward_to:
1111
1130
  header[self.HTTP_HEADER_KEY_X_TARGET_HOST] = self.forward_to
1112
1131
  else:
1113
- header[self.HTTP_HEADER_KEY_X_TARGET_HOST] = self.forward_to + ":" + str(parse.urlsplit(self.server_url).port)
1132
+ header[self.HTTP_HEADER_KEY_X_TARGET_HOST] = self.forward_to + ":" + str(urllib.parse.urlsplit(self.server_url).port)
1114
1133
  header[self.HTTP_HEADER_KEY_USER_AGENT] = self.HTTP_USER_AGENT
1115
1134
 
1116
1135
  if method is AppMeshClient.Method.GET:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: appmesh
3
- Version: 1.4.4
3
+ Version: 1.4.6
4
4
  Summary: Client SDK for App Mesh
5
5
  Home-page: https://github.com/laoshanxi/app-mesh
6
6
  Author: laoshanxi
@@ -3,11 +3,11 @@ appmesh/app.py,sha256=9Q-SOOej-MH13BU5Dv2iTa-p-sECCJQp6ZX9DjWWmwE,10526
3
3
  appmesh/app_output.py,sha256=JK_TMKgjvaw4n_ys_vmN5S4MyWVZpmD7NlKz_UyMIM8,1015
4
4
  appmesh/app_run.py,sha256=9ISKGZ3k3kkbQvSsPfRfkOLqD9xhbqNOM7ork9F4w9c,1712
5
5
  appmesh/appmesh_client.py,sha256=0ltkqHZUq094gKneYmC0bEZCP0X9kHTp9fccKdWFWP0,339
6
- appmesh/http_client.py,sha256=wd-H4vyHlLSCy1N8znHMf3-Zwgs7ow6-voWUludtMdA,44364
6
+ appmesh/http_client.py,sha256=qM0es3dM4PM95ymNqdTAuIvTD9-Xy6jPKhDW-Sn7ehg,45180
7
7
  appmesh/tcp_client.py,sha256=RkHl5s8jE333BJOgxJqJ_fvjbdRQza7ciV49vLT6YO4,10923
8
8
  appmesh/tcp_messages.py,sha256=w1Kehz_aX4X2CYAUsy9mFVJRhxnLQwwc6L58W4YkQqs,969
9
9
  appmesh/tcp_transport.py,sha256=UMGby2oKV4k7lyXZUMSOe2Je34fb1w7nTkxEpatKLKg,7256
10
- appmesh-1.4.4.dist-info/METADATA,sha256=lV4XJMIWhe2jKNV5NOv_q6SFdVrzN1BZLoVrvEd90Is,11501
11
- appmesh-1.4.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
12
- appmesh-1.4.4.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
13
- appmesh-1.4.4.dist-info/RECORD,,
10
+ appmesh-1.4.6.dist-info/METADATA,sha256=x8wWJAFfIPR3PU4nGmmZDezdmmeoCLFmkE-nrOM1lPY,11501
11
+ appmesh-1.4.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
12
+ appmesh-1.4.6.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
13
+ appmesh-1.4.6.dist-info/RECORD,,