Repository: libcloud Updated Branches: refs/heads/trunk f58ef3b89 -> e02fec65d
update to allow simultaneous authorization for all supported google services Closes #302 Signed-off-by: Tomaz Muraus <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/e02fec65 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/e02fec65 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/e02fec65 Branch: refs/heads/trunk Commit: e02fec65d53ab77040fae2460246d2a16409215e Parents: f58ef3b Author: Eric Johnson <[email protected]> Authored: Fri May 30 12:33:47 2014 +0000 Committer: Tomaz Muraus <[email protected]> Committed: Mon Jun 9 20:24:06 2014 +0200 ---------------------------------------------------------------------- CHANGES.rst | 5 +++++ libcloud/common/google.py | 37 +++++++++++++++++++++----------- libcloud/compute/drivers/gce.py | 11 +++++++--- libcloud/dns/drivers/google.py | 16 +++++++------- libcloud/test/common/test_google.py | 17 +++++++++------ libcloud/test/compute/test_gce.py | 3 +++ libcloud/test/dns/test_google.py | 3 +++ 7 files changed, 62 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index 0935c46..f090c8b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -33,6 +33,11 @@ General (GITHUB-309) [Pedro Romano] +- Update Google drivers to allow simultaneous authornization for all the + supported Google Services. + (GITHUB-302) + [Eric Johnson] + Compute ~~~~~~~ http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/libcloud/common/google.py ---------------------------------------------------------------------- diff --git a/libcloud/common/google.py b/libcloud/common/google.py index 9d8000c..725c383 100644 --- a/libcloud/common/google.py +++ b/libcloud/common/google.py @@ -253,7 +253,7 @@ class GoogleBaseAuthConnection(ConnectionUserAndKey): host = 'accounts.google.com' auth_path = '/o/oauth2/auth' - def __init__(self, user_id, key, scope, + def __init__(self, user_id, key, scopes=None, redirect_uri='urn:ietf:wg:oauth:2.0:oob', login_hint=None, **kwargs): """ @@ -266,9 +266,9 @@ class GoogleBaseAuthConnection(ConnectionUserAndKey): authentication. :type key: ``str`` - :param scope: A list of urls defining the scope of authentication + :param scopes: A list of urls defining the scope of authentication to grant. - :type scope: ``list`` + :type scopes: ``list`` :keyword redirect_uri: The Redirect URI for the authentication request. See Google OAUTH2 documentation for @@ -279,8 +279,9 @@ class GoogleBaseAuthConnection(ConnectionUserAndKey): for Installed Application authentication. :type login_hint: ``str`` """ + scopes = scopes or [] - self.scope = " ".join(scope) + self.scopes = " ".join(scopes) self.redirect_uri = redirect_uri self.login_hint = login_hint @@ -329,7 +330,7 @@ class GoogleInstalledAppAuthConnection(GoogleBaseAuthConnection): auth_params = {'response_type': 'code', 'client_id': self.user_id, 'redirect_uri': self.redirect_uri, - 'scope': self.scope, + 'scope': self.scopes, 'state': 'Libcloud Request'} if self.login_hint: auth_params['login_hint'] = self.login_hint @@ -426,7 +427,7 @@ class GoogleServiceAcctAuthConnection(GoogleBaseAuthConnection): # Construct a claim set claim_set = {'iss': self.user_id, - 'scope': self.scope, + 'scope': self.scopes, 'aud': 'https://accounts.google.com/o/oauth2/token', 'exp': int(time.time()) + 3600, 'iat': int(time.time())} @@ -473,7 +474,7 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): timeout = 180 def __init__(self, user_id, key, auth_type=None, - credential_file=None, **kwargs): + credential_file=None, scopes=None, **kwargs): """ Determine authentication type, set up appropriate authentication connection and get initial authentication information. @@ -496,6 +497,10 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): :keyword credential_file: Path to file for caching authentication information. :type credential_file: ``str`` + + :keyword scopes: List of OAuth2 scope URLs. The empty default sets + read/write access to Compute, Storage, and DNS. + :type scopes: ``list`` """ self.credential_file = credential_file or '~/.gce_libcloud_auth' @@ -506,16 +511,24 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): auth_type = 'SA' else: auth_type = 'IA' - if 'scope' in kwargs: - self.scope = kwargs['scope'] - kwargs.pop('scope', None) + + # Default scopes to read/write for compute, storage, and dns. Can + # override this when calling get_driver() or setting in secrets.py + self.scopes = scopes + if not self.scopes: + self.scopes = [ + 'https://www.googleapis.com/auth/compute', + 'https://www.googleapis.com/auth/devstorage.full_control', + 'https://www.googleapis.com/auth/ndev.clouddns.readwrite', + ] self.token_info = self._get_token_info_from_file() + if auth_type == 'SA': self.auth_conn = GoogleServiceAcctAuthConnection( - user_id, key, self.scope, **kwargs) + user_id, key, self.scopes, **kwargs) elif auth_type == 'IA': self.auth_conn = GoogleInstalledAppAuthConnection( - user_id, key, self.scope, **kwargs) + user_id, key, self.scopes, **kwargs) else: raise GoogleAuthError('auth_type should be \'SA\' or \'IA\'') http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/libcloud/compute/drivers/gce.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py index b2b7663..a1cf75e 100644 --- a/libcloud/compute/drivers/gce.py +++ b/libcloud/compute/drivers/gce.py @@ -68,7 +68,6 @@ class GCEConnection(GoogleBaseConnection): def __init__(self, user_id, key, secure, auth_type=None, credential_file=None, project=None, **kwargs): - self.scope = ['https://www.googleapis.com/auth/compute'] super(GCEConnection, self).__init__(user_id, key, secure=secure, auth_type=auth_type, credential_file=credential_file, @@ -536,7 +535,7 @@ class GCENodeDriver(NodeDriver): } def __init__(self, user_id, key, datacenter=None, project=None, - auth_type=None, **kwargs): + auth_type=None, scopes=None, **kwargs): """ :param user_id: The email address (for service accounts) or Client ID (for installed apps) to be used for authentication. @@ -559,9 +558,14 @@ class GCENodeDriver(NodeDriver): If not supplied, auth_type will be guessed based on value of user_id. :type auth_type: ``str`` + + :keyword scopes: List of authorization URLs. Default is empty and + grants read/write to Compute, Storage, DNS. + :type scopes: ``list`` """ self.auth_type = auth_type self.project = project + self.scopes = scopes if not self.project: raise ValueError('Project name must be specified using ' '"project" keyword.') @@ -2518,7 +2522,8 @@ class GCENodeDriver(NodeDriver): def _ex_connection_class_kwargs(self): return {'auth_type': self.auth_type, - 'project': self.project} + 'project': self.project, + 'scopes': self.scopes} def _catch_error(self, ignore_errors=False): """ http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/libcloud/dns/drivers/google.py ---------------------------------------------------------------------- diff --git a/libcloud/dns/drivers/google.py b/libcloud/dns/drivers/google.py index 44a1231..ca3e5f0 100644 --- a/libcloud/dns/drivers/google.py +++ b/libcloud/dns/drivers/google.py @@ -36,13 +36,10 @@ class GoogleDNSConnection(GoogleBaseConnection): responseCls = GoogleDNSResponse def __init__(self, user_id, key, secure, auth_type=None, - credential_file=None, project=None): - self.scope = [ - 'https://www.googleapis.com/auth/ndev.clouddns.readwrite' - ] + credential_file=None, project=None, **kwargs): super(GoogleDNSConnection, self).\ __init__(user_id, key, secure=secure, auth_type=auth_type, - credential_file=credential_file) + credential_file=credential_file, **kwargs) self.request_path = '/dns/%s/projects/%s' % (API_VERSION, project) @@ -65,13 +62,15 @@ class GoogleDNSDriver(DNSDriver): RecordType.TXT: 'TXT', } - def __init__(self, user_id, key, project=None, auth_type=None): + def __init__(self, user_id, key, project=None, auth_type=None, scopes=None, + **kwargs): self.auth_type = auth_type self.project = project + self.scopes = scopes if not self.project: raise ValueError('Project name must be specified using ' '"project" keyword.') - super(GoogleDNSDriver, self).__init__(user_id, key) + super(GoogleDNSDriver, self).__init__(user_id, key, scopes, **kwargs) def iterate_zones(self): """ @@ -304,7 +303,8 @@ class GoogleDNSDriver(DNSDriver): def _ex_connection_class_kwargs(self): return {'auth_type': self.auth_type, - 'project': self.project} + 'project': self.project, + 'scopes': self.scopes} def _to_zones(self, response): zones = [] http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/libcloud/test/common/test_google.py ---------------------------------------------------------------------- diff --git a/libcloud/test/common/test_google.py b/libcloud/test/common/test_google.py index ff082c3..2e9c701 100644 --- a/libcloud/test/common/test_google.py +++ b/libcloud/test/common/test_google.py @@ -56,11 +56,14 @@ class GoogleBaseAuthConnectionTest(LibcloudTestCase): def setUp(self): GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp, GoogleAuthMockHttp) - self.mock_scope = ['https://www.googleapis.com/auth/foo'] - kwargs = {'scope': self.mock_scope} + self.mock_scopes = ['foo', 'bar'] + kwargs = {'scopes': self.mock_scopes} self.conn = GoogleInstalledAppAuthConnection(*GCE_PARAMS, **kwargs) + def test_scopes(self): + self.assertEqual(self.conn.scopes, 'foo bar') + def test_add_default_headers(self): old_headers = {} expected_headers = { @@ -88,8 +91,8 @@ class GoogleInstalledAppAuthConnectionTest(LibcloudTestCase): def setUp(self): GoogleInstalledAppAuthConnection.conn_classes = (GoogleAuthMockHttp, GoogleAuthMockHttp) - self.mock_scope = ['https://www.googleapis.com/auth/foo'] - kwargs = {'scope': self.mock_scope} + self.mock_scopes = ['https://www.googleapis.com/auth/foo'] + kwargs = {'scopes': self.mock_scopes} self.conn = GoogleInstalledAppAuthConnection(*GCE_PARAMS, **kwargs) @@ -128,15 +131,15 @@ class GoogleBaseConnectionTest(LibcloudTestCase): def setUp(self): GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp, GoogleAuthMockHttp) - self.mock_scope = ['https://www.googleapis.com/auth/foo'] - kwargs = {'scope': self.mock_scope, 'auth_type': 'IA'} + self.mock_scopes = ['https://www.googleapis.com/auth/foo'] + kwargs = {'scopes': self.mock_scopes, 'auth_type': 'IA'} self.conn = GoogleBaseConnection(*GCE_PARAMS, **kwargs) def test_auth_type(self): self.assertRaises(GoogleAuthError, GoogleBaseConnection, *GCE_PARAMS, **{'auth_type': 'XX'}) - kwargs = {'scope': self.mock_scope} + kwargs = {'scopes': self.mock_scopes} if SHA256: kwargs['auth_type'] = 'SA' http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/libcloud/test/compute/test_gce.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_gce.py b/libcloud/test/compute/test_gce.py index 91fd9d7..80110d0 100644 --- a/libcloud/test/compute/test_gce.py +++ b/libcloud/test/compute/test_gce.py @@ -64,6 +64,9 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): kwargs['datacenter'] = self.datacenter self.driver = GCENodeDriver(*GCE_PARAMS, **kwargs) + def test_default_scopes(self): + self.assertEqual(self.driver.scopes, None) + def test_timestamp_to_datetime(self): timestamp1 = '2013-06-26T10:05:19.340-07:00' datetime1 = datetime.datetime(2013, 6, 26, 17, 5, 19) http://git-wip-us.apache.org/repos/asf/libcloud/blob/e02fec65/libcloud/test/dns/test_google.py ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/test_google.py b/libcloud/test/dns/test_google.py index e6d0595..a074300 100644 --- a/libcloud/test/dns/test_google.py +++ b/libcloud/test/dns/test_google.py @@ -46,6 +46,9 @@ class GoogleTests(LibcloudTestCase): kwargs['auth_type'] = 'IA' self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs) + def test_default_scopes(self): + self.assertEqual(self.driver.scopes, None) + def test_list_zones(self): zones = self.driver.list_zones() self.assertEqual(len(zones), 2)
