stackit-core 0.3.0__tar.gz → 0.3.1__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.
- {stackit_core-0.3.0 → stackit_core-0.3.1}/PKG-INFO +2 -2
- {stackit_core-0.3.0 → stackit_core-0.3.1}/pyproject.toml +1 -1
- {stackit_core-0.3.0 → stackit_core-0.3.1}/src/stackit/core/auth_methods/key_auth.py +25 -12
- {stackit_core-0.3.0 → stackit_core-0.3.1}/.gitignore +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/LICENSE.md +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/NOTICE.txt +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/README.md +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/src/stackit/core/__init__.py +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/src/stackit/core/auth_methods/token_auth.py +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/src/stackit/core/authorization.py +0 -0
- {stackit_core-0.3.0 → stackit_core-0.3.1}/src/stackit/core/configuration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: stackit-core
|
|
3
|
-
Version:
|
|
3
|
+
Version: v0.3.1
|
|
4
4
|
Summary: Core functionality for the STACKIT SDK for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/stackitcloud/stackit-sdk-python
|
|
6
6
|
Project-URL: Issues, https://github.com/stackitcloud/stackit-sdk-python/issues
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stackit-core"
|
|
3
|
-
version = "v0.3.
|
|
3
|
+
version = "v0.3.1"
|
|
4
4
|
description = "Core functionality for the STACKIT SDK for Python"
|
|
5
5
|
authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }]
|
|
6
6
|
requires-python = ">=3.10,<4.0"
|
|
@@ -79,6 +79,16 @@ class KeyAuth(AuthBase):
|
|
|
79
79
|
if self.__is_token_expired(self.access_token):
|
|
80
80
|
if self.refresh_future is None or self.refresh_future.done():
|
|
81
81
|
self.refresh_future = self.executor.submit(self.__refresh_token)
|
|
82
|
+
refresh_future = self.refresh_future
|
|
83
|
+
else:
|
|
84
|
+
refresh_future = None
|
|
85
|
+
|
|
86
|
+
# Do not hold the lock while waiting. The refresh worker acquires it when
|
|
87
|
+
# updating the access token.
|
|
88
|
+
if refresh_future is not None:
|
|
89
|
+
refresh_future.result()
|
|
90
|
+
|
|
91
|
+
with self.lock:
|
|
82
92
|
r.headers["Authorization"] = f"Bearer {self.access_token}"
|
|
83
93
|
return r
|
|
84
94
|
|
|
@@ -108,8 +118,9 @@ class KeyAuth(AuthBase):
|
|
|
108
118
|
response = requests.post(self.token_endpoint, data=body, timeout=self.timeout)
|
|
109
119
|
response.raise_for_status()
|
|
110
120
|
response_json = response.json()
|
|
111
|
-
self.
|
|
112
|
-
|
|
121
|
+
with self.lock:
|
|
122
|
+
self.access_token = response_json["access_token"]
|
|
123
|
+
self.refresh_token = response_json["refresh_token"]
|
|
113
124
|
except requests.RequestException as e:
|
|
114
125
|
raise requests.RequestException("Initial token fetch failed") from e
|
|
115
126
|
|
|
@@ -130,14 +141,17 @@ class KeyAuth(AuthBase):
|
|
|
130
141
|
thread.start()
|
|
131
142
|
|
|
132
143
|
def __refresh_token(self):
|
|
133
|
-
|
|
144
|
+
with self.lock:
|
|
145
|
+
refresh_token = self.refresh_token
|
|
146
|
+
|
|
147
|
+
if self.__is_token_expired(refresh_token):
|
|
134
148
|
self.__create_initial_token()
|
|
135
149
|
self.__fetch_token_from_endpoint()
|
|
136
150
|
return
|
|
137
151
|
|
|
138
152
|
body = {
|
|
139
153
|
"grant_type": "refresh_token",
|
|
140
|
-
"refresh_token":
|
|
154
|
+
"refresh_token": refresh_token,
|
|
141
155
|
}
|
|
142
156
|
|
|
143
157
|
last_exception = None
|
|
@@ -147,24 +161,23 @@ class KeyAuth(AuthBase):
|
|
|
147
161
|
response.raise_for_status()
|
|
148
162
|
response_data = response.json()
|
|
149
163
|
new_token = response_data.get("access_token")
|
|
150
|
-
self.
|
|
164
|
+
with self.lock:
|
|
165
|
+
self.access_token = new_token
|
|
151
166
|
return
|
|
152
167
|
except requests.RequestException as e:
|
|
153
168
|
last_exception = e
|
|
154
169
|
|
|
155
170
|
raise requests.RequestException("Token refresh failed after retries") from last_exception
|
|
156
171
|
|
|
157
|
-
def __is_token_expired(self, token: str) -> bool:
|
|
172
|
+
def __is_token_expired(self, token: Optional[str]) -> bool:
|
|
158
173
|
try:
|
|
159
174
|
decoded_token = jwt.decode(token, options={"verify_signature": False})
|
|
160
175
|
exp = decoded_token.get("exp")
|
|
161
|
-
if exp:
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
except jwt.DecodeError:
|
|
176
|
+
if exp is None:
|
|
177
|
+
return True
|
|
178
|
+
return time.time() > (float(exp) - self.EXPIRATION_LEEWAY.total_seconds())
|
|
179
|
+
except (jwt.InvalidTokenError, TypeError, ValueError):
|
|
166
180
|
return True
|
|
167
|
-
return False
|
|
168
181
|
|
|
169
182
|
def __shutdown(self):
|
|
170
183
|
self.executor.shutdown(wait=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|