|
14 | 14 | """It contains everything about certification via a file-based provider."""
|
15 | 15 |
|
16 | 16 | import threading
|
17 |
| -from datetime import datetime |
| 17 | +from datetime import datetime, timedelta |
18 | 18 | from typing import Optional
|
19 | 19 | from xml.etree import ElementTree
|
20 | 20 |
|
| 21 | +import requests |
| 22 | +from tos.consts import ECS_DATE_FORMAT |
21 | 23 | from tos.credential import Credentials, CredentialsProvider
|
22 | 24 |
|
23 | 25 | from tosfs.core import logger
|
@@ -182,3 +184,55 @@ def _try_get_credentials(self) -> Optional[Credentials]:
|
182 | 184 | )
|
183 | 185 | return None
|
184 | 186 | return self.credentials
|
| 187 | + |
| 188 | + |
| 189 | +class UrlCredentialsProvider(CredentialsProvider): |
| 190 | + """The class provides the credentials from an url.""" |
| 191 | + |
| 192 | + def __init__(self, credential_url: str): |
| 193 | + """Initialize the UrlCredentialsProvider.""" |
| 194 | + if not credential_url: |
| 195 | + raise TosfsCertificationError("The credential_url param must not be empty.") |
| 196 | + self._lock = threading.Lock() |
| 197 | + self.expires: Optional[datetime] = None |
| 198 | + self.credentials = None |
| 199 | + self.credential_url = credential_url |
| 200 | + |
| 201 | + def get_credentials(self) -> Credentials: |
| 202 | + """Get the credentials from the url.""" |
| 203 | + res = self._try_get_credentials() |
| 204 | + if res is not None: |
| 205 | + return res |
| 206 | + with self._lock: |
| 207 | + try: |
| 208 | + res = self._try_get_credentials() |
| 209 | + if res is not None: |
| 210 | + return res |
| 211 | + |
| 212 | + res = requests.get(self.credential_url, timeout=30) |
| 213 | + res_body = res.json() |
| 214 | + self.credentials = Credentials( |
| 215 | + res_body.get("AccessKeyId"), |
| 216 | + res_body.get("SecretAccessKey"), |
| 217 | + res_body.get("SessionToken"), |
| 218 | + ) |
| 219 | + self.expires = datetime.strptime( |
| 220 | + res_body.get("ExpiredTime"), ECS_DATE_FORMAT |
| 221 | + ) |
| 222 | + return self.credentials |
| 223 | + except Exception as e: |
| 224 | + if self.expires is not None and ( |
| 225 | + datetime.now().timestamp() < self.expires.timestamp() |
| 226 | + ): |
| 227 | + return self.credentials |
| 228 | + raise TosfsCertificationError("Get token failed") from e |
| 229 | + |
| 230 | + def _try_get_credentials(self) -> Optional[Credentials]: |
| 231 | + if self.expires is None or self.credentials is None: |
| 232 | + return None |
| 233 | + if ( |
| 234 | + datetime.now().timestamp() |
| 235 | + > (self.expires - timedelta(minutes=10)).timestamp() |
| 236 | + ): |
| 237 | + return None |
| 238 | + return self.credentials |
0 commit comments