blob: 019b908dae37f08e3c13c3a91bff18480d444d42 [file] [log] [blame]
arithmetic172882293fe2021-04-14 11:22:13 -07001# Copyright 2021 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Tests for the reauth module."""
16
17import base64
18import sys
19
20import mock
21import pytest
22import pyu2f
23
24from google.auth import exceptions
25from google.oauth2 import challenges
26
27
28def test_get_user_password():
29 with mock.patch("getpass.getpass", return_value="foo"):
30 assert challenges.get_user_password("") == "foo"
31
32
33def test_security_key():
34 metadata = {
35 "status": "READY",
36 "challengeId": 2,
37 "challengeType": "SECURITY_KEY",
38 "securityKey": {
39 "applicationId": "security_key_application_id",
40 "challenges": [
41 {
42 "keyHandle": "some_key",
43 "challenge": base64.urlsafe_b64encode(
44 "some_challenge".encode("ascii")
45 ).decode("ascii"),
46 }
47 ],
48 },
49 }
50 mock_key = mock.Mock()
51
52 challenge = challenges.SecurityKeyChallenge()
53
54 # Test the case that security key challenge is passed.
55 with mock.patch("pyu2f.model.RegisteredKey", return_value=mock_key):
56 with mock.patch(
57 "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate"
58 ) as mock_authenticate:
59 mock_authenticate.return_value = "security key response"
60 assert challenge.name == "SECURITY_KEY"
61 assert challenge.is_locally_eligible
62 assert challenge.obtain_challenge_input(metadata) == {
63 "securityKey": "security key response"
64 }
65 mock_authenticate.assert_called_with(
66 "security_key_application_id",
67 [{"key": mock_key, "challenge": b"some_challenge"}],
68 print_callback=sys.stderr.write,
69 )
70
71 # Test various types of exceptions.
72 with mock.patch("pyu2f.model.RegisteredKey", return_value=mock_key):
73 with mock.patch(
74 "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate"
75 ) as mock_authenticate:
76 mock_authenticate.side_effect = pyu2f.errors.U2FError(
77 pyu2f.errors.U2FError.DEVICE_INELIGIBLE
78 )
79 assert challenge.obtain_challenge_input(metadata) is None
80
81 with mock.patch(
82 "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate"
83 ) as mock_authenticate:
84 mock_authenticate.side_effect = pyu2f.errors.U2FError(
85 pyu2f.errors.U2FError.TIMEOUT
86 )
87 assert challenge.obtain_challenge_input(metadata) is None
88
89 with mock.patch(
90 "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate"
91 ) as mock_authenticate:
92 mock_authenticate.side_effect = pyu2f.errors.U2FError(
93 pyu2f.errors.U2FError.BAD_REQUEST
94 )
95 with pytest.raises(pyu2f.errors.U2FError):
96 challenge.obtain_challenge_input(metadata)
97
98 with mock.patch(
99 "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate"
100 ) as mock_authenticate:
101 mock_authenticate.side_effect = pyu2f.errors.NoDeviceFoundError()
102 assert challenge.obtain_challenge_input(metadata) is None
103
104 with mock.patch(
105 "pyu2f.convenience.authenticator.CompositeAuthenticator.Authenticate"
106 ) as mock_authenticate:
107 mock_authenticate.side_effect = pyu2f.errors.UnsupportedVersionException()
108 with pytest.raises(pyu2f.errors.UnsupportedVersionException):
109 challenge.obtain_challenge_input(metadata)
110
111 with mock.patch.dict("sys.modules"):
112 sys.modules["pyu2f"] = None
113 with pytest.raises(exceptions.ReauthFailError) as excinfo:
114 challenge.obtain_challenge_input(metadata)
115 assert excinfo.match(r"pyu2f dependency is required")
116
117
118@mock.patch("getpass.getpass", return_value="foo")
119def test_password_challenge(getpass_mock):
120 challenge = challenges.PasswordChallenge()
121
122 with mock.patch("getpass.getpass", return_value="foo"):
123 assert challenge.is_locally_eligible
124 assert challenge.name == "PASSWORD"
125 assert challenges.PasswordChallenge().obtain_challenge_input({}) == {
126 "credential": "foo"
127 }
128
129 with mock.patch("getpass.getpass", return_value=None):
130 assert challenges.PasswordChallenge().obtain_challenge_input({}) == {
131 "credential": " "
132 }