From 2fe865480576c74160276316be179cdb4d8f55eb Mon Sep 17 00:00:00 2001 From: Robert Spies Date: Thu, 7 Jan 2016 11:36:38 -0800 Subject: [PATCH] Adds token_expiry to the Devshell credential. --- oauth2client/devshell.py | 13 +++++++++++++ tests/test_devshell.py | 35 +++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/oauth2client/devshell.py b/oauth2client/devshell.py index 8131aff..662cb70 100644 --- a/oauth2client/devshell.py +++ b/oauth2client/devshell.py @@ -14,6 +14,7 @@ """OAuth 2.0 utitilies for Google Developer Shell environment.""" +import datetime import json import os import socket @@ -21,6 +22,10 @@ import socket from oauth2client._helpers import _to_bytes from oauth2client import client +# Expose utcnow() at module level to allow for +# easier testing (by replacing with a stub). +_UTCNOW = datetime.datetime.utcnow + DEVSHELL_ENV = 'DEVSHELL_CLIENT_PORT' @@ -52,6 +57,7 @@ class CredentialInfoResponse(object): * Index 0 - user email * Index 1 - default project ID. None if the project context is not known. * Index 2 - OAuth2 access token. None if there is no valid auth context. + * Index 3 - Seconds until the access token expires. None if not present. """ def __init__(self, json_string): @@ -63,6 +69,7 @@ class CredentialInfoResponse(object): self.user_email = pbl[0] if pbl_len > 0 else None self.project_id = pbl[1] if pbl_len > 1 else None self.access_token = pbl[2] if pbl_len > 2 else None + self.expires_in = pbl[3] if pbl_len > 3 else None def _SendRecv(): @@ -117,6 +124,12 @@ class DevshellCredentials(client.GoogleCredentials): def _refresh(self, http_request): self.devshell_response = _SendRecv() self.access_token = self.devshell_response.access_token + expires_in = self.devshell_response.expires_in + if expires_in is not None: + delta = datetime.timedelta(seconds=expires_in) + self.token_expiry = _UTCNOW() + delta + else: + self.token_expiry = None @property def user_email(self): diff --git a/tests/test_devshell.py b/tests/test_devshell.py index e3938f9..d220b4c 100644 --- a/tests/test_devshell.py +++ b/tests/test_devshell.py @@ -14,6 +14,7 @@ """Tests for oauth2client.devshell.""" +import datetime import json import os import socket @@ -22,6 +23,7 @@ import unittest import mock +from oauth2client import devshell from oauth2client._helpers import _from_bytes from oauth2client._helpers import _to_bytes from oauth2client.client import save_to_well_known_file @@ -33,6 +35,16 @@ from oauth2client.devshell import DEVSHELL_ENV from oauth2client.devshell import DevshellCredentials from oauth2client.devshell import NoDevshellServer +# A dummy value to use for the expires_in field +# in CredentialInfoResponse. +EXPIRES_IN = 1000 +DEFAULT_CREDENTIAL_JSON = json.dumps([ + 'joe@example.com', + 'fooproj', + 'sometoken', + EXPIRES_IN +]) + class TestCredentialInfoResponse(unittest.TestCase): @@ -51,16 +63,20 @@ class TestCredentialInfoResponse(unittest.TestCase): self.assertEqual(info_response.user_email, None) self.assertEqual(info_response.project_id, None) self.assertEqual(info_response.access_token, None) + self.assertEqual(info_response.expires_in, None) def test_constructor_full_list(self): user_email = 'user_email' project_id = 'project_id' access_token = 'access_token' - json_string = json.dumps([user_email, project_id, access_token]) + expires_in = 1 + json_string = json.dumps( + [user_email, project_id, access_token, expires_in]) info_response = CredentialInfoResponse(json_string) self.assertEqual(info_response.user_email, user_email) self.assertEqual(info_response.project_id, project_id) self.assertEqual(info_response.access_token, access_token) + self.assertEqual(info_response.expires_in, expires_in) class Test_SendRecv(unittest.TestCase): @@ -106,8 +122,7 @@ class _AuthReferenceServer(threading.Thread): def __init__(self, response=None): super(_AuthReferenceServer, self).__init__(None) - self.response = (response or - '["joe@example.com", "fooproj", "sometoken"]') + self.response = response or DEFAULT_CREDENTIAL_JSON self.bad_request = False def __enter__(self): @@ -195,19 +210,27 @@ class DevshellCredentialsTests(unittest.TestCase): creds = DevshellCredentials() self.assertEquals(None, creds.refresh_token) - def test_reads_credentials(self): + @mock.patch.object(devshell, '_UTCNOW') + def test_reads_credentials(self, utcnow): + NOW = datetime.datetime(1992, 12, 31) + utcnow.return_value = NOW with _AuthReferenceServer(): creds = DevshellCredentials() self.assertEqual('joe@example.com', creds.user_email) self.assertEqual('fooproj', creds.project_id) self.assertEqual('sometoken', creds.access_token) - + self.assertEqual( + NOW + datetime.timedelta(seconds=EXPIRES_IN), + creds.token_expiry) + utcnow.assert_called_once_with() + def test_handles_skipped_fields(self): with _AuthReferenceServer('["joe@example.com"]'): creds = DevshellCredentials() self.assertEqual('joe@example.com', creds.user_email) self.assertEqual(None, creds.project_id) self.assertEqual(None, creds.access_token) + self.assertEqual(None, creds.token_expiry) def test_handles_tiny_response(self): with _AuthReferenceServer('[]'): @@ -218,7 +241,7 @@ class DevshellCredentialsTests(unittest.TestCase): def test_handles_ignores_extra_fields(self): with _AuthReferenceServer( - '["joe@example.com", "fooproj", "sometoken", "extra"]'): + '["joe@example.com", "fooproj", "sometoken", 1, "extra"]'): creds = DevshellCredentials() self.assertEqual('joe@example.com', creds.user_email) self.assertEqual('fooproj', creds.project_id)