Repository: libcloud
Updated Branches:
  refs/heads/trunk b9a5586e9 -> 2846da70f


Fix openstack v3 authentication.

This change also allows user to provide a custom value for the OpenStack
"domain" which defaults to "Default".

With the code for the OpenStack Identity API v3, two new parameters were added:
domain_name and token_scope but it was impossible to define them to a value
other than their respective default.

Closes #744


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/eb497fa9
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/eb497fa9
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/eb497fa9

Branch: refs/heads/trunk
Commit: eb497fa92287623fcc81413ae4775543b7ff8e71
Parents: b9a5586
Author: lionel <lio...@sixsq.com>
Authored: Tue Apr 12 22:58:16 2016 +0200
Committer: Tomaz Muraus <to...@tomaz.me>
Committed: Sat Apr 23 08:44:27 2016 +0200

----------------------------------------------------------------------
 libcloud/common/openstack.py                    | 23 ++++++++++++++
 libcloud/common/openstack_identity.py           | 31 +++++++++---------
 libcloud/test/common/test_openstack_identity.py | 33 ++++++++++++++------
 3 files changed, 61 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/eb497fa9/libcloud/common/openstack.py
----------------------------------------------------------------------
diff --git a/libcloud/common/openstack.py b/libcloud/common/openstack.py
index cfdef79..8347613 100644
--- a/libcloud/common/openstack.py
+++ b/libcloud/common/openstack.py
@@ -94,6 +94,17 @@ class OpenStackBaseConnection(ConnectionUserAndKey):
                                 ex_force_base_url must also be provided.
     :type ex_force_auth_token: ``str``
 
+    :param token_scope: Whether to scope a token to a "project", a
+                        "domain" or "unscoped".
+    :type token_scope: ``str``
+
+    :param ex_domain_name: When authenticating, provide this domain name to
+                           the identity service.  A scoped token will be
+                           returned. Some cloud providers require the domain
+                           name to be provided at authentication time. Others
+                           will use a default domain if none is provided.
+    :type ex_domain_name: ``str``
+
     :param ex_tenant_name: When authenticating, provide this tenant name to the
                            identity service. A scoped token will be returned.
                            Some cloud providers require the tenant name to be
@@ -134,6 +145,8 @@ class OpenStackBaseConnection(ConnectionUserAndKey):
                  ex_force_auth_url=None,
                  ex_force_auth_version=None,
                  ex_force_auth_token=None,
+                 ex_token_scope=None,
+                 ex_domain_name=None,
                  ex_tenant_name=None,
                  ex_force_service_type=None,
                  ex_force_service_name=None,
@@ -149,6 +162,8 @@ class OpenStackBaseConnection(ConnectionUserAndKey):
         self._ex_force_base_url = ex_force_base_url
         self._ex_force_auth_url = ex_force_auth_url
         self._ex_force_auth_token = ex_force_auth_token
+        self._ex_token_scope = ex_token_scope
+        self._ex_domain_name = ex_domain_name
         self._ex_tenant_name = ex_tenant_name
         self._ex_force_service_type = ex_force_service_type
         self._ex_force_service_name = ex_force_service_name
@@ -186,6 +201,8 @@ class OpenStackBaseConnection(ConnectionUserAndKey):
                             user_id=self.user_id,
                             key=self.key,
                             tenant_name=self._ex_tenant_name,
+                            domain_name=self._ex_domain_name,
+                            token_scope=self._ex_token_scope,
                             timeout=self.timeout,
                             parent_conn=self)
 
@@ -400,6 +417,8 @@ class OpenStackDriverMixin(object):
         self._ex_force_auth_url = kwargs.get('ex_force_auth_url', None)
         self._ex_force_auth_version = kwargs.get('ex_force_auth_version', None)
         self._ex_force_auth_token = kwargs.get('ex_force_auth_token', None)
+        self._ex_token_scope = kwargs.get('ex_token_scope', None)
+        self._ex_domain_name = kwargs.get('ex_domain_name', None)
         self._ex_tenant_name = kwargs.get('ex_tenant_name', None)
         self._ex_force_service_type = kwargs.get('ex_force_service_type', None)
         self._ex_force_service_name = kwargs.get('ex_force_service_name', None)
@@ -420,6 +439,10 @@ class OpenStackDriverMixin(object):
             rv['ex_force_auth_url'] = self._ex_force_auth_url
         if self._ex_force_auth_version:
             rv['ex_force_auth_version'] = self._ex_force_auth_version
+        if self._ex_token_scope:
+            rv['ex_token_scope'] = self._ex_token_scope
+        if self._ex_domain_name:
+            rv['ex_domain_name'] = self._ex_domain_name
         if self._ex_tenant_name:
             rv['ex_tenant_name'] = self._ex_tenant_name
         if self._ex_force_service_type:

http://git-wip-us.apache.org/repos/asf/libcloud/blob/eb497fa9/libcloud/common/openstack_identity.py
----------------------------------------------------------------------
diff --git a/libcloud/common/openstack_identity.py 
b/libcloud/common/openstack_identity.py
index 24c12d0..3ac9338 100644
--- a/libcloud/common/openstack_identity.py
+++ b/libcloud/common/openstack_identity.py
@@ -572,14 +572,13 @@ class OpenStackIdentityConnection(ConnectionUserAndKey):
     auth_version = None
 
     def __init__(self, auth_url, user_id, key, tenant_name=None,
+                 domain_name=None, token_scope=None,
                  timeout=None, parent_conn=None):
         super(OpenStackIdentityConnection, self).__init__(user_id=user_id,
                                                           key=key,
                                                           url=auth_url,
                                                           timeout=timeout)
 
