Consolidate handling of scopes.
Reviewed in https://codereview.appspot.com/6853060/.
diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py
index 5439a35..c747528 100644
--- a/oauth2client/appengine.py
+++ b/oauth2client/appengine.py
@@ -126,12 +126,10 @@
"""Constructor for AppAssertionCredentials
Args:
- scope: string or list of strings, scope(s) of the credentials being
+ scope: string or iterable of strings, scope(s) of the credentials being
requested.
"""
- if type(scope) is list:
- scope = ' '.join(scope)
- self.scope = scope
+ self.scope = util.scopes_to_string(scope)
super(AppAssertionCredentials, self).__init__(
'ignored' # assertion_type is ignore in this subclass.
@@ -157,7 +155,8 @@
AccessTokenRefreshError: When the refresh fails.
"""
try:
- (token, _) = app_identity.get_access_token(self.scope)
+ scopes = self.scope.split()
+ (token, _) = app_identity.get_access_token(scopes)
except app_identity.Error, e:
raise AccessTokenRefreshError(str(e))
self.access_token = token
@@ -399,7 +398,7 @@
Args:
client_id: string, client identifier.
client_secret: string client secret.
- scope: string or list of strings, scope(s) of the credentials being
+ scope: string or iterable of strings, scope(s) of the credentials being
requested.
auth_uri: string, URI for authorization endpoint. For convenience
defaults to Google's endpoints but any OAuth 2.0 provider can be used.
@@ -419,7 +418,7 @@
self.credentials = None
self._client_id = client_id
self._client_secret = client_secret
- self._scope = scope
+ self._scope = util.scopes_to_string(scope)
self._auth_uri = auth_uri
self._token_uri = token_uri
self._user_agent = user_agent
@@ -647,7 +646,7 @@
Args:
filename: string, File name of client secrets.
- scope: string or list of strings, scope(s) of the credentials being
+ scope: string or iterable of strings, scope(s) of the credentials being
requested.
message: string, A friendly string to display to the user if the
clientsecrets file is missing or invalid. The message may contain HTML
diff --git a/oauth2client/client.py b/oauth2client/client.py
index dc9bc89..cce4ae6 100644
--- a/oauth2client/client.py
+++ b/oauth2client/client.py
@@ -775,7 +775,7 @@
Args:
service_account_name: string, id for account, usually an email address.
private_key: string, private key in P12 format.
- scope: string or list of strings, scope(s) of the credentials being
+ scope: string or iterable of strings, scope(s) of the credentials being
requested.
private_key_password: string, password for private_key.
user_agent: string, HTTP User-Agent to provide for this application.
@@ -790,9 +790,7 @@
token_uri=token_uri,
)
- if type(scope) is list:
- scope = ' '.join(scope)
- self.scope = scope
+ self.scope = util.scopes_to_string(scope)
# Keep base64 encoded so it can be stored in JSON.
self.private_key = base64.b64encode(private_key)
@@ -936,7 +934,7 @@
Args:
client_id: string, client identifier.
client_secret: string, client secret.
- scope: string or list of strings, scope(s) to request.
+ scope: string or iterable of strings, scope(s) to request.
code: string, An authroization code, most likely passed down from
the client
redirect_uri: string, this is generally set to 'postmessage' to match the
@@ -973,7 +971,7 @@
Args:
filename: string, File name of clientsecrets.
- scope: string or list of strings, scope(s) to request.
+ scope: string or iterable of strings, scope(s) to request.
code: string, An authorization code, most likely passed down from
the client
message: string, A friendly string to display to the user if the
@@ -1024,7 +1022,7 @@
Args:
client_id: string, client identifier.
client_secret: string client secret.
- scope: string or list of strings, scope(s) of the credentials being
+ scope: string or iterable of strings, scope(s) of the credentials being
requested.
redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
a non-web-based application, or a URI that handles the callback from
@@ -1039,9 +1037,7 @@
"""
self.client_id = client_id
self.client_secret = client_secret
- if type(scope) is list:
- scope = ' '.join(scope)
- self.scope = scope
+ self.scope = util.scopes_to_string(scope)
self.redirect_uri = redirect_uri
self.user_agent = user_agent
self.auth_uri = auth_uri
@@ -1169,7 +1165,7 @@
Args:
filename: string, File name of client secrets.
- scope: string or list of strings, scope(s) to request.
+ scope: string or iterable of strings, scope(s) to request.
redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob' for
a non-web-based application, or a URI that handles the callback from
the authorization server.
diff --git a/oauth2client/gce.py b/oauth2client/gce.py
index d1b1234..ad27588 100644
--- a/oauth2client/gce.py
+++ b/oauth2client/gce.py
@@ -53,12 +53,10 @@
"""Constructor for AppAssertionCredentials
Args:
- scope: string or list of strings, scope(s) of the credentials being
+ scope: string or iterable of strings, scope(s) of the credentials being
requested.
"""
- if type(scope) is list:
- scope = ' '.join(scope)
- self.scope = scope
+ self.scope = util.scopes_to_string(scope)
super(AppAssertionCredentials, self).__init__(
'ignored' # assertion_type is ignore in this subclass.
diff --git a/oauth2client/multistore_file.py b/oauth2client/multistore_file.py
index c919573..b8e9ff0 100644
--- a/oauth2client/multistore_file.py
+++ b/oauth2client/multistore_file.py
@@ -69,7 +69,7 @@
filename: The JSON file storing a set of credentials
client_id: The client_id for the credential
user_agent: The user agent for the credential
- scope: string or list of strings, Scope(s) being requested
+ scope: string or iterable of strings, Scope(s) being requested
warn_on_readonly: if True, log a warning if the store is readonly
Returns:
@@ -83,8 +83,7 @@
filename, _MultiStore(filename, warn_on_readonly=warn_on_readonly))
finally:
_multistores_lock.release()
- if type(scope) is list:
- scope = ' '.join(scope)
+ scope = util.scopes_to_string(scope)
return multistore._get_storage(client_id, user_agent, scope)
diff --git a/oauth2client/util.py b/oauth2client/util.py
index bda14c6..8166d39 100644
--- a/oauth2client/util.py
+++ b/oauth2client/util.py
@@ -27,6 +27,7 @@
import gflags
import inspect
import logging
+import types
logger = logging.getLogger(__name__)
@@ -125,3 +126,22 @@
else:
args, _, _, defaults = inspect.getargspec(max_positional_args)
return positional(len(args) - len(defaults))(max_positional_args)
+
+
+def scopes_to_string(scopes):
+ """Converts scope value to a string.
+
+ If scopes is a string then it is simply passed through. If scopes is an
+ iterable then a string is returned that is all the individual scopes
+ concatenated with spaces.
+
+ Args:
+ scopes: string or iterable of strings, the scopes.
+
+ Returns:
+ The scopes formatted as a single string.
+ """
+ if isinstance(scopes, types.StringTypes):
+ return scopes
+ else:
+ return ' '.join(scopes)
diff --git a/tests/test_oauth2client_appengine.py b/tests/test_oauth2client_appengine.py
index 6039a0d..827a31f 100644
--- a/tests/test_oauth2client_appengine.py
+++ b/tests/test_oauth2client_appengine.py
@@ -175,7 +175,9 @@
apiproxy_stub_map.apiproxy.RegisterStub(
'memcache', memcache_stub.MemcacheServiceStub())
- scope = ["http://www.googleapis.com/scope"]
+ scope = [
+ "http://www.googleapis.com/scope",
+ "http://www.googleapis.com/scope2"]
credentials = AppAssertionCredentials(scope)
http = httplib2.Http()
credentials.refresh(http)
@@ -183,8 +185,18 @@
json = credentials.to_json()
credentials = Credentials.new_from_json(json)
- self.assertEqual(scope[0], credentials.scope)
+ self.assertEqual(
+ 'http://www.googleapis.com/scope http://www.googleapis.com/scope2',
+ credentials.scope)
+ scope = "http://www.googleapis.com/scope http://www.googleapis.com/scope2"
+ credentials = AppAssertionCredentials(scope)
+ http = httplib2.Http()
+ credentials.refresh(http)
+ self.assertEqual('a_token_123', credentials.access_token)
+ self.assertEqual(
+ 'http://www.googleapis.com/scope http://www.googleapis.com/scope2',
+ credentials.scope)
class TestFlowModel(db.Model):
flow = FlowProperty()
diff --git a/tests/test_oauth2client_util.py b/tests/test_oauth2client_util.py
new file mode 100644
index 0000000..6c72521
--- /dev/null
+++ b/tests/test_oauth2client_util.py
@@ -0,0 +1,27 @@
+"""Unit tests for oauth2client.util."""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+import unittest
+
+from oauth2client import util
+
+
+class ScopeToStringTests(unittest.TestCase):
+
+ def test_iterables(self):
+ cases = [
+ ('', ''),
+ ('', ()),
+ ('', []),
+ ('', ('', )),
+ ('', ['', ]),
+ ('a', ('a', )),
+ ('b', ['b', ]),
+ ('a b', ['a', 'b']),
+ ('a b', ('a', 'b')),
+ ('a b', 'a b'),
+ ('a b', (s for s in ['a', 'b'])),
+ ]
+ for expected, case in cases:
+ self.assertEqual(expected, util.scopes_to_string(case))