Skip to content

Commit d90b13b

Browse files
authored
Merge pull request #23 from stax-labs/feat/auth_errors
feat(auth) convert boto error's into staxapp errors
2 parents feecb9a + 50798b4 commit d90b13b

4 files changed

Lines changed: 157 additions & 22 deletions

File tree

staxapp/auth.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
from aws_requests_auth.aws_auth import AWSRequestsAuth
99
from botocore import UNSIGNED
1010
from botocore.client import Config as BotoConfig
11+
from botocore.exceptions import ClientError
1112
from warrant import AWSSRP, Cognito
1213

1314
from staxapp.config import Config as JumaConfig
15+
from staxapp.exceptions import InvalidCredentialsException
1416

1517

1618
class StaxAuth:
@@ -23,6 +25,15 @@ def __init__(self, config_branch):
2325
self.aws_region = config.get(config_branch).get("region")
2426

2527
def requests_auth(self, username, password):
28+
if username is None:
29+
raise InvalidCredentialsException(
30+
"Please provide an Access Key to your config"
31+
)
32+
if password is None:
33+
raise InvalidCredentialsException(
34+
"Please provide a Secret Key to your config"
35+
)
36+
2637
id_token = self.id_token_from_cognito(username, password)
2738
id_creds = self.sts_from_cognito_identity_pool(id_token)
2839
auth = self.sigv4_signed_auth_headers(id_creds)
@@ -32,26 +43,39 @@ def requests_auth(self, username, password):
3243

3344
return JumaConfig.auth
3445

35-
def id_token_from_cognito(self, username=None, password=None):
46+
def id_token_from_cognito(self, username=None, password=None, client=None):
3647
token = None
3748
if username and password:
38-
client = boto3.client(
39-
"cognito-idp",
40-
region_name=self.aws_region,
41-
config=BotoConfig(signature_version=UNSIGNED),
42-
)
49+
if not client:
50+
client = boto3.client(
51+
"cognito-idp",
52+
region_name=self.aws_region,
53+
config=BotoConfig(signature_version=UNSIGNED),
54+
)
4355
aws = AWSSRP(
4456
username=username,
4557
password=password,
4658
pool_id=self.user_pool,
4759
client_id=self.client_id,
4860
client=client,
4961
)
50-
tokens = aws.authenticate_user()
51-
# logging.debug(f"TOKEN: {tokens}")
62+
try:
63+
tokens = aws.authenticate_user()
64+
except ClientError as e:
65+
if e.response["Error"]["Code"] == "UserNotFoundException":
66+
raise InvalidCredentialsException(
67+
message=str(e), detail="Please check your Secret Key is correct"
68+
)
69+
elif e.response["Error"]["Code"] == "NotAuthorizedException":
70+
raise InvalidCredentialsException(
71+
message=str(e),
72+
detail="Please check your Access Key, that you have created your Api Token and that you are using the right STAX REGION",
73+
)
74+
else:
75+
raise InvalidCredentialsException(
76+
f"Unexpected Client Error. Error details: {e}"
77+
)
5278
token = tokens["AuthenticationResult"]["IdToken"]
53-
else:
54-
token = jwt.encode({"sub": "unittest"}, "secret", algorithm="HS256")
5579
return token
5680

5781
def sts_from_cognito_identity_pool(self, token, cognito_client=None):

staxapp/exceptions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,15 @@ class ValidationException(Exception):
2626
def __init__(self, message):
2727
# logging.info(f"VALIDATE: {message}")
2828
self.message = message
29+
30+
31+
class InvalidCredentialsException(Exception):
32+
def __init__(self, message, detail=""):
33+
logging.error(message)
34+
prefix = f"InvalidCredentialsException: "
35+
if detail:
36+
prefix = f"{prefix}{detail} - "
37+
self.message = f"{prefix}{message}"
38+
39+
def __str__(self):
40+
return self.message

tests/test_api.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ def testFailedApiException(self):
209209
)
210210
with self.assertRaises(ApiException):
211211
self.Api.get("/test/no/error")
212-
213-
# Test an exception which has no json in response
212+
213+
# Test an exception which has no json in response
214214
responses.add(
215215
responses.GET,
216216
f"{Config.api_base_url()}/test/invalid/json",
@@ -219,16 +219,14 @@ def testFailedApiException(self):
219219
)
220220
with self.assertRaises(ApiException):
221221
self.Api.get("/test/invalid/json")
222-
222+
223223
# Test an exception with no content
224224
responses.add(
225-
responses.GET,
226-
f"{Config.api_base_url()}/test/no/content",
227-
status=500,
225+
responses.GET, f"{Config.api_base_url()}/test/no/content", status=500,
228226
)
229227
with self.assertRaises(ApiException):
230228
self.Api.get("/test/no/content")
231-
229+
232230

233231
if __name__ == "__main__":
234232
unittest.main()

tests/test_auth.py

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212

1313
from botocore import UNSIGNED
1414
from botocore.client import Config as BotoConfig
15-
from botocore.stub import Stubber
15+
from botocore.stub import Stubber, ANY
1616

1717
from staxapp.auth import StaxAuth
18+
from staxapp.exceptions import InvalidCredentialsException
1819

1920

2021
class StaxAuthTests(unittest.TestCase):
@@ -30,8 +31,16 @@ def setUp(self):
3031
)
3132
self.cognito_stub = Stubber(self.cognito_client)
3233