-        self.auth_url = auth_url
-        self.tenant_name = tenant_name
         self.parent_conn = parent_conn
 
         # enable tests to use the same mock connection classes.
@@ -591,6 +590,10 @@ class OpenStackIdentityConnection(ConnectionUserAndKey):
 
         self.auth_url = auth_url
         self.tenant_name = tenant_name
+        self.domain_name = domain_name if domain_name is not None else \
+            'Default'
+        self.token_scope = token_scope if token_scope is not None else \
+            OpenStackIdentityTokenScope.PROJECT
         self.timeout = timeout
 
         self.urls = {}
@@ -927,8 +930,7 @@ class 
OpenStackIdentity_3_0_Connection(OpenStackIdentityConnection):
     ]
 
     def __init__(self, auth_url, user_id, key, tenant_name=None,
-                 domain_name='Default',
-                 token_scope=OpenStackIdentityTokenScope.PROJECT,
+                 domain_name=None, token_scope=None,
                  timeout=None, parent_conn=None):
         """
         :param tenant_name: Name of the project this user belongs to. Note:
@@ -941,8 +943,8 @@ class 
OpenStackIdentity_3_0_Connection(OpenStackIdentityConnection):
                             domain to scope the token to.
         :type domain_name: ``str``
 
-        :param token_scope: Whether to scope a token to a "project" or a
-                         "domain"
+        :param token_scope: Whether to scope a token to a "project", a
+                            "domain" or "unscoped"
         :type token_scope: ``str``
         """
         super(OpenStackIdentity_3_0_Connection,
@@ -950,23 +952,20 @@ class 
OpenStackIdentity_3_0_Connection(OpenStackIdentityConnection):
                              user_id=user_id,
                              key=key,
                              tenant_name=tenant_name,
+                             domain_name=domain_name,
+                             token_scope=token_scope,
                              timeout=timeout,
                              parent_conn=parent_conn)
-        if token_scope not in self.VALID_TOKEN_SCOPES:
+
+        if self.token_scope not in self.VALID_TOKEN_SCOPES:
             raise ValueError('Invalid value for "token_scope" argument: %s' %
-                             (token_scope))
+                             (self.token_scope))
 
-        if (token_scope == OpenStackIdentityTokenScope.PROJECT and
-                (not tenant_name or not domain_name)):
+        if (self.token_scope == OpenStackIdentityTokenScope.PROJECT and
+                (not self.tenant_name or not self.domain_name)):
             raise ValueError('Must provide tenant_name and domain_name '
                              'argument')
-        elif (token_scope == OpenStackIdentityTokenScope.DOMAIN and
-                not domain_name):
-            raise ValueError('Must provide domain_name argument')
 
-        self.tenant_name = tenant_name
-        self.domain_name = domain_name
-        self.token_scope = token_scope
         self.auth_user_roles = None
 
     def authenticate(self, force=False):

http://git-wip-us.apache.org/repos/asf/libcloud/blob/eb497fa9/libcloud/test/common/test_openstack_identity.py
----------------------------------------------------------------------
diff --git a/libcloud/test/common/test_openstack_identity.py 
b/libcloud/test/common/test_openstack_identity.py
index 7c57cf1..69e6f45 100644
--- a/libcloud/test/common/test_openstack_identity.py
+++ b/libcloud/test/common/test_openstack_identity.py
@@ -274,16 +274,6 @@ class 
OpenStackIdentity_3_0_ConnectionTests(unittest.TestCase):
                                 key='test',
                                 token_scope='project')
 
-        # Missing domain_name
-        expected_msg = 'Must provide domain_name argument'
-        self.assertRaisesRegexp(ValueError, expected_msg,
-                                OpenStackIdentity_3_0_Connection,
-                                auth_url='http://none',
-                                user_id='test',
-                                key='test',
-                                token_scope='domain',
-                                domain_name=None)
-
         # Scope to project all ok
         OpenStackIdentity_3_0_Connection(auth_url='http://none',
                                          user_id='test',
@@ -299,6 +289,15 @@ class 
OpenStackIdentity_3_0_ConnectionTests(unittest.TestCase):
                                          tenant_name=None,
                                          domain_name='Default')
 
+    def test_authenticate(self):
+        auth = OpenStackIdentity_3_0_Connection(auth_url='http://none',
+                                                user_id='test_user_id',
+                                                key='test_key',
+                                                token_scope='project',
+                                                tenant_name="test_tenant",
+                                                domain_name='test_domain')
+        auth.authenticate()
+
     def test_list_supported_versions(self):
         OpenStackIdentity_3_0_MockHttp.type = 'v3'
 
@@ -574,6 +573,20 @@ class OpenStackIdentity_3_0_MockHttp(MockHttp):
             return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
         raise NotImplementedError()
 
+    def _v3_auth_tokens(self, method, url, body, headers):
+        if method == 'POST':
+            status = httplib.OK
+            data = json.loads(body)
+            if data['auth']['identity']['password']['user']['domain']['name'] 
!= 'test_domain' or \
+                    data['auth']['scope']['project']['domain']['name'] != 
'test_domain':
+                status = httplib.UNAUTHORIZED
+
+            body = ComputeFileFixtures('openstack').load('_v3__auth.json')
+            headers = self.json_content_headers.copy()
+            headers['x-subject-token'] = '00000000000000000000000000000000'
+            return (status, body, headers, httplib.responses[httplib.OK])
+        raise NotImplementedError()
+
     def _v3_users(self, method, url, body, headers):
         if method == 'GET':
             # list users

Reply via email to