def test_token_refresh_store_expires_soon(self): # Tests the case where an access token that is valid when it is read # from the store expires before the original request succeeds. expiration = (datetime.datetime.utcnow() + datetime.timedelta(minutes=15)) credentials = self._create_test_credentials(expiration=expiration) storage = file_module.Storage(FILENAME) storage.put(credentials) credentials = storage.get() new_cred = copy.copy(credentials) new_cred.access_token = 'bar' storage.put(new_cred) access_token = '1/3w' token_response = {'access_token': access_token, 'expires_in': 3600} http = http_mock.HttpMockSequence([ ({ 'status': http_client.UNAUTHORIZED }, b'Initial token expired'), ({ 'status': http_client.UNAUTHORIZED }, b'Store token expired'), ({ 'status': http_client.OK }, json.dumps(token_response).encode('utf-8')), ({ 'status': http_client.OK }, b'Valid response to original request') ]) credentials.authorize(http) transport.request(http, 'https://example.com') self.assertEqual(credentials.access_token, access_token)
def test_credentials_good(self): credentials = self._make_credentials() http = http_mock.HttpMockSequence([ ({ 'status': http_client.OK }, b'{"access_token":"1/3w","expires_in":3600}'), ({ 'status': http_client.OK }, 'echo_request_headers'), ]) http = credentials.authorize(http) resp, content = transport.request(http, 'http://example.org') self.assertEqual(b'Bearer 1/3w', content[b'Authorization'])
def test_authorize_401(self, utcnow): utcnow.return_value = T1_DATE http = http_mock.HttpMockSequence([ ({ 'status': http_client.OK }, b''), ({ 'status': http_client.UNAUTHORIZED }, b''), ({ 'status': http_client.OK }, b''), ]) self.jwt.authorize(http) transport.request(http, self.url) token_1 = self.jwt.access_token utcnow.return_value = T2_DATE response, _ = transport.request(http, self.url) self.assertEquals(response.status, http_client.OK) token_2 = self.jwt.access_token # Check the 401 forced a new token self.assertNotEqual(token_1, token_2) # Verify mocks. certs = {'key': datafile('public_cert.pem')} self.assertEqual(len(http.requests), 3) issued_at_vals = (T1, T1, T2) exp_vals = (T1_EXPIRY, T1_EXPIRY, T2_EXPIRY) for info, issued_at, exp_val in zip(http.requests, issued_at_vals, exp_vals): self.assertEqual(info['uri'], self.url) self.assertEqual(info['method'], 'GET') self.assertIsNone(info['body']) self.assertEqual(len(info['headers']), 1) bearer, token = info['headers'][b'Authorization'].split() self.assertEqual(bearer, b'Bearer') # To parse the token, skip the time check, since this # test intentionally has stale tokens. with mock.patch('oauth2client.crypt._verify_time_range', return_value=True): payload = crypt.verify_signed_jwt_with_certs(token, certs, audience=self.url) self.assertEqual(len(payload), 5) self.assertEqual(payload['iss'], self.service_account_email) self.assertEqual(payload['sub'], self.service_account_email) self.assertEqual(payload['iat'], issued_at) self.assertEqual(payload['exp'], exp_val) self.assertEqual(payload['aud'], self.url)
def _credentials_refresh(self, credentials): http = http_mock.HttpMockSequence([ ({ 'status': http_client.OK }, b'{"access_token":"1/3w","expires_in":3600}'), ({ 'status': http_client.UNAUTHORIZED }, b''), ({ 'status': http_client.OK }, b'{"access_token":"3/3w","expires_in":3600}'), ({ 'status': http_client.OK }, 'echo_request_headers'), ]) http = credentials.authorize(http) _, content = transport.request(http, 'http://example.org') return content
def test_authorize_no_aud(self, time, utcnow): utcnow.return_value = T1_DATE time.return_value = T1 jwt = service_account._JWTAccessCredentials( self.service_account_email, self.signer, private_key_id=self.private_key_id, client_id=self.client_id) http = http_mock.HttpMockSequence([ ({ 'status': http_client.OK }, b''), ]) jwt.authorize(http) transport.request(http, self.url) # Ensure we do not cache the token self.assertIsNone(jwt.access_token) # Verify mocks. self.assertEqual(len(http.requests), 1) info = http.requests[0] self.assertEqual(info['method'], 'GET') self.assertEqual(info['uri'], self.url) self.assertIsNone(info['body']) self.assertEqual(len(info['headers']), 1) bearer, token = info['headers'][b'Authorization'].split() self.assertEqual(bearer, b'Bearer') certs = {'key': datafile('public_cert.pem')} payload = crypt.verify_signed_jwt_with_certs(token, certs, audience=self.url) self.assertEqual(len(payload), 5) self.assertEqual(payload['iss'], self.service_account_email) self.assertEqual(payload['sub'], self.service_account_email) self.assertEqual(payload['iat'], T1) self.assertEqual(payload['exp'], T1_EXPIRY) self.assertEqual(payload['aud'], self.url)
def test_authorize_success(self, time, utcnow): utcnow.return_value = T1_DATE time.return_value = T1 http = http_mock.HttpMockSequence([ ({ 'status': http_client.OK }, b''), ({ 'status': http_client.OK }, b''), ]) self.jwt.authorize(http) transport.request(http, self.url) # Ensure we use the cached token utcnow.return_value = T2_DATE transport.request(http, self.url) # Verify mocks. certs = {'key': datafile('public_cert.pem')} self.assertEqual(len(http.requests), 2) for info in http.requests: self.assertEqual(info['method'], 'GET') self.assertEqual(info['uri'], self.url) self.assertIsNone(info['body']) self.assertEqual(len(info['headers']), 1) bearer, token = info['headers'][b'Authorization'].split() self.assertEqual(bearer, b'Bearer') payload = crypt.verify_signed_jwt_with_certs(token, certs, audience=self.url) self.assertEqual(len(payload), 5) self.assertEqual(payload['iss'], self.service_account_email) self.assertEqual(payload['sub'], self.service_account_email) self.assertEqual(payload['iat'], T1) self.assertEqual(payload['exp'], T1_EXPIRY) self.assertEqual(payload['aud'], self.url)
def test_token_refresh_stream_body(self): expiration = (datetime.datetime.utcnow() + datetime.timedelta(minutes=15)) credentials = self._create_test_credentials(expiration=expiration) storage = file_module.Storage(FILENAME) storage.put(credentials) credentials = storage.get() new_cred = copy.copy(credentials) new_cred.access_token = 'bar' storage.put(new_cred) valid_access_token = '1/3w' token_response = { 'access_token': valid_access_token, 'expires_in': 3600 } http = http_mock.HttpMockSequence([ ({ 'status': http_client.UNAUTHORIZED }, b'Initial token expired'), ({ 'status': http_client.UNAUTHORIZED }, b'Store token expired'), ({ 'status': http_client.OK }, json.dumps(token_response).encode('utf-8')), ({ 'status': http_client.OK }, 'echo_request_body') ]) body = six.StringIO('streaming body') credentials.authorize(http) _, content = transport.request(http, 'https://example.com', body=body) self.assertEqual(content, 'streaming body') self.assertEqual(credentials.access_token, valid_access_token)
def test_access_token(self, utcnow): # Configure the patch. seconds = 11 NOW = datetime.datetime(1992, 12, 31, second=seconds) utcnow.return_value = NOW # Create a custom credentials with a mock signer. signer = mock.Mock() signed_value = b'signed-content' signer.sign = mock.Mock(name='sign', return_value=signed_value) credentials = service_account.ServiceAccountCredentials( self.service_account_email, signer, private_key_id=self.private_key_id, client_id=self.client_id, ) # Begin testing. lifetime = 2 # number of seconds in which the token expires EXPIRY_TIME = datetime.datetime(1992, 12, 31, second=seconds + lifetime) token1 = u'first_token' token_response_first = { 'access_token': token1, 'expires_in': lifetime, } token2 = u'second_token' token_response_second = { 'access_token': token2, 'expires_in': lifetime, } http = http_mock.HttpMockSequence([ ({ 'status': http_client.OK }, json.dumps(token_response_first).encode('utf-8')), ({ 'status': http_client.OK }, json.dumps(token_response_second).encode('utf-8')), ]) # Get Access Token, First attempt. self.assertIsNone(credentials.access_token) self.assertFalse(credentials.access_token_expired) self.assertIsNone(credentials.token_expiry) token = credentials.get_access_token(http=http) self.assertEqual(credentials.token_expiry, EXPIRY_TIME) self.assertEqual(token1, token.access_token) self.assertEqual(lifetime, token.expires_in) self.assertEqual(token_response_first, credentials.token_response) # Two utcnow calls are expected: # - get_access_token() -> _do_refresh_request (setting expires in) # - get_access_token() -> _expires_in() expected_utcnow_calls = [mock.call()] * 2 self.assertEqual(expected_utcnow_calls, utcnow.mock_calls) # One call to sign() expected: Actual refresh was needed. self.assertEqual(len(signer.sign.mock_calls), 1) # Get Access Token, Second Attempt (not expired) self.assertEqual(credentials.access_token, token1) self.assertFalse(credentials.access_token_expired) token = credentials.get_access_token(http=http) # Make sure no refresh occurred since the token was not expired. self.assertEqual(token1, token.access_token) self.assertEqual(lifetime, token.expires_in) self.assertEqual(token_response_first, credentials.token_response) # Three more utcnow calls are expected: # - access_token_expired # - get_access_token() -> access_token_expired # - get_access_token -> _expires_in expected_utcnow_calls = [mock.call()] * (2 + 3) self.assertEqual(expected_utcnow_calls, utcnow.mock_calls) # No call to sign() expected: the token was not expired. self.assertEqual(len(signer.sign.mock_calls), 1 + 0) # Get Access Token, Third Attempt (force expiration) self.assertEqual(credentials.access_token, token1) credentials.token_expiry = NOW # Manually force expiry. self.assertTrue(credentials.access_token_expired) token = credentials.get_access_token(http=http) # Make sure refresh occurred since the token was not expired. self.assertEqual(token2, token.access_token) self.assertEqual(lifetime, token.expires_in) self.assertFalse(credentials.access_token_expired) self.assertEqual(token_response_second, credentials.token_response) # Five more utcnow calls are expected: # - access_token_expired # - get_access_token -> access_token_expired # - get_access_token -> _do_refresh_request # - get_access_token -> _expires_in # - access_token_expired expected_utcnow_calls = [mock.call()] * (2 + 3 + 5) self.assertEqual(expected_utcnow_calls, utcnow.mock_calls) # One more call to sign() expected: Actual refresh was needed. self.assertEqual(len(signer.sign.mock_calls), 1 + 0 + 1) self.assertEqual(credentials.access_token, token2)