34+
self.aws_srp_client = botocore.session.get_session().create_client(
35+
"cognito-idp",
36+
region_name="ap-southeast-2",
37+
config=BotoConfig(signature_version=UNSIGNED),
38+
)
39+
self.aws_srp_stubber = Stubber(self.aws_srp_client)
40+
3341
def tearDown(self):
3442
self.cognito_stub.deactivate()
43+
self.aws_srp_stubber.deactivate()
3544

3645
def testStaxAuthInit(self):
3746
"""
@@ -45,16 +54,58 @@ def testToken(self):
4554
Test valid JWT is returned
4655
"""
4756
sa = StaxAuth("ApiAuth")
48-
token = sa.id_token_from_cognito()
49-
jwt_token = jwt.decode(token, verify=False)
50-
self.assertIn("sub", jwt_token)
57+
self.stub_aws_srp(sa, "valid_username")
58+
token = sa.id_token_from_cognito(
59+
username="valid_username", password="correct", client=self.aws_srp_client
60+
)
61+
self.assertEqual(token, "valid_token")
62+
63+
def testCredentialErrors(self):
64+
"""
65+
Test that boto errors are caught and converted to InvalidCredentialExceptions
66+
"""
67+
68+
sa = StaxAuth("ApiAuth")
69+
# Test with invalid username password
70+
self.stub_aws_srp(sa, "bad_password", "UserNotFoundException")
71+
user_not_found_success = False
72+
try:
73+
sa.id_token_from_cognito(
74+
username="bad_password", password="wrong", client=self.aws_srp_client
75+
)
76+
except InvalidCredentialsException as e:
77+
self.assertIn("Please check your Secret Key is correct", e.message)
78+
user_not_found_success = True
79+
self.assertTrue(user_not_found_success)
80+
81+
# Test with no access
82+
self.stub_aws_srp(sa, "no_access", "NotAuthorizedException")
83+
no_access_success = False
84+
try:
85+
sa.id_token_from_cognito(
86+
username="no_access", password="wrong", client=self.aws_srp_client
87+
)
88+
except InvalidCredentialsException as e:
89+
self.assertIn(
90+
"Please check your Access Key, that you have created your Api Token and that you are using the right STAX REGION",
91+
e.message,
92+
)
93+
no_access_success = True
94+
self.assertTrue(no_access_success)
95+
96+
# Test Unknown Error
97+
self.stub_aws_srp(sa, "Unknown", "UnitTesting")
98+
with self.assertRaises(InvalidCredentialsException):
99+
sa.id_token_from_cognito(
100+
username="Unknown", password="wrong", client=self.aws_srp_client
101+
)
51102

52103
def testCreds(self):
53104
"""
54105
Test valid credentials are returned
55106
"""
56107
sa = StaxAuth("ApiAuth")
57-
token = sa.id_token_from_cognito()
108+
token = jwt.encode({"sub": "unittest"}, "secret", algorithm="HS256")
58109
jwt_token = jwt.decode(token, verify=False)
59110
self.stub_cognito_creds(jwt_token.get("sub"))
60111
creds = sa.sts_from_cognito_identity_pool(
@@ -63,6 +114,56 @@ def testCreds(self):
63114
self.assertIn("Credentials", creds)
64115
self.assertTrue(creds.get("IdentityId").startswith("ap-southeast-2"))
65116

117+
def testAuthErrors(self):
118+
"""
119+
Test that errors are thrown when keys are invalid
120+
"""
121+
sa = StaxAuth("ApiAuth")
122+
# Test with no username
123+
with self.assertRaises(InvalidCredentialsException):
124+
sa.requests_auth(username=None, password="valid")
125+
126+
# Test with no username
127+
with self.assertRaises(InvalidCredentialsException):
128+
sa.requests_auth(username="valid", password=None)
129+
130+
def stub_aws_srp(self, stax_auth, username, error_code=None):
131+
expected_parameters = {
132+
"AuthFlow": "USER_SRP_AUTH",
133+
"AuthParameters": {"SRP_A": ANY, "USERNAME": username},
134+
"ClientId": stax_auth.client_id,
135+
}
136+
if error_code:
137+
self.aws_srp_stubber.add_client_error(
138+
"initiate_auth",
139+
service_error_code=error_code,
140+
expected_params=expected_parameters,
141+
)
142+
else:
143+
self.aws_srp_stubber.add_response(
144+
"initiate_auth",
145+
{
146+
"ChallengeParameters": {
147+
"USER_ID_FOR_SRP": "user",
148+
"SALT": "4",
149+
"SRP_B": "5",
150+
"SECRET_BLOCK": "secblock",
151+
},
152+
"ChallengeName": "PASSWORD_VERIFIER",
153+
},
154+
expected_parameters,
155+
)
156+
self.aws_srp_stubber.add_response(
157+
"respond_to_auth_challenge",
158+
{"AuthenticationResult": {"IdToken": "valid_token"},},
159+
{
160+
"ClientId": stax_auth.client_id,
161+
"ChallengeName": ANY,
162+
"ChallengeResponses": ANY,
163+
},
164+
)
165+
self.aws_srp_stubber.activate()
166+
66167
def stub_cognito_creds(self, token: str):
67168
sa = StaxAuth("ApiAuth")
68169

0 commit comments

Comments
 (0)