# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for the reauth module.""" import base64 import sys import mock import pytest import pyu2f from google.auth import exceptions from google.oauth2 import challenges def test_get_user_password(): with mock.patch("getpass.getpass", return_value="foo"): assert challenges.get_user_password("") == "foo" def test_security_key(): metadata = { "status": "READY", "challengeId": 2, "challengeType": "SECURITY_KEY", "securityKey": { "applicationId": "security_key_application_id", "challenges": [ { "keyHandle": "some_key", "challenge": base64.urlsafe_b64encode( "some_challenge".encode("ascii") ).decode("ascii"), } ], }, } mock_key = mock.Mock() challenge = challenges.SecurityKeyChallenge() # Test the case that security key challenge is passed. with mock.patch("pyu2f.model.RegisteredKey", return_value=mock_key): with mock.patch( "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate" ) as mock_authenticate: mock_authenticate.return_value = "security key response" assert challenge.name == "SECURITY_KEY" assert challenge.is_locally_eligible assert challenge.obtain_challenge_input(metadata) == { "securityKey": "security key response" } mock_authenticate.assert_called_with( "security_key_application_id", [{"key": mock_key, "challenge": b"some_challenge"}], print_callback=sys.stderr.write, ) # Test various types of exceptions. with mock.patch("pyu2f.model.RegisteredKey", return_value=mock_key): with mock.patch( "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate" ) as mock_authenticate: mock_authenticate.side_effect = pyu2f.errors.U2FError( pyu2f.errors.U2FError.DEVICE_INELIGIBLE ) assert challenge.obtain_challenge_input(metadata) is None with mock.patch( "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate" ) as mock_authenticate: mock_authenticate.side_effect = pyu2f.errors.U2FError( pyu2f.errors.U2FError.TIMEOUT ) assert challenge.obtain_challenge_input(metadata) is None with mock.patch( "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate" ) as mock_authenticate: mock_authenticate.side_effect = pyu2f.errors.U2FError( pyu2f.errors.U2FError.BAD_REQUEST ) with pytest.raises(pyu2f.errors.U2FError): challenge.obtain_challenge_input(metadata) with mock.patch( "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate" ) as mock_authenticate: mock_authenticate.side_effect = pyu2f.errors.NoDeviceFoundError() assert challenge.obtain_challenge_input(metadata) is None with mock.patch( "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate" ) as mock_authenticate: mock_authenticate.side_effect = pyu2f.errors.UnsupportedVersionException() with pytest.raises(pyu2f.errors.UnsupportedVersionException): challenge.obtain_challenge_input(metadata) with mock.patch.dict("sys.modules"): sys.modules["pyu2f"] = None with pytest.raises(exceptions.ReauthFailError) as excinfo: challenge.obtain_challenge_input(metadata) assert excinfo.match(r"pyu2f dependency is required") @mock.patch("getpass.getpass", return_value="foo") def test_password_challenge(getpass_mock): challenge = challenges.PasswordChallenge() with mock.patch("getpass.getpass", return_value="foo"): assert challenge.is_locally_eligible assert challenge.name == "PASSWORD" assert challenges.PasswordChallenge().obtain_challenge_input({}) == { "credential": "foo" } with mock.patch("getpass.getpass", return_value=None): assert challenges.PasswordChallenge().obtain_challenge_input({}) == { "credential": " " }