boto3-refresh-session 1.3.11__py3-none-any.whl → 7.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.
Potentially problematic release.
This version of boto3-refresh-session might be problematic. Click here for more details.
- boto3_refresh_session/__init__.py +19 -6
- boto3_refresh_session/exceptions.py +116 -4
- boto3_refresh_session/methods/__init__.py +14 -0
- boto3_refresh_session/methods/custom.py +145 -0
- boto3_refresh_session/methods/iot/__init__.py +11 -0
- boto3_refresh_session/methods/iot/core.py +46 -0
- boto3_refresh_session/methods/iot/x509.py +614 -0
- boto3_refresh_session/methods/sts.py +276 -0
- boto3_refresh_session/session.py +58 -120
- boto3_refresh_session/utils/__init__.py +16 -0
- boto3_refresh_session/utils/cache.py +98 -0
- boto3_refresh_session/utils/constants.py +16 -0
- boto3_refresh_session/utils/internal.py +441 -0
- boto3_refresh_session/utils/typing.py +137 -0
- boto3_refresh_session-7.0.1.dist-info/METADATA +168 -0
- boto3_refresh_session-7.0.1.dist-info/RECORD +19 -0
- {boto3_refresh_session-1.3.11.dist-info → boto3_refresh_session-7.0.1.dist-info}/WHEEL +1 -1
- boto3_refresh_session-7.0.1.dist-info/licenses/LICENSE +373 -0
- boto3_refresh_session-7.0.1.dist-info/licenses/NOTICE +21 -0
- boto3_refresh_session/custom.py +0 -108
- boto3_refresh_session/ecs.py +0 -109
- boto3_refresh_session/sts.py +0 -85
- boto3_refresh_session-1.3.11.dist-info/LICENSE +0 -21
- boto3_refresh_session-1.3.11.dist-info/METADATA +0 -178
- boto3_refresh_session-1.3.11.dist-info/NOTICE +0 -12
- boto3_refresh_session-1.3.11.dist-info/RECORD +0 -11
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from .sts import STSRefreshableSession
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
4
|
|
|
6
|
-
__all__ = [
|
|
7
|
-
|
|
5
|
+
__all__ = []
|
|
6
|
+
|
|
7
|
+
from . import exceptions, session
|
|
8
|
+
from .exceptions import *
|
|
9
|
+
from .methods.custom import *
|
|
10
|
+
from .methods.iot import *
|
|
11
|
+
from .methods.sts import *
|
|
12
|
+
from .session import *
|
|
13
|
+
|
|
14
|
+
__all__.extend(session.__all__)
|
|
15
|
+
__all__.extend(exceptions.__all__)
|
|
16
|
+
__version__ = "7.0.1"
|
|
17
|
+
__title__ = "boto3-refresh-session"
|
|
8
18
|
__author__ = "Mike Letts"
|
|
19
|
+
__maintainer__ = "Mike Letts"
|
|
20
|
+
__license__ = "MPL-2.0"
|
|
21
|
+
__email__ = "lettsmt@gmail.com"
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
"""Custom exception and warning types for boto3-refresh-session."""
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"BRSError",
|
|
9
|
+
"BRSValidationError",
|
|
10
|
+
"BRSConfigurationError",
|
|
11
|
+
"BRSCredentialError",
|
|
12
|
+
"BRSConnectionError",
|
|
13
|
+
"BRSRequestError",
|
|
14
|
+
"BRSCacheError",
|
|
15
|
+
"BRSCacheNotFoundError",
|
|
16
|
+
"BRSCacheExistsError",
|
|
17
|
+
"BRSWarning",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
import warnings
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
|
|
1
24
|
class BRSError(Exception):
|
|
2
25
|
"""The base exception for boto3-refresh-session.
|
|
3
26
|
|
|
@@ -5,17 +28,100 @@ class BRSError(Exception):
|
|
|
5
28
|
----------
|
|
6
29
|
message : str, optional
|
|
7
30
|
The message to raise.
|
|
31
|
+
code : str | int, optional
|
|
32
|
+
A short machine-friendly code for the error.
|
|
33
|
+
status_code : int, optional
|
|
34
|
+
An HTTP status code, if applicable.
|
|
35
|
+
details : dict[str, Any], optional
|
|
36
|
+
Extra structured metadata for debugging or logging.
|
|
37
|
+
param : str, optional
|
|
38
|
+
The parameter name related to the error.
|
|
39
|
+
value : Any, optional
|
|
40
|
+
The parameter value related to the error.
|
|
8
41
|
"""
|
|
9
42
|
|
|
10
|
-
def __init__(
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
message: str | None = None,
|
|
46
|
+
*,
|
|
47
|
+
code: str | int | None = None,
|
|
48
|
+
status_code: int | None = None,
|
|
49
|
+
details: dict[str, Any] | None = None,
|
|
50
|
+
param: str | None = None,
|
|
51
|
+
value: Any | None = None,
|
|
52
|
+
):
|
|
11
53
|
self.message = "" if message is None else message
|
|
54
|
+
self.code = code
|
|
55
|
+
self.status_code = status_code
|
|
56
|
+
self.details = details
|
|
57
|
+
self.param = param
|
|
58
|
+
self.value = value
|
|
12
59
|
super().__init__(self.message)
|
|
13
60
|
|
|
14
61
|
def __str__(self) -> str:
|
|
15
|
-
|
|
62
|
+
base = self.message
|
|
63
|
+
extras: list[str] = []
|
|
64
|
+
if self.code is not None:
|
|
65
|
+
extras.append(f"code={self.code!r}")
|
|
66
|
+
if self.status_code is not None:
|
|
67
|
+
extras.append(f"status_code={self.status_code!r}")
|
|
68
|
+
if self.param is not None:
|
|
69
|
+
extras.append(f"param={self.param!r}")
|
|
70
|
+
if self.value is not None:
|
|
71
|
+
extras.append(f"value={self.value!r}")
|
|
72
|
+
if self.details is not None:
|
|
73
|
+
extras.append(f"details={self.details!r}")
|
|
74
|
+
if extras:
|
|
75
|
+
if base:
|
|
76
|
+
return f"{base} ({', '.join(extras)})"
|
|
77
|
+
return ", ".join(extras)
|
|
78
|
+
return base
|
|
16
79
|
|
|
17
80
|
def __repr__(self) -> str:
|
|
18
|
-
|
|
81
|
+
args = [repr(self.message)]
|
|
82
|
+
if self.code is not None:
|
|
83
|
+
args.append(f"code={self.code!r}")
|
|
84
|
+
if self.status_code is not None:
|
|
85
|
+
args.append(f"status_code={self.status_code!r}")
|
|
86
|
+
if self.param is not None:
|
|
87
|
+
args.append(f"param={self.param!r}")
|
|
88
|
+
if self.value is not None:
|
|
89
|
+
args.append(f"value={self.value!r}")
|
|
90
|
+
if self.details is not None:
|
|
91
|
+
args.append(f"details={self.details!r}")
|
|
92
|
+
return f"{self.__class__.__name__}({', '.join(args)})"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class BRSValidationError(BRSError):
|
|
96
|
+
"""Raised when inputs are missing or invalid."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class BRSConfigurationError(BRSError):
|
|
100
|
+
"""Raised when configuration is incompatible or malformed."""
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class BRSCredentialError(BRSError):
|
|
104
|
+
"""Raised when credential data is malformed or incomplete."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class BRSConnectionError(BRSError):
|
|
108
|
+
"""Raised when a connection or transport fails."""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class BRSRequestError(BRSError):
|
|
112
|
+
"""Raised when a remote request fails."""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class BRSCacheError(BRSError):
|
|
116
|
+
"""Raised when cache operations violate the cache contract."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class BRSCacheNotFoundError(BRSCacheError):
|
|
120
|
+
"""Raised when a cached item is not found."""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class BRSCacheExistsError(BRSCacheError):
|
|
124
|
+
"""Raised when attempting to insert an existing cached item."""
|
|
19
125
|
|
|
20
126
|
|
|
21
127
|
class BRSWarning(UserWarning):
|
|
@@ -35,4 +141,10 @@ class BRSWarning(UserWarning):
|
|
|
35
141
|
return self.message
|
|
36
142
|
|
|
37
143
|
def __repr__(self) -> str:
|
|
38
|
-
return f"{self.__class__.__name__}({
|
|
144
|
+
return f"{self.__class__.__name__}({self.message!r})"
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def warn(cls, message: str, *, stacklevel: int = 2):
|
|
148
|
+
"""Emits a BRSWarning with a consistent stacklevel."""
|
|
149
|
+
|
|
150
|
+
warnings.warn(cls(message), stacklevel=stacklevel)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
__all__ = []
|
|
6
|
+
|
|
7
|
+
from . import custom, iot, sts
|
|
8
|
+
from .custom import *
|
|
9
|
+
from .iot import *
|
|
10
|
+
from .sts import *
|
|
11
|
+
|
|
12
|
+
__all__.extend(custom.__all__)
|
|
13
|
+
__all__.extend(iot.__all__)
|
|
14
|
+
__all__.extend(sts.__all__)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
"""Custom refreshable session using a user-provided credential getter."""
|
|
6
|
+
|
|
7
|
+
__all__ = ["CustomRefreshableSession"]
|
|
8
|
+
|
|
9
|
+
from ..exceptions import BRSCredentialError, BRSWarning
|
|
10
|
+
from ..utils import (
|
|
11
|
+
BaseRefreshableSession,
|
|
12
|
+
CustomCredentialsMethod,
|
|
13
|
+
CustomCredentialsMethodArgs,
|
|
14
|
+
Identity,
|
|
15
|
+
TemporaryCredentials,
|
|
16
|
+
refreshable_session,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@refreshable_session
|
|
21
|
+
class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
|
|
22
|
+
"""A :class:`boto3.session.Session` object that automatically refreshes
|
|
23
|
+
temporary credentials returned by a custom credential getter provided
|
|
24
|
+
by the user. Useful for users with highly sophisticated or idiosyncratic
|
|
25
|
+
authentication flows.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
custom_credentials_method: CustomCredentialsMethod
|
|
30
|
+
Required. Accepts a callable object that returns temporary AWS
|
|
31
|
+
security credentials. That object must return a dictionary containing
|
|
32
|
+
'access_key', 'secret_key', 'token', and 'expiry_time' when called.
|
|
33
|
+
custom_credentials_method_args : CustomCredentialsMethodArgs, optional
|
|
34
|
+
Optional keyword arguments for the function passed to the
|
|
35
|
+
``custom_credentials_method`` parameter.
|
|
36
|
+
defer_refresh : bool, optional
|
|
37
|
+
If ``True`` then temporary credentials are not automatically refreshed
|
|
38
|
+
until they are explicitly needed. If ``False`` then temporary
|
|
39
|
+
credentials refresh immediately upon expiration. It is highly
|
|
40
|
+
recommended that you use ``True``. Default is ``True``.
|
|
41
|
+
advisory_timeout : int, optional
|
|
42
|
+
USE THIS ARGUMENT WITH CAUTION!!!
|
|
43
|
+
|
|
44
|
+
Botocore will attempt to refresh credentials early according to
|
|
45
|
+
this value (in seconds), but will continue using the existing
|
|
46
|
+
credentials if refresh fails. Default is 15 minutes (900 seconds).
|
|
47
|
+
mandatory_timeout : int, optional
|
|
48
|
+
USE THIS ARGUMENT WITH CAUTION!!!
|
|
49
|
+
|
|
50
|
+
Botocore requires a successful refresh before continuing. If
|
|
51
|
+
refresh fails in this window (in seconds), API calls may fail.
|
|
52
|
+
Default is 10 minutes (600 seconds).
|
|
53
|
+
cache_clients : bool, optional
|
|
54
|
+
If ``True`` then clients created by this session will be cached and
|
|
55
|
+
reused for subsequent calls to :meth:`client()` with the same
|
|
56
|
+
parameter signatures. Due to the memory overhead of clients, the
|
|
57
|
+
default is ``True`` in order to protect system resources.
|
|
58
|
+
|
|
59
|
+
Other Parameters
|
|
60
|
+
----------------
|
|
61
|
+
kwargs : dict
|
|
62
|
+
Optional keyword arguments for the :class:`boto3.session.Session`
|
|
63
|
+
object.
|
|
64
|
+
|
|
65
|
+
Examples
|
|
66
|
+
--------
|
|
67
|
+
Write (or import) the callable object for obtaining temporary AWS security
|
|
68
|
+
credentials.
|
|
69
|
+
|
|
70
|
+
>>> def your_custom_credential_getter(your_param, another_param):
|
|
71
|
+
>>> ...
|
|
72
|
+
>>> return {
|
|
73
|
+
>>> 'access_key': ...,
|
|
74
|
+
>>> 'secret_key': ...,
|
|
75
|
+
>>> 'token': ...,
|
|
76
|
+
>>> 'expiry_time': ...,
|
|
77
|
+
>>> }
|
|
78
|
+
|
|
79
|
+
Pass that callable object to ``RefreshableSession``.
|
|
80
|
+
|
|
81
|
+
>>> sess = RefreshableSession(
|
|
82
|
+
>>> method='custom',
|
|
83
|
+
>>> custom_credentials_method=your_custom_credential_getter,
|
|
84
|
+
>>> custom_credentials_method_args=...,
|
|
85
|
+
>>> )
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
custom_credentials_method: CustomCredentialsMethod,
|
|
91
|
+
custom_credentials_method_args: (
|
|
92
|
+
CustomCredentialsMethodArgs | None
|
|
93
|
+
) = None,
|
|
94
|
+
**kwargs,
|
|
95
|
+
):
|
|
96
|
+
if "refresh_method" in kwargs:
|
|
97
|
+
BRSWarning.warn(
|
|
98
|
+
"'refresh_method' cannot be set manually. "
|
|
99
|
+
"Reverting to 'custom'."
|
|
100
|
+
)
|
|
101
|
+
del kwargs["refresh_method"]
|
|
102
|
+
|
|
103
|
+
# initializing BRSSession
|
|
104
|
+
super().__init__(refresh_method="custom", **kwargs)
|
|
105
|
+
|
|
106
|
+
# initializing various other attributes
|
|
107
|
+
self._custom_get_credentials: CustomCredentialsMethod = (
|
|
108
|
+
custom_credentials_method
|
|
109
|
+
)
|
|
110
|
+
self._custom_get_credentials_args: CustomCredentialsMethodArgs = (
|
|
111
|
+
custom_credentials_method_args
|
|
112
|
+
if custom_credentials_method_args is not None
|
|
113
|
+
else {}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def _get_credentials(self) -> TemporaryCredentials:
|
|
117
|
+
credentials: TemporaryCredentials = self._custom_get_credentials(
|
|
118
|
+
**self._custom_get_credentials_args
|
|
119
|
+
)
|
|
120
|
+
required_keys = {"access_key", "secret_key", "token", "expiry_time"}
|
|
121
|
+
|
|
122
|
+
if missing := required_keys - credentials.keys():
|
|
123
|
+
raise BRSCredentialError(
|
|
124
|
+
"The dict returned by custom_credentials_method is missing "
|
|
125
|
+
"required key-value pairs.",
|
|
126
|
+
details={"missing": sorted(missing)},
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return credentials
|
|
130
|
+
|
|
131
|
+
def get_identity(self) -> Identity:
|
|
132
|
+
"""Returns metadata about the custom credential getter.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
Identity
|
|
137
|
+
Dict containing information about the custom credential getter.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
source = getattr(
|
|
141
|
+
self._custom_get_credentials,
|
|
142
|
+
"__name__",
|
|
143
|
+
repr(self._custom_get_credentials),
|
|
144
|
+
)
|
|
145
|
+
return {"method": "custom", "source": repr(source)}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
__all__ = []
|
|
6
|
+
|
|
7
|
+
from . import core
|
|
8
|
+
from .core import IoTRefreshableSession
|
|
9
|
+
from .x509 import IOTX509RefreshableSession
|
|
10
|
+
|
|
11
|
+
__all__.extend(core.__all__)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
"""IoT refreshable session factory for selecting auth methods."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
__all__ = ["IoTRefreshableSession"]
|
|
10
|
+
|
|
11
|
+
from typing import get_args
|
|
12
|
+
|
|
13
|
+
from ...exceptions import BRSValidationError
|
|
14
|
+
from ...utils import (
|
|
15
|
+
BaseIoTRefreshableSession,
|
|
16
|
+
BaseRefreshableSession,
|
|
17
|
+
IoTAuthenticationMethod,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class IoTRefreshableSession(BaseRefreshableSession, registry_key="iot"):
|
|
22
|
+
def __new__(
|
|
23
|
+
cls,
|
|
24
|
+
authentication_method: IoTAuthenticationMethod = "x509",
|
|
25
|
+
**kwargs,
|
|
26
|
+
) -> BaseIoTRefreshableSession:
|
|
27
|
+
if authentication_method not in (
|
|
28
|
+
methods := cls.get_available_authentication_methods()
|
|
29
|
+
):
|
|
30
|
+
raise BRSValidationError(
|
|
31
|
+
f"{authentication_method!r} is an invalid authentication "
|
|
32
|
+
"method parameter. Available authentication methods are "
|
|
33
|
+
f"{', '.join(repr(meth) for meth in methods)}.",
|
|
34
|
+
param="authentication_method",
|
|
35
|
+
value=authentication_method,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return BaseIoTRefreshableSession.registry[authentication_method](
|
|
39
|
+
**kwargs
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def get_available_authentication_methods(cls) -> list[str]:
|
|
44
|
+
args = list(get_args(IoTAuthenticationMethod))
|
|
45
|
+
args.remove("__iot_sentinel__")
|
|
46
|
+
return args
|