summaryrefslogtreecommitdiff
path: root/troveclient/auth.py
diff options
context:
space:
mode:
authorMichael Basnight <mbasnight@gmail.com>2013-06-17 23:34:27 -0700
committerMichael Basnight <mbasnight@gmail.com>2013-06-21 20:15:23 +0000
commit9916c8f2733b683d859770d05dacd2c9c82912d7 (patch)
tree084a0d53580cbbd34ed8f28de9302d6c78f7050d /troveclient/auth.py
parentbc90b3e088d3d4b83b5b3de0f9f83d9b6956947d (diff)
downloadpython-troveclient-0.1.3.tar.gz
Rename from reddwarf to trove.0.1.3
Implements Blueprint reddwarf-trove-rename Change-Id: Ib2d694c7466887ca297bea4250eca17cdc06b7bf
Diffstat (limited to 'troveclient/auth.py')
-rw-r--r--troveclient/auth.py269
1 files changed, 269 insertions, 0 deletions
diff --git a/troveclient/auth.py b/troveclient/auth.py
new file mode 100644
index 0000000..909d45b
--- /dev/null
+++ b/troveclient/auth.py
@@ -0,0 +1,269 @@
+# Copyright 2012 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from troveclient import exceptions
+
+
+def get_authenticator_cls(cls_or_name):
+ """Factory method to retrieve Authenticator class."""
+ if isinstance(cls_or_name, type):
+ return cls_or_name
+ elif isinstance(cls_or_name, basestring):
+ if cls_or_name == "keystone":
+ return KeyStoneV2Authenticator
+ elif cls_or_name == "rax":
+ return RaxAuthenticator
+ elif cls_or_name == "auth1.1":
+ return Auth1_1
+ elif cls_or_name == "fake":
+ return FakeAuth
+
+ raise ValueError("Could not determine authenticator class from the given "
+ "value %r." % cls_or_name)
+
+
+class Authenticator(object):
+ """
+ Helper class to perform Keystone or other miscellaneous authentication.
+
+ The "authenticate" method returns a ServiceCatalog, which can be used
+ to obtain a token.
+
+ """
+
+ URL_REQUIRED = True
+
+ def __init__(self, client, type, url, username, password, tenant,
+ region=None, service_type=None, service_name=None,
+ service_url=None):
+ self.client = client
+ self.type = type
+ self.url = url
+ self.username = username
+ self.password = password
+ self.tenant = tenant
+ self.region = region
+ self.service_type = service_type
+ self.service_name = service_name
+ self.service_url = service_url
+
+ def _authenticate(self, url, body, root_key='access'):
+ """Authenticate and extract the service catalog."""
+ # Make sure we follow redirects when trying to reach Keystone
+ tmp_follow_all_redirects = self.client.follow_all_redirects
+ self.client.follow_all_redirects = True
+
+ try:
+ resp, body = self.client._time_request(url, "POST", body=body)
+ finally:
+ self.client.follow_all_redirects = tmp_follow_all_redirects
+
+ if resp.status == 200: # content must always present
+ try:
+ return ServiceCatalog(body, region=self.region,
+ service_type=self.service_type,
+ service_name=self.service_name,
+ service_url=self.service_url,
+ root_key=root_key)
+ except exceptions.AmbiguousEndpoints:
+ print "Found more than one valid endpoint. Use a more "\
+ "restrictive filter"
+ raise
+ except KeyError:
+ raise exceptions.AuthorizationFailure()
+ except exceptions.EndpointNotFound:
+ print "Could not find any suitable endpoint. Correct region?"
+ raise
+
+ elif resp.status == 305:
+ return resp['location']
+ else:
+ raise exceptions.from_response(resp, body)
+
+ def authenticate(self):
+ raise NotImplementedError("Missing authenticate method.")
+
+
+class KeyStoneV2Authenticator(Authenticator):
+
+ def authenticate(self):
+ if self.url is None:
+ raise exceptions.AuthUrlNotGiven()
+ return self._v2_auth(self.url)
+
+ def _v2_auth(self, url):
+ """Authenticate against a v2.0 auth service."""
+ body = {"auth": {
+ "passwordCredentials": {
+ "username": self.username,
+ "password": self.password}
+ }
+ }
+
+ if self.tenant:
+ body['auth']['tenantName'] = self.tenant
+
+ return self._authenticate(url, body)
+
+
+class Auth1_1(Authenticator):
+
+ def authenticate(self):
+ """Authenticate against a v2.0 auth service."""
+ if self.url is None:
+ raise exceptions.AuthUrlNotGiven()
+ auth_url = self.url
+ body = {"credentials": {"username": self.username,
+ "key": self.password}}
+ return self._authenticate(auth_url, body, root_key='auth')
+
+ try:
+ print(resp_body)
+ self.auth_token = resp_body['auth']['token']['id']
+ except KeyError:
+ raise nova_exceptions.AuthorizationFailure()
+
+ catalog = resp_body['auth']['serviceCatalog']
+ if 'cloudDatabases' not in catalog:
+ raise nova_exceptions.EndpointNotFound()
+ endpoints = catalog['cloudDatabases']
+ for endpoint in endpoints:
+ if self.region_name is None or \
+ endpoint['region'] == self.region_name:
+ self.management_url = endpoint['publicURL']
+ return
+ raise nova_exceptions.EndpointNotFound()
+
+
+class RaxAuthenticator(Authenticator):
+
+ def authenticate(self):
+ if self.url is None:
+ raise exceptions.AuthUrlNotGiven()
+ return self._rax_auth(self.url)
+
+ def _rax_auth(self, url):
+ """Authenticate against the Rackspace auth service."""
+ body = {'auth': {
+ 'RAX-KSKEY:apiKeyCredentials': {
+ 'username': self.username,
+ 'apiKey': self.password,
+ 'tenantName': self.tenant}
+ }
+ }
+
+ return self._authenticate(self.url, body)
+
+
+class FakeAuth(Authenticator):
+ """Useful for faking auth."""
+
+ def authenticate(self):
+ class FakeCatalog(object):
+ def __init__(self, auth):
+ self.auth = auth
+
+ def get_public_url(self):
+ return "%s/%s" % ('http://localhost:8779/v1.0',
+ self.auth.tenant)
+
+ def get_token(self):
+ return self.auth.tenant
+
+ return FakeCatalog(self)
+
+
+class ServiceCatalog(object):
+ """Represents a Keystone Service Catalog which describes a service.
+
+ This class has methods to obtain a valid token as well as a public service
+ url and a management url.
+
+ """
+
+ def __init__(self, resource_dict, region=None, service_type=None,
+ service_name=None, service_url=None, root_key='access'):
+ self.catalog = resource_dict
+ self.region = region
+ self.service_type = service_type
+ self.service_name = service_name
+ self.service_url = service_url
+ self.management_url = None
+ self.public_url = None
+ self.root_key = root_key
+ self._load()
+
+ def _load(self):
+ if not self.service_url:
+ self.public_url = self._url_for(attr='region',
+ filter_value=self.region,
+ endpoint_type="publicURL")
+ self.management_url = self._url_for(attr='region',
+ filter_value=self.region,
+ endpoint_type="adminURL")
+ else:
+ self.public_url = self.service_url
+ self.management_url = self.service_url
+
+ def get_token(self):
+ return self.catalog[self.root_key]['token']['id']
+
+ def get_management_url(self):
+ return self.management_url
+
+ def get_public_url(self):
+ return self.public_url
+
+ def _url_for(self, attr=None, filter_value=None,
+ endpoint_type='publicURL'):
+ """
+ Fetch the public URL from the Trove service for a particular
+ endpoint attribute. If none given, return the first.
+ """
+ matching_endpoints = []
+ if 'endpoints' in self.catalog:
+ # We have a bastardized service catalog. Treat it special. :/
+ for endpoint in self.catalog['endpoints']:
+ if not filter_value or endpoint[attr] == filter_value:
+ matching_endpoints.append(endpoint)
+ if not matching_endpoints:
+ raise exceptions.EndpointNotFound()
+
+ # We don't always get a service catalog back ...
+ if not 'serviceCatalog' in self.catalog[self.root_key]:
+ raise exceptions.EndpointNotFound()
+
+ # Full catalog ...
+ catalog = self.catalog[self.root_key]['serviceCatalog']
+
+ for service in catalog:
+ if service.get("type") != self.service_type:
+ continue
+
+ if (self.service_name and self.service_type == 'database' and
+ service.get('name') != self.service_name):
+ continue
+
+ endpoints = service['endpoints']
+ for endpoint in endpoints:
+ if not filter_value or endpoint.get(attr) == filter_value:
+ endpoint["serviceName"] = service.get("name")
+ matching_endpoints.append(endpoint)
+
+ if not matching_endpoints:
+ raise exceptions.EndpointNotFound()
+ elif len(matching_endpoints) > 1:
+ raise exceptions.AmbiguousEndpoints(endpoints=matching_endpoints)
+ else:
+ return matching_endpoints[0].get(endpoint_type, None)