blob: 5a4a567456c1c2389ee256d12ba081584d903c84 [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
Christophe Tatonb649b432018-02-08 14:12:23 -080033DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data')
34
35with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh:
36 PRIVATE_KEY_BYTES = fh.read()
37
38SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1')
39
Eugene W. Foley49a18c42019-05-22 13:50:38 -040040SCOPES_AS_LIST = ['https://www.googleapis.com/auth/pubsub',
41 'https://www.googleapis.com/auth/logging.write']
42SCOPES_AS_STRING = ('https://www.googleapis.com/auth/pubsub'
43 ' https://www.googleapis.com/auth/logging.write')
44
Christophe Tatonb649b432018-02-08 14:12:23 -080045
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070046def test__handle_error_response():
47 response_data = json.dumps({
48 'error': 'help',
49 'error_description': 'I\'m alive'})
50
51 with pytest.raises(exceptions.RefreshError) as excinfo:
52 _client._handle_error_response(response_data)
53
54 assert excinfo.match(r'help: I\'m alive')
55
56
57def test__handle_error_response_non_json():
58 response_data = 'Help, I\'m alive'
59
60 with pytest.raises(exceptions.RefreshError) as excinfo:
61 _client._handle_error_response(response_data)
62
63 assert excinfo.match(r'Help, I\'m alive')
64
65
66@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070067def test__parse_expiry(unused_utcnow):
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070068 result = _client._parse_expiry({'expires_in': 500})
69 assert result == datetime.datetime.min + datetime.timedelta(seconds=500)
70
71
72def test__parse_expiry_none():
73 assert _client._parse_expiry({}) is None
74
75
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070076def make_request(response_data, status=http_client.OK):
77 response = mock.create_autospec(transport.Response, instance=True)
78 response.status = status
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070079 response.data = json.dumps(response_data).encode('utf-8')
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070080 request = mock.create_autospec(transport.Request)
81 request.return_value = response
82 return request
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070083
84
85def test__token_endpoint_request():
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070086 request = make_request({'test': 'response'})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070087
88 result = _client._token_endpoint_request(
89 request, 'http://example.com', {'test': 'params'})
90
91 # Check request call
92 request.assert_called_with(
93 method='POST',
94 url='http://example.com',
95 headers={'content-type': 'application/x-www-form-urlencoded'},
96 body='test=params')
97
98 # Check result
99 assert result == {'test': 'response'}
100
101
102def test__token_endpoint_request_error():
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700103 request = make_request({}, status=http_client.BAD_REQUEST)
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700104
105 with pytest.raises(exceptions.RefreshError):
106 _client._token_endpoint_request(request, 'http://example.com', {})
107
108
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700109def verify_request_params(request, params):
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700110 request_body = request.call_args[1]['body']
111 request_params = urllib.parse.parse_qs(request_body)
112
113 for key, value in six.iteritems(params):
114 assert request_params[key][0] == value
115
116
117@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700118def test_jwt_grant(utcnow):
119 request = make_request({
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700120 'access_token': 'token',
121 'expires_in': 500,
122 'extra': 'data'})
123
124 token, expiry, extra_data = _client.jwt_grant(
125 request, 'http://example.com', 'assertion_value')
126
127 # Check request call
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700128 verify_request_params(request, {
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700129 'grant_type': _client._JWT_GRANT_TYPE,
130 'assertion': 'assertion_value'
131 })
132
133 # Check result
134 assert token == 'token'
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700135 assert expiry == utcnow() + datetime.timedelta(seconds=500)
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700136 assert extra_data['extra'] == 'data'
137
138
139def test_jwt_grant_no_access_token():
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700140 request = make_request({
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700141 # No access token.
142 'expires_in': 500,
143 'extra': 'data'})
144
145 with pytest.raises(exceptions.RefreshError):
146 _client.jwt_grant(request, 'http://example.com', 'assertion_value')
147
148
Christophe Tatonb649b432018-02-08 14:12:23 -0800149def test_id_token_jwt_grant():
150 now = _helpers.utcnow()
151 id_token_expiry = _helpers.datetime_to_secs(now)
152 id_token = jwt.encode(SIGNER, {'exp': id_token_expiry}).decode('utf-8')
153 request = make_request({
154 'id_token': id_token,
155 'extra': 'data'})
156
157 token, expiry, extra_data = _client.id_token_jwt_grant(
158 request, 'http://example.com', 'assertion_value')
159
160 # Check request call
161 verify_request_params(request, {
162 'grant_type': _client._JWT_GRANT_TYPE,
163 'assertion': 'assertion_value'
164 })
165
166 # Check result
167 assert token == id_token
168 # JWT does not store microseconds
169 now = now.replace(microsecond=0)
170 assert expiry == now
171 assert extra_data['extra'] == 'data'
172
173
174def test_id_token_jwt_grant_no_access_token():
175 request = make_request({
176 # No access token.
177 'expires_in': 500,
178 'extra': 'data'})
179
180 with pytest.raises(exceptions.RefreshError):
181 _client.id_token_jwt_grant(
182 request, 'http://example.com', 'assertion_value')
183
184
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700185@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700186def test_refresh_grant(unused_utcnow):
187 request = make_request({
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700188 'access_token': 'token',
189 'refresh_token': 'new_refresh_token',
190 'expires_in': 500,
191 'extra': 'data'})
192
193 token, refresh_token, expiry, extra_data = _client.refresh_grant(
194 request, 'http://example.com', 'refresh_token', 'client_id',
195 'client_secret')
196
197 # Check request call
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700198 verify_request_params(request, {
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700199 'grant_type': _client._REFRESH_GRANT_TYPE,
200 'refresh_token': 'refresh_token',
201 'client_id': 'client_id',
202 'client_secret': 'client_secret'
203 })
204
205 # Check result
206 assert token == 'token'
207 assert refresh_token == 'new_refresh_token'
208 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
209 assert extra_data['extra'] == 'data'
210
211
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400212@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min)
213def test_refresh_grant_with_scopes(unused_utcnow):
214 request = make_request({
215 'access_token': 'token',
216 'refresh_token': 'new_refresh_token',
217 'expires_in': 500,
218 'extra': 'data',
219 'scope': SCOPES_AS_STRING})
220
221 token, refresh_token, expiry, extra_data = _client.refresh_grant(
222 request, 'http://example.com', 'refresh_token', 'client_id',
223 'client_secret', SCOPES_AS_LIST)
224
225 # Check request call.
226 verify_request_params(request, {
227 'grant_type': _client._REFRESH_GRANT_TYPE,
228 'refresh_token': 'refresh_token',
229 'client_id': 'client_id',
230 'client_secret': 'client_secret',
231 'scope': SCOPES_AS_STRING
232 })
233
234 # Check result.
235 assert token == 'token'
236 assert refresh_token == 'new_refresh_token'
237 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
238 assert extra_data['extra'] == 'data'
239
240
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700241def test_refresh_grant_no_access_token():
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700242 request = make_request({
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700243 # No access token.
244 'refresh_token': 'new_refresh_token',
245 'expires_in': 500,
246 'extra': 'data'})
247
248 with pytest.raises(exceptions.RefreshError):
249 _client.refresh_grant(
250 request, 'http://example.com', 'refresh_token', 'client_id',
251 'client_secret')