blob: 052390aa81131e4f60331bb136a36f48daafc749 [file] [log] [blame]
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -07001# Copyright 2016 Google Inc.
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
15import datetime
16import json
Christophe Tatonb649b432018-02-08 14:12:23 -080017import os
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070018
19import mock
20import pytest
21import six
22from six.moves import http_client
23from six.moves import urllib
24
Christophe Tatonb649b432018-02-08 14:12:23 -080025from google.auth import _helpers
26from google.auth import crypt
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070027from google.auth import exceptions
Christophe Tatonb649b432018-02-08 14:12:23 -080028from google.auth import jwt
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070029from google.auth import transport
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070030from google.oauth2 import _client
31
32
Bu Sun Kim9eec0912019-10-21 17:04:21 -070033DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
Christophe Tatonb649b432018-02-08 14:12:23 -080034
Bu Sun Kim9eec0912019-10-21 17:04:21 -070035with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:
Christophe Tatonb649b432018-02-08 14:12:23 -080036 PRIVATE_KEY_BYTES = fh.read()
37
Bu Sun Kim9eec0912019-10-21 17:04:21 -070038SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
Christophe Tatonb649b432018-02-08 14:12:23 -080039
Bu Sun Kim9eec0912019-10-21 17:04:21 -070040SCOPES_AS_LIST = [
41 "https://www.googleapis.com/auth/pubsub",
42 "https://www.googleapis.com/auth/logging.write",
43]
44SCOPES_AS_STRING = (
45 "https://www.googleapis.com/auth/pubsub"
46 " https://www.googleapis.com/auth/logging.write"
47)
Eugene W. Foley49a18c42019-05-22 13:50:38 -040048
Christophe Tatonb649b432018-02-08 14:12:23 -080049
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070050def test__handle_error_response():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070051 response_data = json.dumps({"error": "help", "error_description": "I'm alive"})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070052
53 with pytest.raises(exceptions.RefreshError) as excinfo:
54 _client._handle_error_response(response_data)
55
Bu Sun Kim9eec0912019-10-21 17:04:21 -070056 assert excinfo.match(r"help: I\'m alive")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070057
58
59def test__handle_error_response_non_json():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070060 response_data = "Help, I'm alive"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070061
62 with pytest.raises(exceptions.RefreshError) as excinfo:
63 _client._handle_error_response(response_data)
64
Bu Sun Kim9eec0912019-10-21 17:04:21 -070065 assert excinfo.match(r"Help, I\'m alive")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070066
67
Bu Sun Kim9eec0912019-10-21 17:04:21 -070068@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070069def test__parse_expiry(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070070 result = _client._parse_expiry({"expires_in": 500})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070071 assert result == datetime.datetime.min + datetime.timedelta(seconds=500)
72
73
74def test__parse_expiry_none():
75 assert _client._parse_expiry({}) is None
76
77
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070078def make_request(response_data, status=http_client.OK):
79 response = mock.create_autospec(transport.Response, instance=True)
80 response.status = status
Bu Sun Kim9eec0912019-10-21 17:04:21 -070081 response.data = json.dumps(response_data).encode("utf-8")
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070082 request = mock.create_autospec(transport.Request)
83 request.return_value = response
84 return request
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070085
86
87def test__token_endpoint_request():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070088 request = make_request({"test": "response"})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070089
90 result = _client._token_endpoint_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070091 request, "http://example.com", {"test": "params"}
92 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070093
94 # Check request call
95 request.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070096 method="POST",
97 url="http://example.com",
98 headers={"content-type": "application/x-www-form-urlencoded"},
Bu Sun Kima57a7702020-01-10 13:17:34 -080099 body="test=params".encode("utf-8"),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700100 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700101
102 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700103 assert result == {"test": "response"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700104
105
106def test__token_endpoint_request_error():
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700107 request = make_request({}, status=http_client.BAD_REQUEST)
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700108
109 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700110 _client._token_endpoint_request(request, "http://example.com", {})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700111
112
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700113def test__token_endpoint_request_internal_failure_error():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700114 request = make_request(
Georgy Savva46bb58e2019-11-13 22:21:57 +0300115 {"error_description": "internal_failure"}, status=http_client.BAD_REQUEST
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700116 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700117
118 with pytest.raises(exceptions.RefreshError):
119 _client._token_endpoint_request(
Georgy Savva46bb58e2019-11-13 22:21:57 +0300120 request, "http://example.com", {"error_description": "internal_failure"}
121 )
122
123 request = make_request(
124 {"error": "internal_failure"}, status=http_client.BAD_REQUEST
125 )
126
127 with pytest.raises(exceptions.RefreshError):
128 _client._token_endpoint_request(
129 request, "http://example.com", {"error": "internal_failure"}
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700130 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700131
132
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700133def verify_request_params(request, params):
Bu Sun Kima57a7702020-01-10 13:17:34 -0800134 request_body = request.call_args[1]["body"].decode("utf-8")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700135 request_params = urllib.parse.parse_qs(request_body)
136
137 for key, value in six.iteritems(params):
138 assert request_params[key][0] == value
139
140
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700141@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700142def test_jwt_grant(utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700143 request = make_request(
144 {"access_token": "token", "expires_in": 500, "extra": "data"}
145 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700146
147 token, expiry, extra_data = _client.jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700148 request, "http://example.com", "assertion_value"
149 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700150
151 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700152 verify_request_params(
153 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
154 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700155
156 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700157 assert token == "token"
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700158 assert expiry == utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700159 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700160
161
162def test_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700163 request = make_request(
164 {
165 # No access token.
166 "expires_in": 500,
167 "extra": "data",
168 }
169 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700170
171 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700172 _client.jwt_grant(request, "http://example.com", "assertion_value")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700173
174
Christophe Tatonb649b432018-02-08 14:12:23 -0800175def test_id_token_jwt_grant():
176 now = _helpers.utcnow()
177 id_token_expiry = _helpers.datetime_to_secs(now)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700178 id_token = jwt.encode(SIGNER, {"exp": id_token_expiry}).decode("utf-8")
179 request = make_request({"id_token": id_token, "extra": "data"})
Christophe Tatonb649b432018-02-08 14:12:23 -0800180
181 token, expiry, extra_data = _client.id_token_jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700182 request, "http://example.com", "assertion_value"
183 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800184
185 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700186 verify_request_params(
187 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
188 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800189
190 # Check result
191 assert token == id_token
192 # JWT does not store microseconds
193 now = now.replace(microsecond=0)
194 assert expiry == now
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700195 assert extra_data["extra"] == "data"
Christophe Tatonb649b432018-02-08 14:12:23 -0800196
197
198def test_id_token_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700199 request = make_request(
200 {
201 # No access token.
202 "expires_in": 500,
203 "extra": "data",
204 }
205 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800206
207 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700208 _client.id_token_jwt_grant(request, "http://example.com", "assertion_value")
Christophe Tatonb649b432018-02-08 14:12:23 -0800209
210
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700211@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700212def test_refresh_grant(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700213 request = make_request(
214 {
215 "access_token": "token",
216 "refresh_token": "new_refresh_token",
217 "expires_in": 500,
218 "extra": "data",
219 }
220 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700221
222 token, refresh_token, expiry, extra_data = _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700223 request, "http://example.com", "refresh_token", "client_id", "client_secret"
224 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700225
226 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700227 verify_request_params(
228 request,
229 {
230 "grant_type": _client._REFRESH_GRANT_TYPE,
231 "refresh_token": "refresh_token",
232 "client_id": "client_id",
233 "client_secret": "client_secret",
234 },
235 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700236
237 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700238 assert token == "token"
239 assert refresh_token == "new_refresh_token"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700240 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700241 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700242
243
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700244@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400245def test_refresh_grant_with_scopes(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700246 request = make_request(
247 {
248 "access_token": "token",
249 "refresh_token": "new_refresh_token",
250 "expires_in": 500,
251 "extra": "data",
252 "scope": SCOPES_AS_STRING,
253 }
254 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400255
256 token, refresh_token, expiry, extra_data = _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700257 request,
258 "http://example.com",
259 "refresh_token",
260 "client_id",
261 "client_secret",
262 SCOPES_AS_LIST,
263 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400264
265 # Check request call.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700266 verify_request_params(
267 request,
268 {
269 "grant_type": _client._REFRESH_GRANT_TYPE,
270 "refresh_token": "refresh_token",
271 "client_id": "client_id",
272 "client_secret": "client_secret",
273 "scope": SCOPES_AS_STRING,
274 },
275 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400276
277 # Check result.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700278 assert token == "token"
279 assert refresh_token == "new_refresh_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400280 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700281 assert extra_data["extra"] == "data"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400282
283
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700284def test_refresh_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700285 request = make_request(
286 {
287 # No access token.
288 "refresh_token": "new_refresh_token",
289 "expires_in": 500,
290 "extra": "data",
291 }
292 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700293
294 with pytest.raises(exceptions.RefreshError):
295 _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700296 request, "http://example.com", "refresh_token", "client_id", "client_secret"
297 )