boto3-refresh-session 1.3.22__py3-none-any.whl → 2.0.1__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 +4 -4
- boto3_refresh_session/exceptions.py +2 -2
- boto3_refresh_session/methods/__init__.py +0 -0
- boto3_refresh_session/{custom.py → methods/custom.py} +5 -4
- boto3_refresh_session/{ecs.py → methods/ecs.py} +5 -4
- boto3_refresh_session/methods/iot/__init__.typed +4 -0
- boto3_refresh_session/methods/iot/certificate.typed +54 -0
- boto3_refresh_session/methods/iot/cognito.typed +16 -0
- boto3_refresh_session/methods/iot/core.typed +50 -0
- boto3_refresh_session/{sts.py → methods/sts.py} +10 -10
- boto3_refresh_session/session.py +18 -99
- boto3_refresh_session/utils.py +212 -0
- {boto3_refresh_session-1.3.22.dist-info → boto3_refresh_session-2.0.1.dist-info}/METADATA +13 -2
- boto3_refresh_session-2.0.1.dist-info/RECORD +17 -0
- boto3_refresh_session-1.3.22.dist-info/RECORD +0 -11
- {boto3_refresh_session-1.3.22.dist-info → boto3_refresh_session-2.0.1.dist-info}/LICENSE +0 -0
- {boto3_refresh_session-1.3.22.dist-info → boto3_refresh_session-2.0.1.dist-info}/NOTICE +0 -0
- {boto3_refresh_session-1.3.22.dist-info → boto3_refresh_session-2.0.1.dist-info}/WHEEL +0 -0
@@ -1,10 +1,10 @@
|
|
1
|
-
from .custom import CustomRefreshableSession
|
2
|
-
from .ecs import ECSRefreshableSession
|
1
|
+
from .methods.custom import CustomRefreshableSession
|
2
|
+
from .methods.ecs import ECSRefreshableSession
|
3
|
+
from .methods.sts import STSRefreshableSession
|
3
4
|
from .session import RefreshableSession
|
4
|
-
from .sts import STSRefreshableSession
|
5
5
|
|
6
6
|
__all__ = ["RefreshableSession"]
|
7
|
-
__version__ = "
|
7
|
+
__version__ = "2.0.1"
|
8
8
|
__title__ = "boto3-refresh-session"
|
9
9
|
__author__ = "Mike Letts"
|
10
10
|
__maintainer__ = "Mike Letts"
|
@@ -15,7 +15,7 @@ class BRSError(Exception):
|
|
15
15
|
return self.message
|
16
16
|
|
17
17
|
def __repr__(self) -> str:
|
18
|
-
return f"{self.__class__.__name__}({
|
18
|
+
return f"{self.__class__.__name__}({self.message!r})"
|
19
19
|
|
20
20
|
|
21
21
|
class BRSWarning(UserWarning):
|
@@ -35,4 +35,4 @@ class BRSWarning(UserWarning):
|
|
35
35
|
return self.message
|
36
36
|
|
37
37
|
def __repr__(self) -> str:
|
38
|
-
return f"{self.__class__.__name__}({
|
38
|
+
return f"{self.__class__.__name__}({self.message!r})"
|
File without changes
|
@@ -4,11 +4,12 @@ __all__ = ["CustomRefreshableSession"]
|
|
4
4
|
|
5
5
|
from typing import Any, Callable
|
6
6
|
|
7
|
-
from
|
8
|
-
from
|
7
|
+
from ..exceptions import BRSError
|
8
|
+
from ..session import BaseRefreshableSession
|
9
|
+
from ..utils import TemporaryCredentials
|
9
10
|
|
10
11
|
|
11
|
-
class CustomRefreshableSession(BaseRefreshableSession,
|
12
|
+
class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
|
12
13
|
"""A :class:`boto3.session.Session` object that automatically refreshes
|
13
14
|
temporary credentials returned by a custom credential getter provided
|
14
15
|
by the user. Useful for users with highly sophisticated or idiosyncratic
|
@@ -74,7 +75,7 @@ class CustomRefreshableSession(BaseRefreshableSession, method="custom"):
|
|
74
75
|
else {}
|
75
76
|
)
|
76
77
|
|
77
|
-
self.
|
78
|
+
self.initialize(
|
78
79
|
credentials_method=self._get_credentials,
|
79
80
|
defer_refresh=defer_refresh is not False,
|
80
81
|
refresh_method="custom",
|
@@ -6,8 +6,9 @@ import os
|
|
6
6
|
|
7
7
|
import requests
|
8
8
|
|
9
|
-
from
|
10
|
-
from
|
9
|
+
from ..exceptions import BRSError
|
10
|
+
from ..session import BaseRefreshableSession
|
11
|
+
from ..utils import TemporaryCredentials
|
11
12
|
|
12
13
|
_ECS_CREDENTIALS_RELATIVE_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
13
14
|
_ECS_CREDENTIALS_FULL_URI = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
@@ -15,7 +16,7 @@ _ECS_AUTHORIZATION_TOKEN = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
|
15
16
|
_DEFAULT_ENDPOINT_BASE = "http://169.254.170.2"
|
16
17
|
|
17
18
|
|
18
|
-
class ECSRefreshableSession(BaseRefreshableSession,
|
19
|
+
class ECSRefreshableSession(BaseRefreshableSession, registry_key="ecs"):
|
19
20
|
"""A boto3 session that automatically refreshes temporary AWS credentials
|
20
21
|
from the ECS container credentials metadata endpoint.
|
21
22
|
|
@@ -40,7 +41,7 @@ class ECSRefreshableSession(BaseRefreshableSession, method="ecs"):
|
|
40
41
|
self._headers = self._build_headers()
|
41
42
|
self._http = self._init_http_session()
|
42
43
|
|
43
|
-
self.
|
44
|
+
self.initialize(
|
44
45
|
credentials_method=self._get_credentials,
|
45
46
|
defer_refresh=defer_refresh is not False,
|
46
47
|
refresh_method="ecs-container-metadata",
|
@@ -0,0 +1,54 @@
|
|
1
|
+
__all__ = ["IoTCertificateRefreshableSession"]
|
2
|
+
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from ...exceptions import BRSError
|
7
|
+
from ...utils import PKCS11, TemporaryCredentials
|
8
|
+
from .core import BaseIoTRefreshableSession
|
9
|
+
|
10
|
+
|
11
|
+
class IoTCertificateRefreshableSession(
|
12
|
+
BaseIoTRefreshableSession, registry_key="certificate"
|
13
|
+
):
|
14
|
+
def __init__(
|
15
|
+
self,
|
16
|
+
endpoint: str,
|
17
|
+
role_alias: str,
|
18
|
+
thing_name: str,
|
19
|
+
certificate: str | bytes,
|
20
|
+
private_key: str | bytes | None = None,
|
21
|
+
pkcs11: PKCS11 | None = None,
|
22
|
+
ca: bytes | None = None,
|
23
|
+
verify_peer: bool = True,
|
24
|
+
):
|
25
|
+
self.endpoint = endpoint
|
26
|
+
self.role_alias = role_alias
|
27
|
+
self.thing_name = thing_name
|
28
|
+
self.certificate = certificate
|
29
|
+
self.private_key = private_key
|
30
|
+
self.pkcs11 = pkcs11
|
31
|
+
self.ca = ca
|
32
|
+
self.verify_peer = verify_peer
|
33
|
+
|
34
|
+
if self.certificate and isinstance(self.certificate, str):
|
35
|
+
with open(Path(self.certificate), "rb") as cert_pem_file:
|
36
|
+
self.certificate = cert_pem_file.read()
|
37
|
+
|
38
|
+
if self.private_key is None and self.pkcs11 is None:
|
39
|
+
raise BRSError(
|
40
|
+
"Either 'private_key' or 'pkcs11' must be provided."
|
41
|
+
)
|
42
|
+
|
43
|
+
if self.private_key is not None and self.pkcs11 is not None:
|
44
|
+
raise BRSError(
|
45
|
+
"Only one of 'private_key' or 'pkcs11' can be provided."
|
46
|
+
)
|
47
|
+
|
48
|
+
if self.private_key and isinstance(self.private_key, str):
|
49
|
+
with open(Path(self.private_key), "rb") as private_key_pem_file:
|
50
|
+
self.private_key = private_key_pem_file.read()
|
51
|
+
|
52
|
+
def _get_credentials(self) -> TemporaryCredentials: ...
|
53
|
+
|
54
|
+
def get_identity(self) -> dict[str, Any]: ...
|
@@ -0,0 +1,16 @@
|
|
1
|
+
__all__ = ["IoTCognitoRefreshableSession"]
|
2
|
+
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from ...utils import TemporaryCredentials
|
6
|
+
from .core import BaseIoTRefreshableSession
|
7
|
+
|
8
|
+
|
9
|
+
class IoTCognitoRefreshableSession(
|
10
|
+
BaseIoTRefreshableSession, registry_key="cognito"
|
11
|
+
):
|
12
|
+
def __init__(self): ...
|
13
|
+
|
14
|
+
def _get_credentials(self) -> TemporaryCredentials: ...
|
15
|
+
|
16
|
+
def get_identity(self) -> dict[str, Any]: ...
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
__all__ = ["IoTRefreshableSession"]
|
4
|
+
|
5
|
+
from typing import get_args
|
6
|
+
|
7
|
+
from ...exceptions import BRSError
|
8
|
+
from ...session import BaseRefreshableSession
|
9
|
+
from ...utils import (
|
10
|
+
IoTAuthenticationMethod,
|
11
|
+
BRSSession,
|
12
|
+
CredentialProvider,
|
13
|
+
Registry,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
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
|
+
class IoTRefreshableSession(BaseRefreshableSession, registry_key="iot"):
|
28
|
+
def __new__(
|
29
|
+
cls,
|
30
|
+
authentication_method: IoTAuthenticationMethod = "certificate",
|
31
|
+
**kwargs,
|
32
|
+
) -> BaseIoTRefreshableSession:
|
33
|
+
if authentication_method not in (
|
34
|
+
methods := cls.get_available_authentication_methods()
|
35
|
+
):
|
36
|
+
raise BRSError(
|
37
|
+
f"{authentication_method!r} is an invalid authentication "
|
38
|
+
"method parameter. Available authentication methods are "
|
39
|
+
f"{', '.join(repr(meth) for meth in methods)}."
|
40
|
+
)
|
41
|
+
|
42
|
+
return BaseIoTRefreshableSession.registry[authentication_method](
|
43
|
+
**kwargs
|
44
|
+
)
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def get_available_authentication_methods(cls) -> list[str]:
|
48
|
+
args = list(get_args(IoTAuthenticationMethod))
|
49
|
+
args.remove("__iot_sentinel__")
|
50
|
+
return args
|
@@ -4,17 +4,18 @@ __all__ = ["STSRefreshableSession"]
|
|
4
4
|
|
5
5
|
from typing import Any
|
6
6
|
|
7
|
-
from
|
8
|
-
from
|
7
|
+
from ..exceptions import BRSWarning
|
8
|
+
from ..session import BaseRefreshableSession
|
9
|
+
from ..utils import AssumeRoleParams, STSClientParams, TemporaryCredentials
|
9
10
|
|
10
11
|
|
11
|
-
class STSRefreshableSession(BaseRefreshableSession,
|
12
|
+
class STSRefreshableSession(BaseRefreshableSession, registry_key="sts"):
|
12
13
|
"""A :class:`boto3.session.Session` object that automatically refreshes
|
13
14
|
temporary AWS credentials using an IAM role that is assumed via STS.
|
14
15
|
|
15
16
|
Parameters
|
16
17
|
----------
|
17
|
-
assume_role_kwargs :
|
18
|
+
assume_role_kwargs : AssumeRoleParams
|
18
19
|
Required keyword arguments for :meth:`STS.Client.assume_role` (i.e.
|
19
20
|
boto3 STS client).
|
20
21
|
defer_refresh : bool, optional
|
@@ -22,7 +23,7 @@ class STSRefreshableSession(BaseRefreshableSession, method="sts"):
|
|
22
23
|
until they are explicitly needed. If ``False`` then temporary
|
23
24
|
credentials refresh immediately upon expiration. It is highly
|
24
25
|
recommended that you use ``True``. Default is ``True``.
|
25
|
-
sts_client_kwargs :
|
26
|
+
sts_client_kwargs : STSClientParams, optional
|
26
27
|
Optional keyword arguments for the :class:`STS.Client` object. Do not
|
27
28
|
provide values for ``service_name`` as they are unnecessary. Default
|
28
29
|
is None.
|
@@ -36,13 +37,12 @@ class STSRefreshableSession(BaseRefreshableSession, method="sts"):
|
|
36
37
|
|
37
38
|
def __init__(
|
38
39
|
self,
|
39
|
-
assume_role_kwargs:
|
40
|
+
assume_role_kwargs: AssumeRoleParams,
|
40
41
|
defer_refresh: bool | None = None,
|
41
|
-
sts_client_kwargs:
|
42
|
+
sts_client_kwargs: STSClientParams | None = None,
|
42
43
|
**kwargs,
|
43
44
|
):
|
44
45
|
super().__init__(**kwargs)
|
45
|
-
defer_refresh = defer_refresh is not False
|
46
46
|
self.assume_role_kwargs = assume_role_kwargs
|
47
47
|
|
48
48
|
if sts_client_kwargs is not None:
|
@@ -60,9 +60,9 @@ class STSRefreshableSession(BaseRefreshableSession, method="sts"):
|
|
60
60
|
self._sts_client = self.client(service_name="sts")
|
61
61
|
|
62
62
|
# mounting refreshable credentials
|
63
|
-
self.
|
63
|
+
self.initialize(
|
64
64
|
credentials_method=self._get_credentials,
|
65
|
-
defer_refresh=defer_refresh,
|
65
|
+
defer_refresh=defer_refresh is not False,
|
66
66
|
refresh_method="sts-assume-role",
|
67
67
|
)
|
68
68
|
|
boto3_refresh_session/session.py
CHANGED
@@ -2,33 +2,18 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
__all__ = ["RefreshableSession"]
|
4
4
|
|
5
|
-
from
|
6
|
-
from datetime import datetime
|
7
|
-
from typing import Any, Callable, ClassVar, Literal, TypedDict, get_args
|
5
|
+
from typing import get_args
|
8
6
|
|
9
|
-
from
|
10
|
-
from
|
11
|
-
DeferredRefreshableCredentials,
|
12
|
-
RefreshableCredentials,
|
13
|
-
)
|
7
|
+
from .exceptions import BRSError
|
8
|
+
from .utils import BRSSession, CredentialProvider, Method, Registry
|
14
9
|
|
15
|
-
from .exceptions import BRSError, BRSWarning
|
16
10
|
|
17
|
-
|
18
|
-
Method
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
"""Temporary IAM credentials."""
|
24
|
-
|
25
|
-
access_key: str
|
26
|
-
secret_key: str
|
27
|
-
token: str
|
28
|
-
expiry_time: datetime | str
|
29
|
-
|
30
|
-
|
31
|
-
class BaseRefreshableSession(ABC, Session):
|
11
|
+
class BaseRefreshableSession(
|
12
|
+
Registry[Method],
|
13
|
+
CredentialProvider,
|
14
|
+
BRSSession,
|
15
|
+
registry_key="__sentinel__",
|
16
|
+
):
|
32
17
|
"""Abstract base class for implementing refreshable AWS sessions.
|
33
18
|
|
34
19
|
Provides a common interface and factory registration mechanism
|
@@ -45,76 +30,9 @@ class BaseRefreshableSession(ABC, Session):
|
|
45
30
|
Class-level registry mapping method names to registered session types.
|
46
31
|
"""
|
47
32
|
|
48
|
-
# adding this and __init_subclass__ to avoid circular imports
|
49
|
-
# as well as simplify future addition of new methods
|
50
|
-
registry: ClassVar[dict[Method, type[BaseRefreshableSession]]] = {}
|
51
|
-
|
52
|
-
def __init_subclass__(cls, method: Method):
|
53
|
-
super().__init_subclass__()
|
54
|
-
|
55
|
-
# guarantees that methods are unique
|
56
|
-
if method in BaseRefreshableSession.registry:
|
57
|
-
BRSWarning(
|
58
|
-
f"Method {repr(method)} is already registered. Overwriting."
|
59
|
-
)
|
60
|
-
|
61
|
-
BaseRefreshableSession.registry[method] = cls
|
62
|
-
|
63
33
|
def __init__(self, **kwargs):
|
64
34
|
super().__init__(**kwargs)
|
65
35
|
|
66
|
-
@abstractmethod
|
67
|
-
def _get_credentials(self) -> TemporaryCredentials: ...
|
68
|
-
|
69
|
-
@abstractmethod
|
70
|
-
def get_identity(self) -> dict[str, Any]: ...
|
71
|
-
|
72
|
-
def _refresh_using(
|
73
|
-
self,
|
74
|
-
credentials_method: Callable,
|
75
|
-
defer_refresh: bool,
|
76
|
-
refresh_method: RefreshMethod,
|
77
|
-
):
|
78
|
-
# determining how exactly to refresh expired temporary credentials
|
79
|
-
if not defer_refresh:
|
80
|
-
self._credentials = RefreshableCredentials.create_from_metadata(
|
81
|
-
metadata=credentials_method(),
|
82
|
-
refresh_using=credentials_method,
|
83
|
-
method=refresh_method,
|
84
|
-
)
|
85
|
-
else:
|
86
|
-
self._credentials = DeferredRefreshableCredentials(
|
87
|
-
refresh_using=credentials_method, method=refresh_method
|
88
|
-
)
|
89
|
-
|
90
|
-
def refreshable_credentials(self) -> dict[str, str]:
|
91
|
-
"""The current temporary AWS security credentials.
|
92
|
-
|
93
|
-
Returns
|
94
|
-
-------
|
95
|
-
dict[str, str]
|
96
|
-
Temporary AWS security credentials containing:
|
97
|
-
AWS_ACCESS_KEY_ID : str
|
98
|
-
AWS access key identifier.
|
99
|
-
AWS_SECRET_ACCESS_KEY : str
|
100
|
-
AWS secret access key.
|
101
|
-
AWS_SESSION_TOKEN : str
|
102
|
-
AWS session token.
|
103
|
-
"""
|
104
|
-
|
105
|
-
creds = self.get_credentials().get_frozen_credentials()
|
106
|
-
return {
|
107
|
-
"AWS_ACCESS_KEY_ID": creds.access_key,
|
108
|
-
"AWS_SECRET_ACCESS_KEY": creds.secret_key,
|
109
|
-
"AWS_SESSION_TOKEN": creds.token,
|
110
|
-
}
|
111
|
-
|
112
|
-
@property
|
113
|
-
def credentials(self) -> dict[str, str]:
|
114
|
-
"""The current temporary AWS security credentials."""
|
115
|
-
|
116
|
-
return self.refreshable_credentials()
|
117
|
-
|
118
36
|
|
119
37
|
class RefreshableSession:
|
120
38
|
"""Factory class for constructing refreshable boto3 sessions using various
|
@@ -143,9 +61,9 @@ class RefreshableSession:
|
|
143
61
|
|
144
62
|
See Also
|
145
63
|
--------
|
146
|
-
boto3_refresh_session.custom.CustomRefreshableSession
|
147
|
-
boto3_refresh_session.sts.STSRefreshableSession
|
148
|
-
boto3_refresh_session.ecs.ECSRefreshableSession
|
64
|
+
boto3_refresh_session.methods.custom.CustomRefreshableSession
|
65
|
+
boto3_refresh_session.methods.sts.STSRefreshableSession
|
66
|
+
boto3_refresh_session.methods.ecs.ECSRefreshableSession
|
149
67
|
"""
|
150
68
|
|
151
69
|
def __new__(
|
@@ -153,13 +71,12 @@ class RefreshableSession:
|
|
153
71
|
) -> BaseRefreshableSession:
|
154
72
|
if method not in (methods := cls.get_available_methods()):
|
155
73
|
raise BRSError(
|
156
|
-
f"{
|
74
|
+
f"{method!r} is an invalid method parameter. "
|
157
75
|
"Available methods are "
|
158
76
|
f"{', '.join(repr(meth) for meth in methods)}."
|
159
77
|
)
|
160
78
|
|
161
|
-
|
162
|
-
return obj(**kwargs)
|
79
|
+
return BaseRefreshableSession.registry[method](**kwargs)
|
163
80
|
|
164
81
|
@classmethod
|
165
82
|
def get_available_methods(cls) -> list[str]:
|
@@ -169,7 +86,9 @@ class RefreshableSession:
|
|
169
86
|
-------
|
170
87
|
list[str]
|
171
88
|
A list of all currently available credential refresh methods,
|
172
|
-
e.g. 'sts'.
|
89
|
+
e.g. 'sts', 'ecs', 'custom'.
|
173
90
|
"""
|
174
91
|
|
175
|
-
|
92
|
+
args = list(get_args(Method))
|
93
|
+
args.remove("__sentinel__")
|
94
|
+
return args
|
@@ -0,0 +1,212 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import (
|
4
|
+
Any,
|
5
|
+
Callable,
|
6
|
+
ClassVar,
|
7
|
+
Generic,
|
8
|
+
List,
|
9
|
+
Literal,
|
10
|
+
TypedDict,
|
11
|
+
TypeVar,
|
12
|
+
)
|
13
|
+
|
14
|
+
from boto3.session import Session
|
15
|
+
from botocore.credentials import (
|
16
|
+
DeferredRefreshableCredentials,
|
17
|
+
RefreshableCredentials,
|
18
|
+
)
|
19
|
+
|
20
|
+
from .exceptions import BRSWarning
|
21
|
+
|
22
|
+
try:
|
23
|
+
from typing import NotRequired # type: ignore[import]
|
24
|
+
except ImportError:
|
25
|
+
from typing_extensions import NotRequired
|
26
|
+
|
27
|
+
#: Type alias for all currently available IoT authentication methods.
|
28
|
+
IoTAuthenticationMethod = Literal["certificate", "cognito", "__iot_sentinel__"]
|
29
|
+
|
30
|
+
#: Type alias for all currently available credential refresh methods.
|
31
|
+
Method = Literal[
|
32
|
+
"sts",
|
33
|
+
"ecs",
|
34
|
+
"custom",
|
35
|
+
"__sentinel__",
|
36
|
+
] # TODO: Add iot when implemented
|
37
|
+
|
38
|
+
#: Type alias for all refresh method names.
|
39
|
+
RefreshMethod = Literal[
|
40
|
+
"sts-assume-role",
|
41
|
+
"ecs-container-metadata",
|
42
|
+
"custom",
|
43
|
+
] # Add iot-certificate and iot-cognito when iot implemented
|
44
|
+
|
45
|
+
#: Type alias for all currently registered credential refresh methods.
|
46
|
+
RegistryKey = TypeVar("RegistryKey", bound=str)
|
47
|
+
|
48
|
+
|
49
|
+
class Registry(Generic[RegistryKey]):
|
50
|
+
"""Gives any hierarchy a class-level registry."""
|
51
|
+
|
52
|
+
registry: ClassVar[dict[str, type]] = {}
|
53
|
+
|
54
|
+
def __init_subclass__(cls, *, registry_key: RegistryKey, **kwargs: Any):
|
55
|
+
super().__init_subclass__(**kwargs)
|
56
|
+
|
57
|
+
if registry_key in cls.registry:
|
58
|
+
BRSWarning(f"{registry_key!r} already registered. Overwriting.")
|
59
|
+
|
60
|
+
if "sentinel" not in registry_key:
|
61
|
+
cls.registry[registry_key] = cls
|
62
|
+
|
63
|
+
@classmethod
|
64
|
+
def items(cls) -> dict[str, type]:
|
65
|
+
"""Typed accessor for introspection / debugging."""
|
66
|
+
|
67
|
+
return dict(cls.registry)
|
68
|
+
|
69
|
+
|
70
|
+
class TemporaryCredentials(TypedDict):
|
71
|
+
"""Temporary IAM credentials."""
|
72
|
+
|
73
|
+
access_key: str
|
74
|
+
secret_key: str
|
75
|
+
token: str
|
76
|
+
expiry_time: datetime | str
|
77
|
+
|
78
|
+
|
79
|
+
class RefreshableTemporaryCredentials(TypedDict):
|
80
|
+
"""Refreshable IAM credentials.
|
81
|
+
|
82
|
+
Parameters
|
83
|
+
----------
|
84
|
+
AWS_ACCESS_KEY_ID : str
|
85
|
+
AWS access key identifier.
|
86
|
+
AWS_SECRET_ACCESS_KEY : str
|
87
|
+
AWS secret access key.
|
88
|
+
AWS_SESSION_TOKEN : str
|
89
|
+
AWS session token.
|
90
|
+
"""
|
91
|
+
|
92
|
+
AWS_ACCESS_KEY_ID: str
|
93
|
+
AWS_SECRET_ACCESS_KEY: str
|
94
|
+
AWS_SESSION_TOKEN: str
|
95
|
+
|
96
|
+
|
97
|
+
class CredentialProvider(ABC):
|
98
|
+
"""Defines the abstract surface every refreshable session must expose."""
|
99
|
+
|
100
|
+
@abstractmethod
|
101
|
+
def _get_credentials(self) -> TemporaryCredentials: ...
|
102
|
+
|
103
|
+
@abstractmethod
|
104
|
+
def get_identity(self) -> dict[str, Any]: ...
|
105
|
+
|
106
|
+
|
107
|
+
class BRSSession(Session):
|
108
|
+
"""Wrapper for boto3.session.Session.
|
109
|
+
|
110
|
+
Other Parameters
|
111
|
+
----------------
|
112
|
+
kwargs : Any
|
113
|
+
Optional keyword arguments for initializing boto3.session.Session."""
|
114
|
+
|
115
|
+
def __init__(self, **kwargs):
|
116
|
+
super().__init__(**kwargs)
|
117
|
+
|
118
|
+
def initialize(
|
119
|
+
self,
|
120
|
+
credentials_method: Callable,
|
121
|
+
defer_refresh: bool,
|
122
|
+
refresh_method: RefreshMethod,
|
123
|
+
):
|
124
|
+
# determining how exactly to refresh expired temporary credentials
|
125
|
+
if not defer_refresh:
|
126
|
+
self._credentials = RefreshableCredentials.create_from_metadata(
|
127
|
+
metadata=credentials_method(),
|
128
|
+
refresh_using=credentials_method,
|
129
|
+
method=refresh_method,
|
130
|
+
)
|
131
|
+
else:
|
132
|
+
self._credentials = DeferredRefreshableCredentials(
|
133
|
+
refresh_using=credentials_method, method=refresh_method
|
134
|
+
)
|
135
|
+
|
136
|
+
def refreshable_credentials(self) -> RefreshableTemporaryCredentials:
|
137
|
+
"""The current temporary AWS security credentials.
|
138
|
+
|
139
|
+
Returns
|
140
|
+
-------
|
141
|
+
RefreshableTemporaryCredentials
|
142
|
+
Temporary AWS security credentials containing:
|
143
|
+
AWS_ACCESS_KEY_ID : str
|
144
|
+
AWS access key identifier.
|
145
|
+
AWS_SECRET_ACCESS_KEY : str
|
146
|
+
AWS secret access key.
|
147
|
+
AWS_SESSION_TOKEN : str
|
148
|
+
AWS session token.
|
149
|
+
"""
|
150
|
+
|
151
|
+
creds = self.get_credentials().get_frozen_credentials()
|
152
|
+
return {
|
153
|
+
"AWS_ACCESS_KEY_ID": creds.access_key,
|
154
|
+
"AWS_SECRET_ACCESS_KEY": creds.secret_key,
|
155
|
+
"AWS_SESSION_TOKEN": creds.token,
|
156
|
+
}
|
157
|
+
|
158
|
+
@property
|
159
|
+
def credentials(self) -> RefreshableTemporaryCredentials:
|
160
|
+
"""The current temporary AWS security credentials."""
|
161
|
+
|
162
|
+
return self.refreshable_credentials()
|
163
|
+
|
164
|
+
|
165
|
+
class Tag(TypedDict):
|
166
|
+
Key: str
|
167
|
+
Value: str
|
168
|
+
|
169
|
+
|
170
|
+
class PolicyDescriptorType(TypedDict):
|
171
|
+
arn: str
|
172
|
+
|
173
|
+
|
174
|
+
class ProvidedContext(TypedDict):
|
175
|
+
ProviderArn: str
|
176
|
+
ContextAssertion: str
|
177
|
+
|
178
|
+
|
179
|
+
class AssumeRoleParams(TypedDict):
|
180
|
+
RoleArn: str
|
181
|
+
RoleSessionName: str
|
182
|
+
PolicyArns: NotRequired[List[PolicyDescriptorType]]
|
183
|
+
Policy: NotRequired[str]
|
184
|
+
DurationSeconds: NotRequired[int]
|
185
|
+
ExternalId: NotRequired[str]
|
186
|
+
SerialNumber: NotRequired[str]
|
187
|
+
TokenCode: NotRequired[str]
|
188
|
+
Tags: NotRequired[List[Tag]]
|
189
|
+
TransitiveTagKeys: NotRequired[List[str]]
|
190
|
+
SourceIdentity: NotRequired[str]
|
191
|
+
ProvidedContexts: NotRequired[List[ProvidedContext]]
|
192
|
+
|
193
|
+
|
194
|
+
class STSClientParams(TypedDict):
|
195
|
+
region_name: NotRequired[str]
|
196
|
+
api_version: NotRequired[str]
|
197
|
+
use_ssl: NotRequired[bool]
|
198
|
+
verify: NotRequired[bool | str]
|
199
|
+
endpoint_url: NotRequired[str]
|
200
|
+
aws_access_key_id: NotRequired[str]
|
201
|
+
aws_secret_access_key: NotRequired[str]
|
202
|
+
aws_session_token: NotRequired[str]
|
203
|
+
config: NotRequired[Any]
|
204
|
+
aws_account_id: NotRequired[str]
|
205
|
+
|
206
|
+
|
207
|
+
class PKCS11(TypedDict):
|
208
|
+
pkcs11_loc: str
|
209
|
+
user_pin: NotRequired[str]
|
210
|
+
slot_id: NotRequired[int]
|
211
|
+
token_label: NotRequired[str | None]
|
212
|
+
private_key_label: NotRequired[str | None]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: boto3-refresh-session
|
3
|
-
Version:
|
3
|
+
Version: 2.0.1
|
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
6
|
Keywords: boto3,botocore,aws,sts,ecs,credentials,token,refresh
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Requires-Dist: boto3
|
19
19
|
Requires-Dist: botocore
|
20
20
|
Requires-Dist: requests
|
21
|
+
Requires-Dist: typing-extensions
|
21
22
|
Project-URL: Documentation, https://michaelthomasletts.github.io/boto3-refresh-session/index.html
|
22
23
|
Project-URL: Repository, https://github.com/michaelthomasletts/boto3-refresh-session
|
23
24
|
Description-Content-Type: text/markdown
|
@@ -57,7 +58,7 @@ Description-Content-Type: text/markdown
|
|
57
58
|
</a>
|
58
59
|
|
59
60
|
<a href="https://pepy.tech/projects/boto3-refresh-session">
|
60
|
-
<img src="https://img.shields.io/badge/downloads-
|
61
|
+
<img src="https://img.shields.io/badge/downloads-85.0K-red?logo=python&color=%23FF0000&label=Downloads" alt="Downloads"/>
|
61
62
|
</a>
|
62
63
|
|
63
64
|
<a href="https://michaelthomasletts.github.io/boto3-refresh-session/index.html">
|
@@ -83,6 +84,16 @@ Description-Content-Type: text/markdown
|
|
83
84
|
|
84
85
|
</div>
|
85
86
|
|
87
|
+
---
|
88
|
+
|
89
|
+
## ⚠️ Important Update
|
90
|
+
|
91
|
+
I am currently grappling with a serious medical condition that negatively impacts my vision. Accordingly, development of the `iot` and `ec2` modules has been delayed. Expect delayed responses to issues and pull requests until my health stabilizes.
|
92
|
+
|
93
|
+
Thank you for supporting this project.
|
94
|
+
|
95
|
+
---
|
96
|
+
|
86
97
|
## Features
|
87
98
|
|
88
99
|
- Drop-in replacement for `boto3.session.Session`
|
@@ -0,0 +1,17 @@
|
|
1
|
+
boto3_refresh_session/__init__.py,sha256=R7O6fs-G6dGNu2w76a3-5ctRTVCa8JFuTYRit1nsVH4,387
|
2
|
+
boto3_refresh_session/exceptions.py,sha256=cP5d9S8QnUEwXIU3pzMGr6jMOz447kddNJ_UIRERMrk,964
|
3
|
+
boto3_refresh_session/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
boto3_refresh_session/methods/custom.py,sha256=ZJAHgAajz9w6lohDeN-NWrxmZ2LaDxLfGWT2jmoTilQ,3868
|
5
|
+
boto3_refresh_session/methods/ecs.py,sha256=yAfQbFXMB_RDk-uX_zVVgOt2Lc-1SVx4PJmFyLuDbms,3750
|
6
|
+
boto3_refresh_session/methods/iot/__init__.typed,sha256=Z33nIB6oCsz9TZwikHfNHgY1SKxkSCdB5rwdPSUl3C4,135
|
7
|
+
boto3_refresh_session/methods/iot/certificate.typed,sha256=yvKptwH-GagBREI_1AXs_mCaU6vL3AcUDQ58vn7V8QM,1774
|
8
|
+
boto3_refresh_session/methods/iot/cognito.typed,sha256=0VorzOXHpsVemiGWZzHE9fuX-MZcpqzWQ4nK3gNDUMg,389
|
9
|
+
boto3_refresh_session/methods/iot/core.typed,sha256=tL-ngB2XYq0XtxhS9mbggCJYdX3eEE0u1Gvcq8sEYGE,1422
|
10
|
+
boto3_refresh_session/methods/sts.py,sha256=uWvT-ybRVZBusr4jNHyfHOB_AKhs6t0dN_fNI-gkUKM,3268
|
11
|
+
boto3_refresh_session/session.py,sha256=_Z3uB5Xq3S-dFqOFmWhMQbcd__NPGThjULLPStHI6E4,2914
|
12
|
+
boto3_refresh_session/utils.py,sha256=ME3sNWZc6oTNUdoRmiNOVmLLp9iSqJe-yfudRV0A46U,5729
|
13
|
+
boto3_refresh_session-2.0.1.dist-info/LICENSE,sha256=I3ZYTXAjbIly6bm6J-TvFTuuHwTKws4h89QaY5c5HiY,1067
|
14
|
+
boto3_refresh_session-2.0.1.dist-info/METADATA,sha256=1cAoUQV7MYHj6Fu41GxqTMTaFq3o7RrLYm7yqtEXPXQ,8570
|
15
|
+
boto3_refresh_session-2.0.1.dist-info/NOTICE,sha256=1s8r33qbl1z0YvPB942iWgvbkP94P_e8AnROr1qXXuw,939
|
16
|
+
boto3_refresh_session-2.0.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
17
|
+
boto3_refresh_session-2.0.1.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
boto3_refresh_session/__init__.py,sha256=WW-YPxwblDXCjhzR9VclZPfz_g0i_YcHrAMK8JIMwOs,364
|
2
|
-
boto3_refresh_session/custom.py,sha256=wR7122COYuFkmVprORfz-mPRqH4XeTH8Uw8_2QUYWUg,3845
|
3
|
-
boto3_refresh_session/ecs.py,sha256=npuMlooixhB7qXYACsPDRx8A2xhFTrRgcAPFZK1P9i8,3727
|
4
|
-
boto3_refresh_session/exceptions.py,sha256=qcFzdIuK5PZirs77H_Kb64S9QFb6cn2OJtirjvaRLiY,972
|
5
|
-
boto3_refresh_session/session.py,sha256=TGI3-Zv52kGigw0f-mUNBkB2nqM632gCRZgY6KhZd-A,5564
|
6
|
-
boto3_refresh_session/sts.py,sha256=f9dtJMfs5bYWfZAcKuANAe0InjmNjZfKyqt5LpHFDxk,3202
|
7
|
-
boto3_refresh_session-1.3.22.dist-info/LICENSE,sha256=I3ZYTXAjbIly6bm6J-TvFTuuHwTKws4h89QaY5c5HiY,1067
|
8
|
-
boto3_refresh_session-1.3.22.dist-info/METADATA,sha256=kH4EJR3iEgSslxmBxmhy53q4jNKp3q9Gg6bj5PxSld0,8209
|
9
|
-
boto3_refresh_session-1.3.22.dist-info/NOTICE,sha256=1s8r33qbl1z0YvPB942iWgvbkP94P_e8AnROr1qXXuw,939
|
10
|
-
boto3_refresh_session-1.3.22.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
11
|
-
boto3_refresh_session-1.3.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|