HedgeTech 0.3.2b1__tar.gz → 0.4.0b0__tar.gz
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.
- hedgetech-0.4.0b0/HedgeTech/Auth/__AuthAsyncClient.py +224 -0
- hedgetech-0.4.0b0/HedgeTech/Auth/__AuthSyncClient.py +230 -0
- hedgetech-0.4.0b0/HedgeTech/DataEngine/__ime/__AsyncClient.py +868 -0
- hedgetech-0.4.0b0/HedgeTech/DataEngine/__ime/__SyncClient.py +867 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__ime/__io_types/__response.py +204 -117
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__tse_ifb/__AsyncClient.py +127 -59
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__tse_ifb/__SyncClient.py +169 -66
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__tse_ifb/__io_types/__response.py +286 -100
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__ime/__AsyncClient.py +414 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__ime/__SyncClient.py +405 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__ime/__init__.py +2 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__ime/__io_types/__response.py +72 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/EmsEngine/__init__.py +4 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__tse_ifb/__AsyncClient.py +981 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__tse_ifb/__SyncClient.py +994 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__tse_ifb/__io_types/__init__.py +4 -0
- hedgetech-0.4.0b0/HedgeTech/EmsEngine/__tse_ifb/__io_types/__response.py +88 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech.egg-info/PKG-INFO +5 -1
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech.egg-info/SOURCES.txt +5 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/PKG-INFO +5 -1
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/pyproject.toml +6 -2
- hedgetech-0.3.2b1/HedgeTech/Auth/__AuthAsyncClient.py +0 -154
- hedgetech-0.3.2b1/HedgeTech/Auth/__AuthSyncClient.py +0 -157
- hedgetech-0.3.2b1/HedgeTech/DataEngine/__ime/__AsyncClient.py +0 -349
- hedgetech-0.3.2b1/HedgeTech/DataEngine/__ime/__SyncClient.py +0 -339
- hedgetech-0.3.2b1/HedgeTech/EmsEngine/__tse_ifb/__AsyncClient.py +0 -858
- hedgetech-0.3.2b1/HedgeTech/EmsEngine/__tse_ifb/__SyncClient.py +0 -875
- hedgetech-0.3.2b1/HedgeTech/EmsEngine/__tse_ifb/__io_types/__response.py +0 -79
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/Auth/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/Auth/__utils/__RetriableAsyncClient.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/Auth/__utils/__RetriableSyncClient.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/Auth/__utils/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__ime/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__ime/__io_types/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__tse_ifb/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/DataEngine/__tse_ifb/__io_types/__init__.py +0 -0
- {hedgetech-0.3.2b1/HedgeTech/EmsEngine/__tse_ifb → hedgetech-0.4.0b0/HedgeTech/EmsEngine/__ime}/__io_types/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech/EmsEngine/__tse_ifb/__init__.py +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech.egg-info/dependency_links.txt +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech.egg-info/requires.txt +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/HedgeTech.egg-info/top_level.txt +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/LICENSE +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/NOTICE +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/README.md +0 -0
- {hedgetech-0.3.2b1 → hedgetech-0.4.0b0}/setup.cfg +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# ========================================|======================================== #
|
|
2
|
+
# Imports #
|
|
3
|
+
# ========================================|======================================== #
|
|
4
|
+
|
|
5
|
+
from .__utils import RetriableAsyncClient
|
|
6
|
+
from httpx import (
|
|
7
|
+
AsyncClient ,
|
|
8
|
+
Timeout ,
|
|
9
|
+
)
|
|
10
|
+
from jwt import decode
|
|
11
|
+
|
|
12
|
+
# ========================================|======================================== #
|
|
13
|
+
# Class Definitions #
|
|
14
|
+
# ========================================|======================================== #
|
|
15
|
+
|
|
16
|
+
class AuthAsyncClient:
|
|
17
|
+
"""
|
|
18
|
+
Asynchronous client for interacting with the HedgeTech API using JWT-based authentication.
|
|
19
|
+
|
|
20
|
+
This class provides a high-level interface to authenticate users, manage JWT tokens,
|
|
21
|
+
and make authorized HTTP requests using an `httpx.AsyncClient`.
|
|
22
|
+
|
|
23
|
+
Normally an instance is created with ``AuthAsyncClient.login(...)``. Calling the
|
|
24
|
+
constructor directly is only useful when you already have a configured HTTP client and
|
|
25
|
+
a valid token. The resulting object is what the data clients
|
|
26
|
+
(``DataEngine_Ime_AsyncClient``, ``DataEngine_TseIfb_AsyncClient``) receive: they use
|
|
27
|
+
``httpx_Client`` for REST requests and ``token`` as the WebSocket headers.
|
|
28
|
+
|
|
29
|
+
Note:
|
|
30
|
+
* The client does not refresh the token. When the JWT expires, requests start to
|
|
31
|
+
fail and you need to call ``login`` again to get a new client.
|
|
32
|
+
* ``Permissions`` is decoded WITHOUT verifying the signature or the expiry, so it is
|
|
33
|
+
only meant for reading the claims (e.g. to display them), never for making
|
|
34
|
+
security decisions.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
httpx_Client (AsyncClient): The HTTP client used for authorized requests. When created
|
|
38
|
+
by ``login`` it is a ``RetriableAsyncClient`` (a wrapper around ``httpx.AsyncClient``
|
|
39
|
+
that retries failed requests, up to 10 times) configured with:
|
|
40
|
+
|
|
41
|
+
- the authentication headers (the token returned by the server plus an ``origin``
|
|
42
|
+
header) and the cookies received at login;
|
|
43
|
+
- HTTP/2 only (``http1=False``, ``http2=True``) with TLS verification enabled;
|
|
44
|
+
- short timeouts: connect 0.25 s, read 1.5 s, write 1.5 s, pool 0.25 s.
|
|
45
|
+
token (dict[str, str]): Dictionary containing the authentication tokens. It must contain
|
|
46
|
+
the ``'Authorization'`` key holding the JWT string. It is also passed as the
|
|
47
|
+
``additional_headers`` of WebSocket connections.
|
|
48
|
+
Permissions (dict): The decoded JWT payload (its claims), set by ``UpdatePermission``
|
|
49
|
+
when the object is created. The available claims depend on the server.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> client = await AuthAsyncClient.login(
|
|
53
|
+
... UserName_or_Email="user@example.com",
|
|
54
|
+
... Password="secure_password"
|
|
55
|
+
... )
|
|
56
|
+
>>> client.token['Authorization'][:10]
|
|
57
|
+
'eyJ0eXAiOi'
|
|
58
|
+
>>> client.Permissions # Decoded JWT payload
|
|
59
|
+
{'sub': 'user_id', 'exp': 1700350200, ...}
|
|
60
|
+
>>> data_client = DataEngine_TseIfb_AsyncClient(AuthAsyncClient=client)
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
*,
|
|
66
|
+
httpx_Client:AsyncClient,
|
|
67
|
+
token:dict[str,str],
|
|
68
|
+
):
|
|
69
|
+
"""
|
|
70
|
+
Initialize the AuthAsyncClient with an existing httpx.AsyncClient and authentication token.
|
|
71
|
+
|
|
72
|
+
Both arguments are keyword-only. The token is decoded immediately (see
|
|
73
|
+
``UpdatePermission``), so ``Permissions`` is available right after construction.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
httpx_Client (AsyncClient): Preconfigured AsyncClient for making authorized requests.
|
|
77
|
+
It should already send the authentication headers (``login`` does this for you).
|
|
78
|
+
token (dict[str, str]): Dictionary containing authentication tokens. It must contain
|
|
79
|
+
the ``'Authorization'`` key with the JWT string.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
ValueError: If the token cannot be decoded (see ``UpdatePermission``).
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
>>> client = AuthAsyncClient(
|
|
86
|
+
... httpx_Client=my_httpx_client,
|
|
87
|
+
... token={"Authorization": "<jwt>"}
|
|
88
|
+
... )
|
|
89
|
+
>>> client.Permissions
|
|
90
|
+
{'sub': 'user_id', 'exp': 1700350200, ...}
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
self.httpx_Client : AsyncClient = httpx_Client
|
|
94
|
+
self.token : dict[str,str] = token
|
|
95
|
+
|
|
96
|
+
self.UpdatePermission
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def UpdatePermission(self)-> None:
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
Decodes the JWT 'Authorization' token to extract user permissions.
|
|
104
|
+
|
|
105
|
+
This is a **property**, not a method: reading ``client.UpdatePermission`` (without
|
|
106
|
+
parentheses) performs the work and returns ``None``. It is read once in ``__init__``;
|
|
107
|
+
read it again to re-decode ``token`` after you replace it.
|
|
108
|
+
|
|
109
|
+
The decoded payload is stored in ``self.Permissions``. The token is decoded
|
|
110
|
+
WITHOUT verifying the signature, and the expiry (``exp``) is not checked either.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: If the JWT cannot be decoded. Any underlying error is wrapped in
|
|
114
|
+
``ValueError``, including a missing ``'Authorization'`` key in ``token``.
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
>>> client.token = new_token
|
|
118
|
+
>>> client.UpdatePermission # no parentheses; re-decodes the token
|
|
119
|
+
>>> client.Permissions
|
|
120
|
+
{'sub': 'user_id', 'exp': 1700350200, ...}
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
try :
|
|
124
|
+
|
|
125
|
+
self.Permissions = decode(
|
|
126
|
+
jwt=self.token['Authorization'].encode(),
|
|
127
|
+
algorithms='HS256',
|
|
128
|
+
options={"verify_signature": False},
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
|
|
133
|
+
raise ValueError(e)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
async def login(
|
|
138
|
+
cls,
|
|
139
|
+
*,
|
|
140
|
+
UserName_or_Email : str,
|
|
141
|
+
Password : str
|
|
142
|
+
)-> "AuthAsyncClient":
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
Authenticate a user and return an instance of `AuthAsyncClient`.
|
|
146
|
+
|
|
147
|
+
This method performs the login request to the HedgeTech API, retrieves JWT tokens,
|
|
148
|
+
and configures an `httpx.AsyncClient` with authentication headers and cookies.
|
|
149
|
+
|
|
150
|
+
It sends ``POST https://core.hedgetech.ir/auth/user/token/issue`` with the credentials as
|
|
151
|
+
form data. The login succeeds only when the server answers with HTTP status ``201``.
|
|
152
|
+
The JSON body of that answer is used as ``token`` and is also sent as headers on every
|
|
153
|
+
later request, together with an ``origin: https://core.hedgetech.ir`` header. The cookies
|
|
154
|
+
set at login are kept.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
UserName_or_Email (str): The username or email of the user.
|
|
158
|
+
Password (str): The user's password.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
AuthAsyncClient: An initialized client ready to make authorized requests. Its
|
|
162
|
+
``httpx_Client`` is a ``RetriableAsyncClient`` (10 retries) using HTTP/2 with the
|
|
163
|
+
short timeouts described in the class docstring.
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
ValueError: If the login attempt fails (e.g., wrong credentials), i.e. the status is
|
|
167
|
+
not ``201``. The message is the server's ``detail`` field, which is ``None`` if
|
|
168
|
+
the server did not send one. The same exception is raised if the token returned
|
|
169
|
+
by the server cannot be decoded (see ``UpdatePermission``).
|
|
170
|
+
|
|
171
|
+
Note:
|
|
172
|
+
Network errors while sending the login request (for example a connection failure or
|
|
173
|
+
timeout) are raised by ``httpx`` as they are and are not converted to ``ValueError``.
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
>>> client = await AuthAsyncClient.login(
|
|
177
|
+
... UserName_or_Email="user@example.com",
|
|
178
|
+
... Password="secure_password"
|
|
179
|
+
... )
|
|
180
|
+
>>> client.token['Authorization'][:10]
|
|
181
|
+
'eyJ0eXAiOi'
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
httpx_Client = AsyncClient(verify=True ,http1=False ,http2=True)
|
|
185
|
+
|
|
186
|
+
login_res = await httpx_Client.post(
|
|
187
|
+
url='https://core.hedgetech.ir/auth/user/token/issue',
|
|
188
|
+
data={
|
|
189
|
+
'UserName_or_Email' : UserName_or_Email,
|
|
190
|
+
'Password' : Password
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
if login_res.status_code == 201:
|
|
195
|
+
|
|
196
|
+
token = login_res.json()
|
|
197
|
+
headers = {'origin' : 'https://core.hedgetech.ir'}
|
|
198
|
+
headers.update(token)
|
|
199
|
+
|
|
200
|
+
httpx_Client = RetriableAsyncClient(
|
|
201
|
+
client = AsyncClient(
|
|
202
|
+
verify=True ,
|
|
203
|
+
http1=False ,
|
|
204
|
+
http2=True ,
|
|
205
|
+
headers=headers,
|
|
206
|
+
cookies=httpx_Client.cookies,
|
|
207
|
+
timeout=Timeout(
|
|
208
|
+
connect=.250,
|
|
209
|
+
read=1.5,
|
|
210
|
+
write=1.5,
|
|
211
|
+
pool=.250,
|
|
212
|
+
),
|
|
213
|
+
),
|
|
214
|
+
retries= 10
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return cls(
|
|
218
|
+
httpx_Client = httpx_Client,
|
|
219
|
+
token = token
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
else :
|
|
223
|
+
|
|
224
|
+
raise ValueError(login_res.json().get('detail'))
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# ========================================|======================================== #
|
|
2
|
+
# Imports #
|
|
3
|
+
# ========================================|======================================== #
|
|
4
|
+
|
|
5
|
+
from .__utils import RetriableSyncClient
|
|
6
|
+
from httpx import (
|
|
7
|
+
Client ,
|
|
8
|
+
Timeout ,
|
|
9
|
+
)
|
|
10
|
+
from jwt import decode
|
|
11
|
+
|
|
12
|
+
# ========================================|======================================== #
|
|
13
|
+
# Class Definitions #
|
|
14
|
+
# ========================================|======================================== #
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AuthSyncClient:
|
|
18
|
+
"""
|
|
19
|
+
Synchronous client for interacting with the HedgeTech API using JWT-based authentication.
|
|
20
|
+
|
|
21
|
+
This class provides a high-level interface to authenticate users, manage JWT tokens,
|
|
22
|
+
and make authorized HTTP requests using an `httpx.Client`.
|
|
23
|
+
|
|
24
|
+
This is the blocking twin of ``AuthAsyncClient``: same login endpoint, token handling and
|
|
25
|
+
configuration, only the calling style differs (plain calls instead of ``await``).
|
|
26
|
+
|
|
27
|
+
Normally an instance is created with ``AuthSyncClient.login(...)``. Calling the
|
|
28
|
+
constructor directly is only useful when you already have a configured HTTP client and
|
|
29
|
+
a valid token. The resulting object is what the data clients
|
|
30
|
+
(``DataEngine_Ime_SyncClient``, ``DataEngine_TseIfb_SyncClient``) receive: they use
|
|
31
|
+
``httpx_Client`` for REST requests and ``token`` as the WebSocket headers.
|
|
32
|
+
|
|
33
|
+
Note:
|
|
34
|
+
* The client does not refresh the token. When the JWT expires, requests start to
|
|
35
|
+
fail and you need to call ``login`` again to get a new client.
|
|
36
|
+
* ``Permissions`` is decoded WITHOUT verifying the signature or the expiry, so it is
|
|
37
|
+
only meant for reading the claims (e.g. to display them), never for making
|
|
38
|
+
security decisions.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
httpx_Client (Client): The HTTP client used for authorized requests. When created
|
|
42
|
+
by ``login`` it is a ``RetriableSyncClient`` (a wrapper around ``httpx.Client``
|
|
43
|
+
that retries failed requests, up to 10 times) configured with:
|
|
44
|
+
|
|
45
|
+
- the authentication headers (the token returned by the server plus an ``origin``
|
|
46
|
+
header) and the cookies received at login;
|
|
47
|
+
- HTTP/2 only (``http1=False``, ``http2=True``) with TLS verification enabled;
|
|
48
|
+
- short timeouts: connect 0.25 s, read 1.5 s, write 1.5 s, pool 0.25 s.
|
|
49
|
+
token (dict[str, str]): Dictionary containing the authentication tokens. It must contain
|
|
50
|
+
the ``'Authorization'`` key holding the JWT string. It is also passed as the
|
|
51
|
+
``additional_headers`` of WebSocket connections.
|
|
52
|
+
Permissions (dict): The decoded JWT payload (its claims), set by ``UpdatePermission``
|
|
53
|
+
when the object is created. The available claims depend on the server.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> client = AuthSyncClient.login(
|
|
57
|
+
... UserName_or_Email="user@example.com",
|
|
58
|
+
... Password="secure_password"
|
|
59
|
+
... )
|
|
60
|
+
>>> client.token['Authorization'][:10]
|
|
61
|
+
'eyJ0eXAiOi'
|
|
62
|
+
>>> client.Permissions # Decoded JWT payload
|
|
63
|
+
{'sub': 'user_id', 'exp': 1700350200, ...}
|
|
64
|
+
>>> data_client = DataEngine_TseIfb_SyncClient(AuthSyncClient=client)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
*,
|
|
70
|
+
httpx_Client:Client,
|
|
71
|
+
token:dict[str,str],
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
Initialize the AuthSyncClient with an existing httpx.Client and authentication token.
|
|
75
|
+
|
|
76
|
+
Both arguments are keyword-only. The token is decoded immediately (see
|
|
77
|
+
``UpdatePermission``), so ``Permissions`` is available right after construction.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
httpx_Client (Client): Preconfigured Client for making authorized requests.
|
|
81
|
+
It should already send the authentication headers (``login`` does this for you).
|
|
82
|
+
token (dict[str, str]): Dictionary containing authentication tokens. It must contain
|
|
83
|
+
the ``'Authorization'`` key with the JWT string.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
ValueError: If the token cannot be decoded (see ``UpdatePermission``).
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
>>> client = AuthSyncClient(
|
|
90
|
+
... httpx_Client=my_httpx_client,
|
|
91
|
+
... token={"Authorization": "<jwt>"}
|
|
92
|
+
... )
|
|
93
|
+
>>> client.Permissions
|
|
94
|
+
{'sub': 'user_id', 'exp': 1700350200, ...}
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
self.httpx_Client : Client = httpx_Client
|
|
98
|
+
self.token : dict[str,str] = token
|
|
99
|
+
|
|
100
|
+
self.UpdatePermission
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def UpdatePermission(self)-> None:
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
Decodes the JWT 'Authorization' token to extract user permissions.
|
|
108
|
+
|
|
109
|
+
This is a **property**, not a method: reading ``client.UpdatePermission`` (without
|
|
110
|
+
parentheses) performs the work and returns ``None``. It is read once in ``__init__``;
|
|
111
|
+
read it again to re-decode ``token`` after you replace it.
|
|
112
|
+
|
|
113
|
+
The decoded payload is stored in ``self.Permissions``. The token is decoded
|
|
114
|
+
WITHOUT verifying the signature, and the expiry (``exp``) is not checked either.
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
ValueError: If the JWT cannot be decoded. Any underlying error is wrapped in
|
|
118
|
+
``ValueError``, including a missing ``'Authorization'`` key in ``token``.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
>>> client.token = new_token
|
|
122
|
+
>>> client.UpdatePermission # no parentheses; re-decodes the token
|
|
123
|
+
>>> client.Permissions
|
|
124
|
+
{'sub': 'user_id', 'exp': 1700350200, ...}
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
try :
|
|
128
|
+
|
|
129
|
+
self.Permissions = decode(
|
|
130
|
+
jwt=self.token['Authorization'].encode(),
|
|
131
|
+
algorithms='HS256',
|
|
132
|
+
options={"verify_signature": False},
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
|
|
137
|
+
raise ValueError(e)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@classmethod
|
|
141
|
+
def login(
|
|
142
|
+
cls,
|
|
143
|
+
*,
|
|
144
|
+
UserName_or_Email : str,
|
|
145
|
+
Password : str
|
|
146
|
+
)-> "AuthSyncClient":
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
Authenticate a user synchronously and return an instance of `AuthSyncClient`.
|
|
150
|
+
|
|
151
|
+
This method performs the login request to the HedgeTech API, retrieves JWT tokens,
|
|
152
|
+
and configures an `httpx.Client` with authentication headers and cookies.
|
|
153
|
+
|
|
154
|
+
It sends ``POST https://core.hedgetech.ir/auth/user/token/issue`` with the credentials as
|
|
155
|
+
form data. The login succeeds only when the server answers with HTTP status ``201``.
|
|
156
|
+
The JSON body of that answer is used as ``token`` and is also sent as headers on every
|
|
157
|
+
later request, together with an ``origin: https://core.hedgetech.ir`` header. The cookies
|
|
158
|
+
set at login are kept.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
UserName_or_Email (str): The username or email of the user.
|
|
162
|
+
Password (str): The user's password.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
AuthSyncClient: An initialized client ready to make authorized requests. Its
|
|
166
|
+
``httpx_Client`` is a ``RetriableSyncClient`` (10 retries) using HTTP/2 with the
|
|
167
|
+
short timeouts described in the class docstring.
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
ValueError: If the login attempt fails (e.g., wrong credentials), i.e. the status is
|
|
171
|
+
not ``201``. The message is the server's ``detail`` field, which is ``None`` if
|
|
172
|
+
the server did not send one. The same exception is raised if the token returned
|
|
173
|
+
by the server cannot be decoded (see ``UpdatePermission``).
|
|
174
|
+
|
|
175
|
+
Note:
|
|
176
|
+
Network errors while sending the login request (for example a connection failure or
|
|
177
|
+
timeout) are raised by ``httpx`` as they are and are not converted to ``ValueError``.
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
>>> client = AuthSyncClient.login(
|
|
181
|
+
... UserName_or_Email="user@example.com",
|
|
182
|
+
... Password="secure_password"
|
|
183
|
+
... )
|
|
184
|
+
>>> client.token['Authorization'][:10]
|
|
185
|
+
'eyJ0eXAiOi'
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
httpx_Client = Client(verify=True ,http1=False ,http2=True)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
login_res = httpx_Client.post(
|
|
192
|
+
url='https://core.hedgetech.ir/auth/user/token/issue',
|
|
193
|
+
data={
|
|
194
|
+
'UserName_or_Email' : UserName_or_Email,
|
|
195
|
+
'Password' : Password
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if login_res.status_code == 201:
|
|
201
|
+
|
|
202
|
+
token = login_res.json()
|
|
203
|
+
headers = {'origin' : 'https://core.hedgetech.ir'}
|
|
204
|
+
headers.update(token)
|
|
205
|
+
|
|
206
|
+
httpx_Client = RetriableSyncClient(
|
|
207
|
+
client = Client(
|
|
208
|
+
verify=True ,
|
|
209
|
+
http1=False ,
|
|
210
|
+
http2=True ,
|
|
211
|
+
headers=headers,
|
|
212
|
+
cookies=httpx_Client.cookies,
|
|
213
|
+
timeout=Timeout(
|
|
214
|
+
connect=.250,
|
|
215
|
+
read=1.5,
|
|
216
|
+
write=1.5,
|
|
217
|
+
pool=.250,
|
|
218
|
+
),
|
|
219
|
+
),
|
|
220
|
+
retries= 10
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return cls(
|
|
224
|
+
httpx_Client = httpx_Client,
|
|
225
|
+
token = token
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
else :
|
|
229
|
+
|
|
230
|
+
raise ValueError(login_res.json().get('detail'))
|