From 34a0c20d036240a3bdf5ec03221bc629427e2315 Mon Sep 17 00:00:00 2001 From: Matus Faro Date: Tue, 19 Dec 2023 09:27:30 -0500 Subject: [PATCH] Add cognitoidp.admin_respond_to_auth_challenge (#7136) --- IMPLEMENTATION_COVERAGE.md | 4 +-- docs/docs/services/cognito-idp.rst | 2 +- moto/cognitoidp/models.py | 38 ++++++++++++++++++++++++ moto/cognitoidp/responses.py | 11 +++++++ tests/test_cognitoidp/test_cognitoidp.py | 12 +++++--- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index ebcb2d39c..bd76a8b7c 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -1180,7 +1180,7 @@ ## cognito-idp
-59% implemented +60% implemented - [X] add_custom_attributes - [X] admin_add_user_to_group @@ -1201,7 +1201,7 @@ - [ ] admin_list_user_auth_events - [X] admin_remove_user_from_group - [X] admin_reset_user_password -- [ ] admin_respond_to_auth_challenge +- [X] admin_respond_to_auth_challenge - [X] admin_set_user_mfa_preference - [X] admin_set_user_password - [ ] admin_set_user_settings diff --git a/docs/docs/services/cognito-idp.rst b/docs/docs/services/cognito-idp.rst index b5021803d..3b487dc2f 100644 --- a/docs/docs/services/cognito-idp.rst +++ b/docs/docs/services/cognito-idp.rst @@ -46,7 +46,7 @@ cognito-idp - [ ] admin_list_user_auth_events - [X] admin_remove_user_from_group - [X] admin_reset_user_password -- [ ] admin_respond_to_auth_challenge +- [X] admin_respond_to_auth_challenge - [X] admin_set_user_mfa_preference - [X] admin_set_user_password - [ ] admin_set_user_settings diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py index a2cac85fd..7cb137803 100644 --- a/moto/cognitoidp/models.py +++ b/moto/cognitoidp/models.py @@ -1483,6 +1483,27 @@ class CognitoIdpBackend(BaseBackend): # We shouldn't get here due to enum validation of auth_flow return None # type: ignore[return-value] + def admin_respond_to_auth_challenge( + self, + session: str, + client_id: str, + challenge_name: str, + challenge_responses: Dict[str, str], + ) -> Dict[str, Any]: + """ + Responds to an authentication challenge, as an administrator. + + The only differences between this admin endpoint and public endpoint are not relevant and so we can safely call + the public endpoint to do the work: + - The admin endpoint requires a user pool id along with a session; the public endpoint searches across all pools + - ContextData is passed in; we don't use it + + ref: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminRespondToAuthChallenge.html + """ + return self.respond_to_auth_challenge( + session, client_id, challenge_name, challenge_responses + ) + def respond_to_auth_challenge( self, session: str, @@ -1490,6 +1511,11 @@ class CognitoIdpBackend(BaseBackend): challenge_name: str, challenge_responses: Dict[str, str], ) -> Dict[str, Any]: + """ + Responds to an authentication challenge, from public client. + + ref: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RespondToAuthChallenge.html + """ if challenge_name == "PASSWORD_VERIFIER": session = challenge_responses.get("PASSWORD_CLAIM_SECRET_BLOCK") # type: ignore[assignment] @@ -2180,6 +2206,18 @@ class RegionAgnosticBackend: backend = self._find_backend_by_access_token(access_token) return backend.get_user(access_token) + def admin_respond_to_auth_challenge( + self, + session: str, + client_id: str, + challenge_name: str, + challenge_responses: Dict[str, str], + ) -> Dict[str, Any]: + backend = self._find_backend_for_clientid(client_id) + return backend.admin_respond_to_auth_challenge( + session, client_id, challenge_name, challenge_responses + ) + def respond_to_auth_challenge( self, session: str, diff --git a/moto/cognitoidp/responses.py b/moto/cognitoidp/responses.py index dbe3ae509..adf4f2889 100644 --- a/moto/cognitoidp/responses.py +++ b/moto/cognitoidp/responses.py @@ -449,6 +449,17 @@ class CognitoIdpResponse(BaseResponse): return json.dumps(auth_result) + def admin_respond_to_auth_challenge(self) -> str: + session = self._get_param("Session") + client_id = self._get_param("ClientId") + challenge_name = self._get_param("ChallengeName") + challenge_responses = self._get_param("ChallengeResponses") + auth_result = region_agnostic_backend.admin_respond_to_auth_challenge( + session, client_id, challenge_name, challenge_responses + ) + + return json.dumps(auth_result) + def respond_to_auth_challenge(self) -> str: session = self._get_param("Session") client_id = self._get_param("ClientId") diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py index d7abd1beb..9b5b1db1f 100644 --- a/tests/test_cognitoidp/test_cognitoidp.py +++ b/tests/test_cognitoidp/test_cognitoidp.py @@ -1532,7 +1532,8 @@ def test_group_in_access_token(): # This sets a new password and logs the user in (creates tokens) new_password = "P2$Sword" - result = conn.respond_to_auth_challenge( + result = conn.admin_respond_to_auth_challenge( + UserPoolId=user_pool_id, Session=result["Session"], ClientId=client_id, ChallengeName="NEW_PASSWORD_REQUIRED", @@ -1585,7 +1586,8 @@ def test_group_in_id_token(): # This sets a new password and logs the user in (creates tokens) new_password = "P2$Sword" - result = conn.respond_to_auth_challenge( + result = conn.admin_respond_to_auth_challenge( + UserPoolId=user_pool_id, Session=result["Session"], ClientId=client_id, ChallengeName="NEW_PASSWORD_REQUIRED", @@ -2749,7 +2751,8 @@ def authentication_flow(conn, auth_flow): # This sets a new password and logs the user in (creates tokens) new_password = "P2$Sword" - result = conn.respond_to_auth_challenge( + result = conn.admin_respond_to_auth_challenge( + UserPoolId=user_pool_id, Session=result["Session"], ClientId=client_id, ChallengeName="NEW_PASSWORD_REQUIRED", @@ -4388,7 +4391,8 @@ def test_admin_initiate_auth_when_token_totp_enabled(): assert result["Session"] != "" # Respond to challenge with TOTP - result = conn.respond_to_auth_challenge( + result = conn.admin_respond_to_auth_challenge( + UserPoolId=user_pool_id, ClientId=client_id, ChallengeName="SOFTWARE_TOKEN_MFA", Session=result["Session"],