pyheltycloud 0.2.0__tar.gz → 0.2.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.
- {pyheltycloud-0.2.0/src/pyheltycloud.egg-info → pyheltycloud-0.2.1}/PKG-INFO +1 -1
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/pyproject.toml +1 -1
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/auth.py +18 -2
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1/src/pyheltycloud.egg-info}/PKG-INFO +1 -1
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/tests/test_auth_flow.py +32 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/LICENSE +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/README.md +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/setup.cfg +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/__init__.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/client.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/const.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/exceptions.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/models.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud/py.typed +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud.egg-info/SOURCES.txt +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud.egg-info/dependency_links.txt +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud.egg-info/requires.txt +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/src/pyheltycloud.egg-info/top_level.txt +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/tests/test_auth.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/tests/test_client.py +0 -0
- {pyheltycloud-0.2.0 → pyheltycloud-0.2.1}/tests/test_models.py +0 -0
|
@@ -26,6 +26,19 @@ import aiohttp
|
|
|
26
26
|
|
|
27
27
|
from .exceptions import HeltyCloudAuthError, HeltyCloudConnectionError
|
|
28
28
|
|
|
29
|
+
#: Cognito errors that mean the credentials themselves were refused. Anything
|
|
30
|
+
#: else it answers with, like throttling or its own internal errors, is a
|
|
31
|
+
#: passing failure: reporting it as bad credentials would send the user to
|
|
32
|
+
#: re-enter a password that is still right.
|
|
33
|
+
CREDENTIAL_ERRORS = frozenset(
|
|
34
|
+
{
|
|
35
|
+
"NotAuthorizedException",
|
|
36
|
+
"PasswordResetRequiredException",
|
|
37
|
+
"UserNotConfirmedException",
|
|
38
|
+
"UserNotFoundException",
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
29
42
|
# 3072-bit prime and generator, fixed by the Cognito protocol.
|
|
30
43
|
N_HEX = (
|
|
31
44
|
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74"
|
|
@@ -195,8 +208,11 @@ class CognitoSrpAuth:
|
|
|
195
208
|
raise HeltyCloudConnectionError(f"{target}: unexpected answer {text[:200]}")
|
|
196
209
|
|
|
197
210
|
if status != 200:
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
error_type = str(body.get("__type", "")).rpartition("#")[2]
|
|
212
|
+
message = body.get("message") or error_type or str(body)
|
|
213
|
+
if error_type in CREDENTIAL_ERRORS:
|
|
214
|
+
raise HeltyCloudAuthError(f"{target}: {message}")
|
|
215
|
+
raise HeltyCloudConnectionError(f"{target}: {message}")
|
|
200
216
|
return body
|
|
201
217
|
|
|
202
218
|
def _password_key(self, username: str, password: str, srp_b_hex: str, salt_hex: str) -> bytes:
|
|
@@ -94,6 +94,38 @@ async def test_wrong_password_is_auth_error(auth: CognitoSrpAuth, cloud: FakeClo
|
|
|
94
94
|
await auth.authenticate("user@example.com", "wrong")
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
@pytest.mark.parametrize(
|
|
98
|
+
("status", "error_type"),
|
|
99
|
+
[
|
|
100
|
+
(400, "TooManyRequestsException"),
|
|
101
|
+
(400, "LimitExceededException"),
|
|
102
|
+
(500, "InternalErrorException"),
|
|
103
|
+
(500, "com.amazonaws.cognito#InternalErrorException"),
|
|
104
|
+
],
|
|
105
|
+
)
|
|
106
|
+
async def test_passing_cognito_failure_is_not_an_auth_error(
|
|
107
|
+
auth: CognitoSrpAuth, cloud: FakeCloud, status: int, error_type: str
|
|
108
|
+
) -> None:
|
|
109
|
+
"""Throttling or an outage must not look like a wrong password."""
|
|
110
|
+
cloud.reply_sequence(
|
|
111
|
+
COGNITO_PATH, [(200, CHALLENGE), (status, {"__type": error_type, "message": "busy"})]
|
|
112
|
+
)
|
|
113
|
+
with pytest.raises(HeltyCloudConnectionError, match="busy"):
|
|
114
|
+
await auth.authenticate("user@example.com", "secret")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.parametrize(
|
|
118
|
+
"error_type",
|
|
119
|
+
["UserNotFoundException", "PasswordResetRequiredException", "UserNotConfirmedException"],
|
|
120
|
+
)
|
|
121
|
+
async def test_refused_account_is_auth_error(
|
|
122
|
+
auth: CognitoSrpAuth, cloud: FakeCloud, error_type: str
|
|
123
|
+
) -> None:
|
|
124
|
+
cloud.reply(COGNITO_PATH, {"__type": error_type, "message": "refused"}, status=400)
|
|
125
|
+
with pytest.raises(HeltyCloudAuthError, match="refused"):
|
|
126
|
+
await auth.authenticate("user@example.com", "secret")
|
|
127
|
+
|
|
128
|
+
|
|
97
129
|
async def test_unexpected_challenge(auth: CognitoSrpAuth, cloud: FakeCloud) -> None:
|
|
98
130
|
"""Should they ever enable MFA, better a clear error."""
|
|
99
131
|
cloud.reply(COGNITO_PATH, {"ChallengeName": "SMS_MFA", "ChallengeParameters": {}})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|