blob: c415a1f44a38ad6186fe76d104ebe6311f5b72e2 [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"},
99 body="test=params",
100 )
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(
115 {"error": "internal_failure", "error_description": "internal_failure"},
116 status=http_client.BAD_REQUEST,
117 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700118
119 with pytest.raises(exceptions.RefreshError):
120 _client._token_endpoint_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700121 request,
122 "http://example.com",
123 {"error": "internal_failure", "error_description": "internal_failure"},
124 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700125
126
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700127def verify_request_params(request, params):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700128 request_body = request.call_args[1]["body"]
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700129 request_params = urllib.parse.parse_qs(request_body)
130
131 for key, value in six.iteritems(params):
132 assert request_params[key][0] == value
133
134
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700135@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700136def test_jwt_grant(utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700137 request = make_request(
138 {"access_token": "token", "expires_in": 500, "extra": "data"}
139 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700140
141 token, expiry, extra_data = _client.jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700142 request, "http://example.com", "assertion_value"
143 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700144
145 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700146 verify_request_params(
147 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
148 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700149
150 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700151 assert token == "token"
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700152 assert expiry == utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700153 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700154
155
156def test_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700157 request = make_request(
158 {
159 # No access token.
160 "expires_in": 500,
161 "extra": "data",
162 }
163 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700164
165 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700166 _client.jwt_grant(request, "http://example.com", "assertion_value")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700167
168
Christophe Tatonb649b432018-02-08 14:12:23 -0800169def test_id_token_jwt_grant():
170 now = _helpers.utcnow()
171 id_token_expiry = _helpers.datetime_to_secs(now)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700172 id_token = jwt.encode(SIGNER, {"exp": id_token_expiry}).decode("utf-8")
173 request = make_request({"id_token": id_token, "extra": "data"})
Christophe Tatonb649b432018-02-08 14:12:23 -0800174
175 token, expiry, extra_data = _client.id_token_jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700176 request, "http://example.com", "assertion_value"
177 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800178
179 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700180 verify_request_params(
181 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
182 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800183
184 # Check result
185 assert token == id_token
186 # JWT does not store microseconds
187 now = now.replace(microsecond=0)
188 assert expiry == now
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700189 assert extra_data["extra"] == "data"
Christophe Tatonb649b432018-02-08 14:12:23 -0800190
191
192def test_id_token_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700193 request = make_request(
194 {
195 # No access token.
196 "expires_in": 500,
197 "extra": "data",
198 }
199 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800200
201 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700202 _client.id_token_jwt_grant(request, "http://example.com", "assertion_value")
Christophe Tatonb649b432018-02-08 14:12:23 -0800203
204
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700205@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700206def test_refresh_grant(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700207 request = make_request(
208 {
209 "access_token": "token",
210 "refresh_token": "new_refresh_token",
211 "expires_in": 500,
212 "extra": "data",
213 }
214 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700215
216 token, refresh_token, expiry, extra_data = _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700217 request, "http://example.com", "refresh_token", "client_id", "client_secret"
218 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700219
220 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700221 verify_request_params(
222 request,
223 {
224 "grant_type": _client._REFRESH_GRANT_TYPE,
225 "refresh_token": "refresh_token",
226 "client_id": "client_id",
227 "client_secret": "client_secret",
228 },
229 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700230
231 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700232 assert token == "token"
233 assert refresh_token == "new_refresh_token"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700234 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700235 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700236
237
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700238@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400239def test_refresh_grant_with_scopes(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700240 request = make_request(
241 {
242 "access_token": "token",
243 "refresh_token": "new_refresh_token",
244 "expires_in": 500,
245 "extra": "data",
246 "scope": SCOPES_AS_STRING,
247 }
248 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400249
250 token, refresh_token, expiry, extra_data = _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700251 request,
252 "http://example.com",
253 "refresh_token",
254 "client_id",
255 "client_secret",
256 SCOPES_AS_LIST,
257 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400258
259 # Check request call.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700260 verify_request_params(
261 request,
262 {
263 "grant_type": _client._REFRESH_GRANT_TYPE,
264 "refresh_token": "refresh_token",
265 "client_id": "client_id",
266 "client_secret": "client_secret",
267 "scope": SCOPES_AS_STRING,
268 },
269 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400270
271 # Check result.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700272 assert token == "token"
273 assert refresh_token == "new_refresh_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400274 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700275 assert extra_data["extra"] == "data"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400276
277
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700278def test_refresh_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700279 request = make_request(
280 {
281 # No access token.
282 "refresh_token": "new_refresh_token",
283 "expires_in": 500,
284 "extra": "data",
285 }
286 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700287
288 with pytest.raises(exceptions.RefreshError):
289 _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700290 request, "http://example.com", "refresh_token", "client_id", "client_secret"
291 )