boto3-refresh-session 3.0.3__py3-none-any.whl → 5.0.0__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.
- boto3_refresh_session/__init__.py +2 -2
- boto3_refresh_session/exceptions.py +8 -0
- boto3_refresh_session/methods/__init__.py +5 -7
- boto3_refresh_session/methods/custom.py +1 -1
- boto3_refresh_session/methods/iot/__init__.py +7 -0
- boto3_refresh_session/methods/iot/{core.typed → core.py} +4 -16
- boto3_refresh_session/methods/iot/x509.py +336 -0
- boto3_refresh_session/methods/sts.py +2 -2
- boto3_refresh_session/session.py +2 -2
- boto3_refresh_session/utils/internal.py +39 -1
- boto3_refresh_session/utils/typing.py +9 -8
- {boto3_refresh_session-3.0.3.dist-info → boto3_refresh_session-5.0.0.dist-info}/METADATA +71 -26
- boto3_refresh_session-5.0.0.dist-info/RECORD +17 -0
- boto3_refresh_session/methods/ecs.py +0 -115
- boto3_refresh_session/methods/iot/__init__.typed +0 -4
- boto3_refresh_session/methods/iot/certificate.typed +0 -57
- boto3_refresh_session/methods/iot/cognito.typed +0 -17
- boto3_refresh_session-3.0.3.dist-info/RECORD +0 -19
- {boto3_refresh_session-3.0.3.dist-info → boto3_refresh_session-5.0.0.dist-info}/LICENSE +0 -0
- {boto3_refresh_session-3.0.3.dist-info → boto3_refresh_session-5.0.0.dist-info}/NOTICE +0 -0
- {boto3_refresh_session-3.0.3.dist-info → boto3_refresh_session-5.0.0.dist-info}/WHEEL +0 -0
@@ -3,13 +3,13 @@ __all__ = []
|
|
3
3
|
from . import exceptions, session
|
4
4
|
from .exceptions import *
|
5
5
|
from .methods.custom import *
|
6
|
-
from .methods.
|
6
|
+
from .methods.iot import *
|
7
7
|
from .methods.sts import *
|
8
8
|
from .session import *
|
9
9
|
|
10
10
|
__all__.extend(session.__all__)
|
11
11
|
__all__.extend(exceptions.__all__)
|
12
|
-
__version__ = "
|
12
|
+
__version__ = "5.0.0"
|
13
13
|
__title__ = "boto3-refresh-session"
|
14
14
|
__author__ = "Mike Letts"
|
15
15
|
__maintainer__ = "Mike Letts"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
__all__ = ["BRSError", "BRSWarning"]
|
2
2
|
|
3
|
+
import warnings
|
4
|
+
|
3
5
|
|
4
6
|
class BRSError(Exception):
|
5
7
|
"""The base exception for boto3-refresh-session.
|
@@ -39,3 +41,9 @@ class BRSWarning(UserWarning):
|
|
39
41
|
|
40
42
|
def __repr__(self) -> str:
|
41
43
|
return f"{self.__class__.__name__}({self.message!r})"
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def warn(cls, message: str, *, stacklevel: int = 2):
|
47
|
+
"""Emits a BRSWarning with a consistent stacklevel."""
|
48
|
+
|
49
|
+
warnings.warn(cls(message), stacklevel=stacklevel)
|
@@ -1,12 +1,10 @@
|
|
1
1
|
__all__ = []
|
2
2
|
|
3
|
-
|
4
|
-
from . import
|
5
|
-
from .
|
6
|
-
from .
|
7
|
-
from .sts import STSRefreshableSession
|
3
|
+
from . import custom, iot, sts
|
4
|
+
from .custom import *
|
5
|
+
from .iot import *
|
6
|
+
from .sts import *
|
8
7
|
|
9
|
-
# TODO: add iot submodules to __all__ when finished
|
10
8
|
__all__.extend(custom.__all__)
|
11
|
-
__all__.extend(
|
9
|
+
__all__.extend(iot.__all__)
|
12
10
|
__all__.extend(sts.__all__)
|
@@ -6,28 +6,16 @@ from typing import get_args
|
|
6
6
|
|
7
7
|
from ...exceptions import BRSError
|
8
8
|
from ...utils import (
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
IoTAuthenticationMethod,
|
13
|
-
Registry,
|
9
|
+
BaseIoTRefreshableSession,
|
10
|
+
BaseRefreshableSession,
|
11
|
+
IoTAuthenticationMethod,
|
14
12
|
)
|
15
13
|
|
16
14
|
|
17
|
-
class BaseIoTRefreshableSession(
|
18
|
-
Registry[IoTAuthenticationMethod],
|
19
|
-
CredentialProvider,
|
20
|
-
BRSSession,
|
21
|
-
registry_key="__iot_sentinel__",
|
22
|
-
):
|
23
|
-
def __init__(self, **kwargs):
|
24
|
-
super().__init__(**kwargs)
|
25
|
-
|
26
|
-
|
27
15
|
class IoTRefreshableSession(BaseRefreshableSession, registry_key="iot"):
|
28
16
|
def __new__(
|
29
17
|
cls,
|
30
|
-
authentication_method: IoTAuthenticationMethod = "
|
18
|
+
authentication_method: IoTAuthenticationMethod = "x509",
|
31
19
|
**kwargs,
|
32
20
|
) -> BaseIoTRefreshableSession:
|
33
21
|
if authentication_method not in (
|
@@ -0,0 +1,336 @@
|
|
1
|
+
__all__ = ["IOTX509RefreshableSession"]
|
2
|
+
|
3
|
+
import json
|
4
|
+
import re
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import cast
|
7
|
+
from urllib.parse import ParseResult, urlparse
|
8
|
+
|
9
|
+
from awscrt.exceptions import AwsCrtError
|
10
|
+
from awscrt.http import HttpClientConnection, HttpRequest
|
11
|
+
from awscrt.io import (
|
12
|
+
ClientBootstrap,
|
13
|
+
ClientTlsContext,
|
14
|
+
DefaultHostResolver,
|
15
|
+
EventLoopGroup,
|
16
|
+
Pkcs11Lib,
|
17
|
+
TlsConnectionOptions,
|
18
|
+
TlsContextOptions,
|
19
|
+
)
|
20
|
+
|
21
|
+
from ...exceptions import BRSError, BRSWarning
|
22
|
+
from ...utils import (
|
23
|
+
PKCS11,
|
24
|
+
AWSCRTResponse,
|
25
|
+
Identity,
|
26
|
+
TemporaryCredentials,
|
27
|
+
refreshable_session,
|
28
|
+
)
|
29
|
+
from .core import BaseIoTRefreshableSession
|
30
|
+
|
31
|
+
|
32
|
+
@refreshable_session
|
33
|
+
class IOTX509RefreshableSession(
|
34
|
+
BaseIoTRefreshableSession, registry_key="x509"
|
35
|
+
):
|
36
|
+
"""A :class:`boto3.session.Session` object that automatically refreshes
|
37
|
+
temporary credentials returned by the IoT Core credential provider.
|
38
|
+
|
39
|
+
Parameters
|
40
|
+
----------
|
41
|
+
endpoint : str
|
42
|
+
The endpoint URL for the IoT Core credential provider. Must contain
|
43
|
+
'.credentials.iot.'.
|
44
|
+
role_alias : str
|
45
|
+
The IAM role alias to use when requesting temporary credentials.
|
46
|
+
certificate : str | bytes
|
47
|
+
The X.509 certificate to use when requesting temporary credentials.
|
48
|
+
``str`` represents the file path to the certificate, while ``bytes``
|
49
|
+
represents the actual certificate data.
|
50
|
+
thing_name : str, optional
|
51
|
+
The name of the IoT thing to use when requesting temporary
|
52
|
+
credentials. Default is None.
|
53
|
+
private_key : str | bytes | None, optional
|
54
|
+
The private key to use when requesting temporary credentials. ``str``
|
55
|
+
represents the file path to the private key, while ``bytes``
|
56
|
+
represents the actual private key data. Optional only if ``pkcs11``
|
57
|
+
is provided. Default is None.
|
58
|
+
pkcs11 : PKCS11, optional
|
59
|
+
The PKCS#11 library to use when requesting temporary credentials. If
|
60
|
+
provided, ``private_key`` must be None.
|
61
|
+
ca : str | bytes | None, optional
|
62
|
+
The CA certificate to use when verifying the IoT Core endpoint. ``str``
|
63
|
+
represents the file path to the CA certificate, while ``bytes``
|
64
|
+
represents the actual CA certificate data. Default is None.
|
65
|
+
verify_peer : bool, optional
|
66
|
+
Whether to verify the CA certificate when establishing the TLS
|
67
|
+
connection. Default is True.
|
68
|
+
timeout : float | int | None, optional
|
69
|
+
The timeout for the TLS connection in seconds. Default is 10.0.
|
70
|
+
duration_seconds : int | None, optional
|
71
|
+
The duration for which the temporary credentials are valid, in
|
72
|
+
seconds. Cannot exceed the value declared in the IAM policy.
|
73
|
+
Default is None.
|
74
|
+
|
75
|
+
Notes
|
76
|
+
-----
|
77
|
+
Gavin Adams at AWS was a major influence on this implementation.
|
78
|
+
Thank you, Gavin!
|
79
|
+
"""
|
80
|
+
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
endpoint: str,
|
84
|
+
role_alias: str,
|
85
|
+
certificate: str | bytes,
|
86
|
+
thing_name: str | None = None,
|
87
|
+
private_key: str | bytes | None = None,
|
88
|
+
pkcs11: PKCS11 | None = None,
|
89
|
+
ca: str | bytes | None = None,
|
90
|
+
verify_peer: bool = True,
|
91
|
+
timeout: float | int | None = None,
|
92
|
+
duration_seconds: int | None = None,
|
93
|
+
**kwargs,
|
94
|
+
):
|
95
|
+
# initializing BRSSession
|
96
|
+
super().__init__(refresh_method="iot-x509", **kwargs)
|
97
|
+
|
98
|
+
# initializing public attributes
|
99
|
+
self.endpoint = self._normalize_iot_credential_endpoint(
|
100
|
+
endpoint=endpoint
|
101
|
+
)
|
102
|
+
self.role_alias = role_alias
|
103
|
+
self.certificate = certificate
|
104
|
+
self.thing_name = thing_name
|
105
|
+
self.private_key = private_key
|
106
|
+
self.pkcs11 = pkcs11
|
107
|
+
self.ca = ca
|
108
|
+
self.verify_peer = verify_peer
|
109
|
+
self.timeout = 10.0 if timeout is None else timeout
|
110
|
+
self.duration_seconds = duration_seconds
|
111
|
+
|
112
|
+
# loading X.509 certificate if presented as a string, which
|
113
|
+
# is presumed to be the file path.
|
114
|
+
# if presented as bytes then self.certificate is presumed to be
|
115
|
+
# the actual certificate itself
|
116
|
+
if self.certificate and isinstance(self.certificate, str):
|
117
|
+
self.certificate = (
|
118
|
+
Path(self.certificate).expanduser().resolve().read_bytes()
|
119
|
+
)
|
120
|
+
|
121
|
+
# either private_key or pkcs11 must be provided
|
122
|
+
if self.private_key is None and self.pkcs11 is None:
|
123
|
+
raise BRSError(
|
124
|
+
"Either 'private_key' or 'pkcs11' must be provided."
|
125
|
+
)
|
126
|
+
|
127
|
+
# . . . but both cannot be provided!
|
128
|
+
if self.private_key is not None and self.pkcs11 is not None:
|
129
|
+
raise BRSError(
|
130
|
+
"Only one of 'private_key' or 'pkcs11' can be provided."
|
131
|
+
)
|
132
|
+
|
133
|
+
# if the provided private_key is bytes then it's presumed to be
|
134
|
+
# the actual private key. but if it's string then it's presumed
|
135
|
+
# to be the file path
|
136
|
+
if self.private_key and isinstance(self.private_key, str):
|
137
|
+
self.private_key = (
|
138
|
+
Path(self.private_key).expanduser().resolve().read_bytes()
|
139
|
+
)
|
140
|
+
|
141
|
+
# verifying PKCS#11 dict
|
142
|
+
if self.pkcs11:
|
143
|
+
self.pkcs11 = self._validate_pkcs11(pkcs11=self.pkcs11)
|
144
|
+
|
145
|
+
# ca is like many other attributes in that str implies file location
|
146
|
+
if self.ca and isinstance(self.ca, str):
|
147
|
+
self.ca = Path(self.ca).expanduser().resolve().read_bytes()
|
148
|
+
|
149
|
+
def _get_credentials(self) -> TemporaryCredentials:
|
150
|
+
url = urlparse(
|
151
|
+
f"https://{self.endpoint}/role-aliases/{self.role_alias}"
|
152
|
+
"/credentials"
|
153
|
+
)
|
154
|
+
request = HttpRequest("GET", url.path)
|
155
|
+
request.headers.add("host", str(url.hostname))
|
156
|
+
if self.thing_name:
|
157
|
+
request.headers.add("x-amzn-iot-thingname", self.thing_name)
|
158
|
+
if self.duration_seconds:
|
159
|
+
request.headers.add(
|
160
|
+
"x-amzn-iot-credential-duration-seconds",
|
161
|
+
str(self.duration_seconds),
|
162
|
+
)
|
163
|
+
response = AWSCRTResponse()
|
164
|
+
port = 443 if not url.port else url.port
|
165
|
+
connection = (
|
166
|
+
self._mtls_client_connection(url=url, port=port)
|
167
|
+
if not self.pkcs11
|
168
|
+
else self._mtls_pkcs11_client_connection(url=url, port=port)
|
169
|
+
)
|
170
|
+
|
171
|
+
try:
|
172
|
+
stream = connection.request(
|
173
|
+
request, response.on_response, response.on_body
|
174
|
+
)
|
175
|
+
stream.activate()
|
176
|
+
stream.completion_future.result(float(self.timeout))
|
177
|
+
finally:
|
178
|
+
try:
|
179
|
+
connection.close()
|
180
|
+
except Exception:
|
181
|
+
...
|
182
|
+
|
183
|
+
if response.status_code == 200:
|
184
|
+
credentials = json.loads(response.body.decode("utf-8"))[
|
185
|
+
"credentials"
|
186
|
+
]
|
187
|
+
return {
|
188
|
+
"access_key": credentials["accessKeyId"],
|
189
|
+
"secret_key": credentials["secretAccessKey"],
|
190
|
+
"token": credentials["sessionToken"],
|
191
|
+
"expiry_time": credentials["expiration"],
|
192
|
+
}
|
193
|
+
else:
|
194
|
+
raise BRSError(
|
195
|
+
"Error getting credentials: "
|
196
|
+
f"{json.loads(response.body.decode())}"
|
197
|
+
)
|
198
|
+
|
199
|
+
def _mtls_client_connection(
|
200
|
+
self, url: ParseResult, port: int
|
201
|
+
) -> HttpClientConnection:
|
202
|
+
event_loop_group: EventLoopGroup = EventLoopGroup()
|
203
|
+
host_resolver: DefaultHostResolver = DefaultHostResolver(
|
204
|
+
event_loop_group
|
205
|
+
)
|
206
|
+
bootstrap: ClientBootstrap = ClientBootstrap(
|
207
|
+
event_loop_group, host_resolver
|
208
|
+
)
|
209
|
+
tls_ctx_opt = TlsContextOptions.create_client_with_mtls(
|
210
|
+
cert_buffer=self.certificate, key_buffer=self.private_key
|
211
|
+
)
|
212
|
+
|
213
|
+
if self.ca:
|
214
|
+
tls_ctx_opt.override_default_trust_store(self.ca)
|
215
|
+
|
216
|
+
tls_ctx_opt.verify_peer = self.verify_peer
|
217
|
+
tls_ctx = ClientTlsContext(tls_ctx_opt)
|
218
|
+
tls_conn_opt: TlsConnectionOptions = cast(
|
219
|
+
TlsConnectionOptions, tls_ctx.new_connection_options()
|
220
|
+
)
|
221
|
+
tls_conn_opt.set_server_name(str(url.hostname))
|
222
|
+
|
223
|
+
try:
|
224
|
+
connection_future = HttpClientConnection.new(
|
225
|
+
host_name=str(url.hostname),
|
226
|
+
port=port,
|
227
|
+
bootstrap=bootstrap,
|
228
|
+
tls_connection_options=tls_conn_opt,
|
229
|
+
)
|
230
|
+
return connection_future.result(self.timeout)
|
231
|
+
except AwsCrtError as err:
|
232
|
+
raise BRSError(
|
233
|
+
"Error completing mTLS connection to endpoint "
|
234
|
+
f"'{url.hostname}'"
|
235
|
+
) from err
|
236
|
+
|
237
|
+
def _mtls_pkcs11_client_connection(
|
238
|
+
self, url: ParseResult, port: int
|
239
|
+
) -> HttpClientConnection:
|
240
|
+
event_loop_group: EventLoopGroup = EventLoopGroup()
|
241
|
+
host_resolver: DefaultHostResolver = DefaultHostResolver(
|
242
|
+
event_loop_group
|
243
|
+
)
|
244
|
+
bootstrap: ClientBootstrap = ClientBootstrap(
|
245
|
+
event_loop_group, host_resolver
|
246
|
+
)
|
247
|
+
|
248
|
+
if not self.pkcs11:
|
249
|
+
raise BRSError(
|
250
|
+
"Attempting to establish mTLS connection using PKCS#11"
|
251
|
+
"but 'pkcs11' parameter is 'None'!"
|
252
|
+
)
|
253
|
+
|
254
|
+
tls_ctx_opt = TlsContextOptions.create_client_with_mtls_pkcs11(
|
255
|
+
pkcs11_lib=Pkcs11Lib(file=self.pkcs11["pkcs11_lib"]),
|
256
|
+
user_pin=self.pkcs11["user_pin"],
|
257
|
+
slot_id=self.pkcs11["slot_id"],
|
258
|
+
token_label=self.pkcs11["token_label"],
|
259
|
+
private_key_label=self.pkcs11["private_key_label"],
|
260
|
+
cert_file_contents=self.certificate,
|
261
|
+
)
|
262
|
+
|
263
|
+
if self.ca:
|
264
|
+
tls_ctx_opt.override_default_trust_store(self.ca)
|
265
|
+
|
266
|
+
tls_ctx_opt.verify_peer = self.verify_peer
|
267
|
+
tls_ctx = ClientTlsContext(tls_ctx_opt)
|
268
|
+
tls_conn_opt: TlsConnectionOptions = cast(
|
269
|
+
TlsConnectionOptions, tls_ctx.new_connection_options()
|
270
|
+
)
|
271
|
+
tls_conn_opt.set_server_name(str(url.hostname))
|
272
|
+
|
273
|
+
try:
|
274
|
+
connection_future = HttpClientConnection.new(
|
275
|
+
host_name=str(url.hostname),
|
276
|
+
port=port,
|
277
|
+
bootstrap=bootstrap,
|
278
|
+
tls_connection_options=tls_conn_opt,
|
279
|
+
)
|
280
|
+
return connection_future.result(self.timeout)
|
281
|
+
except AwsCrtError as err:
|
282
|
+
raise BRSError("Error completing mTLS connection.") from err
|
283
|
+
|
284
|
+
def get_identity(self) -> Identity:
|
285
|
+
"""Returns metadata about the current caller identity.
|
286
|
+
|
287
|
+
Returns
|
288
|
+
-------
|
289
|
+
Identity
|
290
|
+
Dict containing information about the current calleridentity.
|
291
|
+
"""
|
292
|
+
|
293
|
+
return self.client("sts").get_caller_identity()
|
294
|
+
|
295
|
+
@staticmethod
|
296
|
+
def _normalize_iot_credential_endpoint(endpoint: str) -> str:
|
297
|
+
if ".credentials.iot." in endpoint:
|
298
|
+
return endpoint
|
299
|
+
|
300
|
+
if ".iot." in endpoint and "-ats." in endpoint:
|
301
|
+
logged_data_endpoint = re.sub(r"^[^. -]+", "***", endpoint)
|
302
|
+
logged_credential_endpoint = re.sub(
|
303
|
+
r"^[^. -]+",
|
304
|
+
"***",
|
305
|
+
(endpoint := endpoint.replace("-ats.iot", ".credentials.iot")),
|
306
|
+
)
|
307
|
+
BRSWarning.warn(
|
308
|
+
"The 'endpoint' parameter you provided represents the data "
|
309
|
+
"endpoint for IoT not the credentials endpoint! The endpoint "
|
310
|
+
"you provided was therefore modified from "
|
311
|
+
f"'{logged_data_endpoint}' -> '{logged_credential_endpoint}'"
|
312
|
+
)
|
313
|
+
return endpoint
|
314
|
+
|
315
|
+
raise BRSError(
|
316
|
+
"Invalid IoT endpoint provided for credentials provider. "
|
317
|
+
"Expected '<id>.credentials.iot.<region>.amazonaws.com'"
|
318
|
+
)
|
319
|
+
|
320
|
+
@staticmethod
|
321
|
+
def _validate_pkcs11(pkcs11: PKCS11) -> PKCS11:
|
322
|
+
if "pkcs11_lib" not in pkcs11:
|
323
|
+
raise BRSError(
|
324
|
+
"PKCS#11 library path must be provided as 'pkcs11_lib'"
|
325
|
+
" in 'pkcs11'."
|
326
|
+
)
|
327
|
+
elif not Path(pkcs11["pkcs11_lib"]).expanduser().resolve().is_file():
|
328
|
+
raise BRSError(
|
329
|
+
f"'{pkcs11['pkcs11_lib']}' is not a valid file path for "
|
330
|
+
"'pkcs11_lib' in 'pkcs11'."
|
331
|
+
)
|
332
|
+
pkcs11.setdefault("user_pin", None)
|
333
|
+
pkcs11.setdefault("slot_id", None)
|
334
|
+
pkcs11.setdefault("token_label", None)
|
335
|
+
pkcs11.setdefault("private_key_label", None)
|
336
|
+
return pkcs11
|
@@ -45,7 +45,7 @@ class STSRefreshableSession(BaseRefreshableSession, registry_key="sts"):
|
|
45
45
|
**kwargs,
|
46
46
|
):
|
47
47
|
if "refresh_method" in kwargs:
|
48
|
-
BRSWarning(
|
48
|
+
BRSWarning.warn(
|
49
49
|
"'refresh_method' cannot be set manually. "
|
50
50
|
"Reverting to 'sts-assume-role'."
|
51
51
|
)
|
@@ -60,7 +60,7 @@ class STSRefreshableSession(BaseRefreshableSession, registry_key="sts"):
|
|
60
60
|
if sts_client_kwargs is not None:
|
61
61
|
# overwriting 'service_name' if if appears in sts_client_kwargs
|
62
62
|
if "service_name" in sts_client_kwargs:
|
63
|
-
BRSWarning(
|
63
|
+
BRSWarning.warn(
|
64
64
|
"'sts_client_kwargs' cannot contain values for "
|
65
65
|
"'service_name'. Reverting to service_name = 'sts'."
|
66
66
|
)
|
boto3_refresh_session/session.py
CHANGED
@@ -36,8 +36,8 @@ class RefreshableSession:
|
|
36
36
|
See Also
|
37
37
|
--------
|
38
38
|
boto3_refresh_session.methods.custom.CustomRefreshableSession
|
39
|
+
boto3_refresh_session.methods.iot.IOTX509RefreshableSession
|
39
40
|
boto3_refresh_session.methods.sts.STSRefreshableSession
|
40
|
-
boto3_refresh_session.methods.ecs.ECSRefreshableSession
|
41
41
|
"""
|
42
42
|
|
43
43
|
def __new__(
|
@@ -60,7 +60,7 @@ class RefreshableSession:
|
|
60
60
|
-------
|
61
61
|
list[str]
|
62
62
|
A list of all currently available credential refresh methods,
|
63
|
-
e.g. 'sts', '
|
63
|
+
e.g. 'sts', 'custom'.
|
64
64
|
"""
|
65
65
|
|
66
66
|
args = list(get_args(Method))
|
@@ -1,4 +1,6 @@
|
|
1
1
|
__all__ = [
|
2
|
+
"AWSCRTResponse",
|
3
|
+
"BaseIoTRefreshableSession",
|
2
4
|
"BaseRefreshableSession",
|
3
5
|
"BRSSession",
|
4
6
|
"CredentialProvider",
|
@@ -10,6 +12,7 @@ from abc import ABC, abstractmethod
|
|
10
12
|
from functools import wraps
|
11
13
|
from typing import Any, Callable, ClassVar, Generic, TypeVar, cast
|
12
14
|
|
15
|
+
from awscrt.http import HttpHeaders
|
13
16
|
from boto3.session import Session
|
14
17
|
from botocore.credentials import (
|
15
18
|
DeferredRefreshableCredentials,
|
@@ -19,6 +22,7 @@ from botocore.credentials import (
|
|
19
22
|
from ..exceptions import BRSWarning
|
20
23
|
from .typing import (
|
21
24
|
Identity,
|
25
|
+
IoTAuthenticationMethod,
|
22
26
|
Method,
|
23
27
|
RefreshableTemporaryCredentials,
|
24
28
|
RefreshMethod,
|
@@ -46,7 +50,9 @@ class Registry(Generic[RegistryKey]):
|
|
46
50
|
super().__init_subclass__(**kwargs)
|
47
51
|
|
48
52
|
if registry_key in cls.registry:
|
49
|
-
BRSWarning(
|
53
|
+
BRSWarning.warn(
|
54
|
+
f"{registry_key!r} already registered. Overwriting."
|
55
|
+
)
|
50
56
|
|
51
57
|
if "sentinel" not in registry_key:
|
52
58
|
cls.registry[registry_key] = cls
|
@@ -202,3 +208,35 @@ class BaseRefreshableSession(
|
|
202
208
|
|
203
209
|
def __init__(self, **kwargs):
|
204
210
|
super().__init__(**kwargs)
|
211
|
+
|
212
|
+
|
213
|
+
class BaseIoTRefreshableSession(
|
214
|
+
Registry[IoTAuthenticationMethod],
|
215
|
+
CredentialProvider,
|
216
|
+
BRSSession,
|
217
|
+
registry_key="__iot_sentinel__",
|
218
|
+
):
|
219
|
+
def __init__(self, **kwargs):
|
220
|
+
super().__init__(**kwargs)
|
221
|
+
|
222
|
+
|
223
|
+
class AWSCRTResponse:
|
224
|
+
"""Lightweight response collector for awscrt HTTP."""
|
225
|
+
|
226
|
+
def __init__(self):
|
227
|
+
"""Initialize to default for when callbacks are called."""
|
228
|
+
|
229
|
+
self.status_code = None
|
230
|
+
self.headers = None
|
231
|
+
self.body = bytearray()
|
232
|
+
|
233
|
+
def on_response(self, http_stream, status_code, headers, **kwargs):
|
234
|
+
"""Process awscrt.io response."""
|
235
|
+
|
236
|
+
self.status_code = status_code
|
237
|
+
self.headers = HttpHeaders(headers)
|
238
|
+
|
239
|
+
def on_body(self, http_stream, chunk, **kwargs):
|
240
|
+
"""Process awscrt.io body."""
|
241
|
+
|
242
|
+
self.body.extend(chunk)
|
@@ -33,22 +33,23 @@ except ImportError:
|
|
33
33
|
from typing_extensions import NotRequired
|
34
34
|
|
35
35
|
#: Type alias for all currently available IoT authentication methods.
|
36
|
-
IoTAuthenticationMethod = Literal["
|
36
|
+
IoTAuthenticationMethod = Literal["x509", "__iot_sentinel__"]
|
37
37
|
|
38
38
|
#: Type alias for all currently available credential refresh methods.
|
39
39
|
Method = Literal[
|
40
|
-
"sts",
|
41
|
-
"ecs",
|
42
40
|
"custom",
|
41
|
+
"iot",
|
42
|
+
"sts",
|
43
43
|
"__sentinel__",
|
44
|
-
|
44
|
+
"__iot_sentinel__",
|
45
|
+
]
|
45
46
|
|
46
47
|
#: Type alias for all refresh method names.
|
47
48
|
RefreshMethod = Literal[
|
48
|
-
"sts-assume-role",
|
49
|
-
"ecs-container-metadata",
|
50
49
|
"custom",
|
51
|
-
|
50
|
+
"iot-x509",
|
51
|
+
"sts-assume-role",
|
52
|
+
]
|
52
53
|
|
53
54
|
#: Type alias for all currently registered credential refresh methods.
|
54
55
|
RegistryKey = TypeVar("RegistryKey", bound=str)
|
@@ -138,7 +139,7 @@ class STSClientParams(TypedDict):
|
|
138
139
|
|
139
140
|
|
140
141
|
class PKCS11(TypedDict):
|
141
|
-
|
142
|
+
pkcs11_lib: str
|
142
143
|
user_pin: NotRequired[str]
|
143
144
|
slot_id: NotRequired[int]
|
144
145
|
token_label: NotRequired[str | None]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: boto3-refresh-session
|
3
|
-
Version:
|
3
|
+
Version: 5.0.0
|
4
4
|
Summary: A simple Python package for refreshing the temporary security credentials in a boto3.session.Session object automatically.
|
5
5
|
License: MIT
|
6
|
-
Keywords: boto3,botocore,aws,sts,
|
6
|
+
Keywords: boto3,botocore,aws,sts,credentials,token,refresh,iot,x509
|
7
7
|
Author: Mike Letts
|
8
8
|
Author-email: lettsmt@gmail.com
|
9
9
|
Maintainer: Michael Letts
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
18
|
+
Requires-Dist: awscrt
|
18
19
|
Requires-Dist: boto3
|
19
20
|
Requires-Dist: botocore
|
20
21
|
Requires-Dist: requests
|
@@ -120,13 +121,14 @@ Description-Content-Type: text/markdown
|
|
120
121
|
## 😛 Features
|
121
122
|
|
122
123
|
- Drop-in replacement for `boto3.session.Session`
|
123
|
-
- Supports automatic credential refresh
|
124
|
-
- STS
|
125
|
-
-
|
126
|
-
-
|
124
|
+
- Supports automatic credential refresh for:
|
125
|
+
- **STS**
|
126
|
+
- **IoT Core**
|
127
|
+
- X.509 certificates w/ role aliases over mTLS (PEM files and PKCS#11)
|
128
|
+
- Custom authentication methods
|
127
129
|
- Natively supports all parameters supported by `boto3.session.Session`
|
128
130
|
- [Tested](https://github.com/michaelthomasletts/boto3-refresh-session/tree/main/tests), [documented](https://michaelthomasletts.github.io/boto3-refresh-session/index.html), and [published to PyPI](https://pypi.org/project/boto3-refresh-session/)
|
129
|
-
- Future releases will include support for IoT (coming soon)
|
131
|
+
- Future releases will include support for IoT (coming soon)
|
130
132
|
|
131
133
|
## ⚠️ Important Updates
|
132
134
|
|
@@ -138,6 +140,14 @@ Advanced users, however, particularly those using low-level objects such as `Bas
|
|
138
140
|
|
139
141
|
Please review [this PR](https://github.com/michaelthomasletts/boto3-refresh-session/pull/75) for additional details.
|
140
142
|
|
143
|
+
#### ✂️ v4.0.0
|
144
|
+
|
145
|
+
The `ecs` module has been dropped. For additional details and rationale, please review [this PR](https://github.com/michaelthomasletts/boto3-refresh-session/pull/78).
|
146
|
+
|
147
|
+
#### 😛 v5.0.0
|
148
|
+
|
149
|
+
Support for IoT Core via X.509 certificate-based authentication (over HTTPS) is now available!
|
150
|
+
|
141
151
|
#### ☎️ Delayed Responses
|
142
152
|
|
143
153
|
I am currently grappling with a very serious medical condition. Accordingly, expect delayed responses to issues and requests until my health stabilizes.
|
@@ -171,7 +181,7 @@ pip install boto3-refresh-session
|
|
171
181
|
|
172
182
|
To use the `boto3.client` or `boto3.resource` interface, but with the benefits of `boto3-refresh-session`, you have a few options!
|
173
183
|
|
174
|
-
In the following examples, let's assume you want to use STS for retrieving temporary credentials for the sake of simplicity. Let's also focus specifically on `client`. Switching to `resource` follows the same exact idioms as below, except that `client` must be switched to `resource` in the pseudo-code, obviously. If you are not sure how to use `RefreshableSession` for STS (or
|
184
|
+
In the following examples, let's assume you want to use STS for retrieving temporary credentials for the sake of simplicity. Let's also focus specifically on `client`. Switching to `resource` follows the same exact idioms as below, except that `client` must be switched to `resource` in the pseudo-code, obviously. If you are not sure how to use `RefreshableSession` for STS (or custom auth flows) then check the usage instructions in the following sections!
|
175
185
|
|
176
186
|
##### `RefreshableSession.client` (Recommended)
|
177
187
|
|
@@ -269,24 +279,6 @@ pip install boto3-refresh-session
|
|
269
279
|
|
270
280
|
</details>
|
271
281
|
|
272
|
-
<details>
|
273
|
-
<summary><strong>ECS (click to expand)</strong></summary>
|
274
|
-
|
275
|
-
### ECS
|
276
|
-
|
277
|
-
You can use boto3-refresh-session in an ECS container to automatically refresh temporary security credentials. For additional information on the exact parameters that `RefreshableSession` takes for ECS, [check this documentation](https://github.com/michaelthomasletts/boto3-refresh-session/blob/main/boto3_refresh_session/methods/ecs.py).
|
278
|
-
|
279
|
-
```python
|
280
|
-
session = RefreshableSession(
|
281
|
-
method="ecs",
|
282
|
-
region_name=region_name,
|
283
|
-
profile_name=profile_name,
|
284
|
-
...
|
285
|
-
)
|
286
|
-
```
|
287
|
-
|
288
|
-
</details>
|
289
|
-
|
290
282
|
<details>
|
291
283
|
<summary><strong>Custom Authentication Flows (click to expand)</strong></summary>
|
292
284
|
|
@@ -317,3 +309,56 @@ pip install boto3-refresh-session
|
|
317
309
|
```
|
318
310
|
|
319
311
|
</details>
|
312
|
+
|
313
|
+
<details>
|
314
|
+
<summary><strong>IoT Core X.509 (click to expand)</strong></summary>
|
315
|
+
|
316
|
+
### IoT Core X.509
|
317
|
+
|
318
|
+
AWS IoT Core can vend temporary AWS credentials through the **credentials provider** when you connect with an X.509 certificate and a **role alias**. `boto3-refresh-session` makes this flow seamless by automatically refreshing credentials over **mTLS**.
|
319
|
+
|
320
|
+
For additional information on the exact parameters that `IOTX509RefreshableSession` takes, [check this documentation](https://github.com/michaelthomasletts/boto3-refresh-session/blob/main/boto3_refresh_session/methods/iot/x509.py).
|
321
|
+
|
322
|
+
### PEM file
|
323
|
+
|
324
|
+
```python
|
325
|
+
import boto3_refresh_session as brs
|
326
|
+
|
327
|
+
# PEM certificate + private key example
|
328
|
+
session = brs.RefreshableSession(
|
329
|
+
method="iot",
|
330
|
+
endpoint="<your-credentials-endpoint>.credentials.iot.<region>.amazonaws.com",
|
331
|
+
role_alias="<your-role-alias>",
|
332
|
+
certificate="/path/to/certificate.pem",
|
333
|
+
private_key="/path/to/private-key.pem",
|
334
|
+
thing_name="<your-thing-name>", # optional, if used in policies
|
335
|
+
duration_seconds=3600, # optional, capped by role alias
|
336
|
+
region_name="us-east-1",
|
337
|
+
)
|
338
|
+
|
339
|
+
# Now you can use the session like any boto3 session
|
340
|
+
s3 = session.client("s3")
|
341
|
+
print(s3.list_buckets())
|
342
|
+
```
|
343
|
+
|
344
|
+
### PKCS#11
|
345
|
+
|
346
|
+
```python
|
347
|
+
session = brs.RefreshableSession(
|
348
|
+
method="iot",
|
349
|
+
endpoint="<your-credentials-endpoint>.credentials.iot.<region>.amazonaws.com",
|
350
|
+
role_alias="<your-role-alias>",
|
351
|
+
certificate="/path/to/certificate.pem",
|
352
|
+
pkcs11={
|
353
|
+
"pkcs11_lib": "/usr/local/lib/softhsm/libsofthsm2.so",
|
354
|
+
"user_pin": "1234",
|
355
|
+
"slot_id": 0,
|
356
|
+
"token_label": "MyToken",
|
357
|
+
"private_key_label": "MyKey",
|
358
|
+
},
|
359
|
+
thing_name="<your-thing-name>",
|
360
|
+
region_name="us-east-1",
|
361
|
+
)
|
362
|
+
```
|
363
|
+
|
364
|
+
</details>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
boto3_refresh_session/__init__.py,sha256=XR8slNXWaySOCG-_BnO5yi6d8276TH34EzGUTS347vM,415
|
2
|
+
boto3_refresh_session/exceptions.py,sha256=QS5_xy3hNrfkdT_wKPZWH8WqSbFYCKPcK8DomGYIvcU,1218
|
3
|
+
boto3_refresh_session/methods/__init__.py,sha256=FpwWixSVpy_6pUe1u4fXmjO-_fDH--qTk_xrMnBCHxU,193
|
4
|
+
boto3_refresh_session/methods/custom.py,sha256=MLdUMU9s6NQoJWBKQ5Fsxeyxb_Xrm9V59pVX22M8fyI,4178
|
5
|
+
boto3_refresh_session/methods/iot/__init__.py,sha256=wIYp7HFZ_Q8XEHwWmpKjDNXxBm29C0RisP_9GSVwzZI,147
|
6
|
+
boto3_refresh_session/methods/iot/core.py,sha256=xtvbC23h6fw06lRZWN4r7TlnUEf3t9T7-zSPGCSlSLI,1151
|
7
|
+
boto3_refresh_session/methods/iot/x509.py,sha256=QvHXJKRkRuF5TOUOEH4rTN8lfm_rRnp-flLoolMaxbw,12368
|
8
|
+
boto3_refresh_session/methods/sts.py,sha256=NGqJFJNLjG9Mve7o19tb_i6lvgQW1HoALIqF6lJNV9A,3336
|
9
|
+
boto3_refresh_session/session.py,sha256=UM_dWHSo0Wn8gLN99zg36SRVb-Yy_to1wk8UgZEuQZA,2086
|
10
|
+
boto3_refresh_session/utils/__init__.py,sha256=6F2ErbgBT2ZmZwFF3OzvQEd1Vh4XM3kaL6YGMTrcrkQ,156
|
11
|
+
boto3_refresh_session/utils/internal.py,sha256=HbuIzT0pC8QS4pgNj3M7POGaW-OEz2l3ESfYI1Qouuo,7072
|
12
|
+
boto3_refresh_session/utils/typing.py,sha256=AqPey1N8nNUU2BwQYIIz-xGrfgjyNUzDt8MK0eB5DSQ,3429
|
13
|
+
boto3_refresh_session-5.0.0.dist-info/LICENSE,sha256=I3ZYTXAjbIly6bm6J-TvFTuuHwTKws4h89QaY5c5HiY,1067
|
14
|
+
boto3_refresh_session-5.0.0.dist-info/METADATA,sha256=xTwVXf2-RzpcMWZH9SvZTvrVM2stHCp6ZWRYrX5Q7p4,14151
|
15
|
+
boto3_refresh_session-5.0.0.dist-info/NOTICE,sha256=1s8r33qbl1z0YvPB942iWgvbkP94P_e8AnROr1qXXuw,939
|
16
|
+
boto3_refresh_session-5.0.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
17
|
+
boto3_refresh_session-5.0.0.dist-info/RECORD,,
|
@@ -1,115 +0,0 @@
|
|
1
|
-
__all__ = ["ECSRefreshableSession"]
|
2
|
-
|
3
|
-
import os
|
4
|
-
|
5
|
-
import requests
|
6
|
-
|
7
|
-
from ..exceptions import BRSError, BRSWarning
|
8
|
-
from ..utils import (
|
9
|
-
BaseRefreshableSession,
|
10
|
-
Identity,
|
11
|
-
TemporaryCredentials,
|
12
|
-
refreshable_session,
|
13
|
-
)
|
14
|
-
|
15
|
-
_ECS_CREDENTIALS_RELATIVE_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
16
|
-
_ECS_CREDENTIALS_FULL_URI = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
17
|
-
_ECS_AUTHORIZATION_TOKEN = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
18
|
-
_DEFAULT_ENDPOINT_BASE = "http://169.254.170.2"
|
19
|
-
|
20
|
-
|
21
|
-
@refreshable_session
|
22
|
-
class ECSRefreshableSession(BaseRefreshableSession, registry_key="ecs"):
|
23
|
-
"""A boto3 session that automatically refreshes temporary AWS credentials
|
24
|
-
from the ECS container credentials metadata endpoint.
|
25
|
-
|
26
|
-
Parameters
|
27
|
-
----------
|
28
|
-
defer_refresh : bool, optional
|
29
|
-
If ``True`` then temporary credentials are not automatically
|
30
|
-
refreshed until they are explicitly needed. If ``False`` then
|
31
|
-
temporary credentials refresh immediately upon expiration. It
|
32
|
-
is highly recommended that you use ``True``. Default is ``True``.
|
33
|
-
|
34
|
-
Other Parameters
|
35
|
-
----------------
|
36
|
-
kwargs : dict
|
37
|
-
Optional keyword arguments passed to :class:`boto3.session.Session`.
|
38
|
-
"""
|
39
|
-
|
40
|
-
def __init__(self, **kwargs):
|
41
|
-
if "refresh_method" in kwargs:
|
42
|
-
BRSWarning(
|
43
|
-
"'refresh_method' cannot be set manually. "
|
44
|
-
"Reverting to 'ecs-container-metadata'."
|
45
|
-
)
|
46
|
-
del kwargs["refresh_method"]
|
47
|
-
|
48
|
-
# initializing BRSSession
|
49
|
-
super().__init__(refresh_method="ecs-container-metadata", **kwargs)
|
50
|
-
|
51
|
-
# initializing various other attributes
|
52
|
-
self._endpoint = self._resolve_endpoint()
|
53
|
-
self._headers = self._build_headers()
|
54
|
-
self._http = self._init_http_session()
|
55
|
-
|
56
|
-
def _resolve_endpoint(self) -> str:
|
57
|
-
uri = os.environ.get(_ECS_CREDENTIALS_FULL_URI) or os.environ.get(
|
58
|
-
_ECS_CREDENTIALS_RELATIVE_URI
|
59
|
-
)
|
60
|
-
if not uri:
|
61
|
-
raise BRSError(
|
62
|
-
"Neither AWS_CONTAINER_CREDENTIALS_FULL_URI nor "
|
63
|
-
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set. "
|
64
|
-
"Are you running inside an ECS container?"
|
65
|
-
)
|
66
|
-
if uri.startswith("http://") or uri.startswith("https://"):
|
67
|
-
return uri
|
68
|
-
return f"{_DEFAULT_ENDPOINT_BASE}{uri}"
|
69
|
-
|
70
|
-
def _build_headers(self) -> dict[str, str]:
|
71
|
-
token = os.environ.get(_ECS_AUTHORIZATION_TOKEN)
|
72
|
-
if token:
|
73
|
-
return {"Authorization": f"Bearer {token}"}
|
74
|
-
return {}
|
75
|
-
|
76
|
-
def _init_http_session(self) -> requests.Session:
|
77
|
-
session = requests.Session()
|
78
|
-
session.headers.update(self._headers)
|
79
|
-
return session
|
80
|
-
|
81
|
-
def _get_credentials(self) -> TemporaryCredentials:
|
82
|
-
try:
|
83
|
-
response = self._http.get(self._endpoint, timeout=3)
|
84
|
-
response.raise_for_status()
|
85
|
-
except requests.RequestException as exc:
|
86
|
-
raise BRSError(
|
87
|
-
f"Failed to retrieve ECS credentials from {self._endpoint}"
|
88
|
-
) from exc
|
89
|
-
|
90
|
-
credentials = response.json()
|
91
|
-
required = {
|
92
|
-
"AccessKeyId",
|
93
|
-
"SecretAccessKey",
|
94
|
-
"SessionToken",
|
95
|
-
"Expiration",
|
96
|
-
}
|
97
|
-
if not required.issubset(credentials):
|
98
|
-
raise BRSError(f"Incomplete credentials received: {credentials}")
|
99
|
-
return {
|
100
|
-
"access_key": credentials.get("AccessKeyId"),
|
101
|
-
"secret_key": credentials.get("SecretAccessKey"),
|
102
|
-
"token": credentials.get("SessionToken"),
|
103
|
-
"expiry_time": credentials.get("Expiration"), # already ISO8601
|
104
|
-
}
|
105
|
-
|
106
|
-
def get_identity(self) -> Identity:
|
107
|
-
"""Returns metadata about ECS.
|
108
|
-
|
109
|
-
Returns
|
110
|
-
-------
|
111
|
-
Identity
|
112
|
-
Dict containing metadata about ECS.
|
113
|
-
"""
|
114
|
-
|
115
|
-
return {"method": "ecs", "source": "ecs-container-metadata"}
|
@@ -1,57 +0,0 @@
|
|
1
|
-
__all__ = ["IoTCertificateRefreshableSession"]
|
2
|
-
|
3
|
-
from pathlib import Path
|
4
|
-
from typing import Any
|
5
|
-
|
6
|
-
from ...exceptions import BRSError
|
7
|
-
from ...utils import (
|
8
|
-
Identity, PKCS11, TemporaryCredentials, refreshable_session
|
9
|
-
)
|
10
|
-
from .core import BaseIoTRefreshableSession
|
11
|
-
|
12
|
-
|
13
|
-
@refreshable_session
|
14
|
-
class IoTCertificateRefreshableSession(
|
15
|
-
BaseIoTRefreshableSession, registry_key="certificate"
|
16
|
-
):
|
17
|
-
def __init__(
|
18
|
-
self,
|
19
|
-
endpoint: str,
|
20
|
-
role_alias: str,
|
21
|
-
thing_name: str,
|
22
|
-
certificate: str | bytes,
|
23
|
-
private_key: str | bytes | None = None,
|
24
|
-
pkcs11: PKCS11 | None = None,
|
25
|
-
ca: bytes | None = None,
|
26
|
-
verify_peer: bool = True,
|
27
|
-
):
|
28
|
-
self.endpoint = endpoint
|
29
|
-
self.role_alias = role_alias
|
30
|
-
self.thing_name = thing_name
|
31
|
-
self.certificate = certificate
|
32
|
-
self.private_key = private_key
|
33
|
-
self.pkcs11 = pkcs11
|
34
|
-
self.ca = ca
|
35
|
-
self.verify_peer = verify_peer
|
36
|
-
|
37
|
-
if self.certificate and isinstance(self.certificate, str):
|
38
|
-
with open(Path(self.certificate), "rb") as cert_pem_file:
|
39
|
-
self.certificate = cert_pem_file.read()
|
40
|
-
|
41
|
-
if self.private_key is None and self.pkcs11 is None:
|
42
|
-
raise BRSError(
|
43
|
-
"Either 'private_key' or 'pkcs11' must be provided."
|
44
|
-
)
|
45
|
-
|
46
|
-
if self.private_key is not None and self.pkcs11 is not None:
|
47
|
-
raise BRSError(
|
48
|
-
"Only one of 'private_key' or 'pkcs11' can be provided."
|
49
|
-
)
|
50
|
-
|
51
|
-
if self.private_key and isinstance(self.private_key, str):
|
52
|
-
with open(Path(self.private_key), "rb") as private_key_pem_file:
|
53
|
-
self.private_key = private_key_pem_file.read()
|
54
|
-
|
55
|
-
def _get_credentials(self) -> TemporaryCredentials: ...
|
56
|
-
|
57
|
-
def get_identity(self) -> Identity: ...
|
@@ -1,17 +0,0 @@
|
|
1
|
-
__all__ = ["IoTCognitoRefreshableSession"]
|
2
|
-
|
3
|
-
from typing import Any
|
4
|
-
|
5
|
-
from ...utils import Identity, TemporaryCredentials, refreshable_session
|
6
|
-
from .core import BaseIoTRefreshableSession
|
7
|
-
|
8
|
-
|
9
|
-
@refreshable_session
|
10
|
-
class IoTCognitoRefreshableSession(
|
11
|
-
BaseIoTRefreshableSession, registry_key="cognito"
|
12
|
-
):
|
13
|
-
def __init__(self): ...
|
14
|
-
|
15
|
-
def _get_credentials(self) -> TemporaryCredentials: ...
|
16
|
-
|
17
|
-
def get_identity(self) -> Identity: ...
|
@@ -1,19 +0,0 @@
|
|
1
|
-
boto3_refresh_session/__init__.py,sha256=L-9FGJIUsvwRFNlfJeKb_61NvZpT07xGdBAyV38DMhI,415
|
2
|
-
boto3_refresh_session/exceptions.py,sha256=DumBh6cDVU46eelSNt1CsG2uMSBekSbmhqWEaAWw130,1003
|
3
|
-
boto3_refresh_session/methods/__init__.py,sha256=zpVBJIR4P-l4pjE9kMnLGffehPVawY1vLiX2CPcpV7w,352
|
4
|
-
boto3_refresh_session/methods/custom.py,sha256=j90Iv1DKdGgP1JNwQfpEhaJDBrB2AtDe8kqI2Mktwlg,4173
|
5
|
-
boto3_refresh_session/methods/ecs.py,sha256=dxDrNOu8xTFHciuwL7jLh5nB2QXWwQRRA1CoY7AuO5g,3893
|
6
|
-
boto3_refresh_session/methods/iot/__init__.typed,sha256=Z33nIB6oCsz9TZwikHfNHgY1SKxkSCdB5rwdPSUl3C4,135
|
7
|
-
boto3_refresh_session/methods/iot/certificate.typed,sha256=sFTa1rF7tebr48Bjw_YtVeOdVvazAHBJGGiM33tsFXI,1828
|
8
|
-
boto3_refresh_session/methods/iot/cognito.typed,sha256=wyBMWUkuhLt27JsKZIwtfylDdCavNexcEy16ZaDFjUY,435
|
9
|
-
boto3_refresh_session/methods/iot/core.typed,sha256=Q5WshxgIIOgAaqoU7n8wBKMe9eSzZ6H8db-q1gThHzk,1407
|
10
|
-
boto3_refresh_session/methods/sts.py,sha256=dzf68BE0f1nFsITOKOnygh-mTvBqThKkrW2eEc-wFKA,3326
|
11
|
-
boto3_refresh_session/session.py,sha256=8YAdanwnJUG622Cv9MNKg25uj9ZmMYzRL4xiqH1i0nk,2089
|
12
|
-
boto3_refresh_session/utils/__init__.py,sha256=6F2ErbgBT2ZmZwFF3OzvQEd1Vh4XM3kaL6YGMTrcrkQ,156
|
13
|
-
boto3_refresh_session/utils/internal.py,sha256=bpKTAF_xdBw1wJPHIG8aGRMiXkSkp7CI9et0U5o3qEI,6103
|
14
|
-
boto3_refresh_session/utils/typing.py,sha256=I4VJS1vkRwIRdJF08dZF1YgUed_anviz3hq4hLvPnLw,3537
|
15
|
-
boto3_refresh_session-3.0.3.dist-info/LICENSE,sha256=I3ZYTXAjbIly6bm6J-TvFTuuHwTKws4h89QaY5c5HiY,1067
|
16
|
-
boto3_refresh_session-3.0.3.dist-info/METADATA,sha256=91keQVV9MNrd7zoC8s5c4e59qzrU7vMB9plvJ1d4Sr8,12561
|
17
|
-
boto3_refresh_session-3.0.3.dist-info/NOTICE,sha256=1s8r33qbl1z0YvPB942iWgvbkP94P_e8AnROr1qXXuw,939
|
18
|
-
boto3_refresh_session-3.0.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
19
|
-
boto3_refresh_session-3.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|