diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-11-19 19:39:36 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-11-19 19:39:36 +0000 |
| commit | c1874df5c02393b24e9cfb508d9fdd5bc08e489f (patch) | |
| tree | 646d9482102f280cc069e9e7a2520685fed1f3e0 | |
| parent | 023762deab2b1715b1aa30cf474cb9779f1b47aa (diff) | |
| parent | e59360da677c4cd3f6a6391cfebb973c11e2ee47 (diff) | |
Merge "Import auth_token middleware from keystoneclient"
| -rw-r--r-- | keystone/middleware/auth_token.py | 843 | ||||
| -rw-r--r-- | tools/pip-requires | 1 | ||||
| -rw-r--r-- | tools/test-requires | 1 |
3 files changed, 12 insertions, 833 deletions
diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py index 86fcb66b..25e2685e 100644 --- a/keystone/middleware/auth_token.py +++ b/keystone/middleware/auth_token.py @@ -16,839 +16,18 @@ # limitations under the License. """ -TOKEN-BASED AUTH MIDDLEWARE - -This WSGI component: - -* Verifies that incoming client requests have valid tokens by validating - tokens with the auth service. -* Rejects unauthenticated requests UNLESS it is in 'delay_auth_decision' - mode, which means the final decision is delegated to the downstream WSGI - component (usually the OpenStack service) -* Collects and forwards identity information based on a valid token - such as user name, tenant, etc - -Refer to: http://keystone.openstack.org/middlewarearchitecture.html - -HEADERS -------- - -* Headers starting with HTTP\_ is a standard http header -* Headers starting with HTTP_X is an extended http header - -Coming in from initial call from client or customer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTH_TOKEN - The client token being passed in. - -HTTP_X_STORAGE_TOKEN - The client token being passed in (legacy Rackspace use) to support - swift/cloud files - -Used for communication between components -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -WWW-Authenticate - HTTP header returned to a user indicating which endpoint to use - to retrieve a new token - -What we add to the request for use by the OpenStack service -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_IDENTITY_STATUS - 'Confirmed' or 'Invalid' - The underlying service will only see a value of 'Invalid' if the Middleware - is configured to run in 'delay_auth_decision' mode - -HTTP_X_TENANT_ID - Identity service managed unique identifier, string - -HTTP_X_TENANT_NAME - Unique tenant identifier, string - -HTTP_X_USER_ID - Identity-service managed unique identifier, string - -HTTP_X_USER_NAME - Unique user identifier, string - -HTTP_X_ROLES - Comma delimited list of case-sensitive Roles - -HTTP_X_SERVICE_CATALOG - json encoded keystone service catalog (optional). - -HTTP_X_TENANT - *Deprecated* in favor of HTTP_X_TENANT_ID and HTTP_X_TENANT_NAME - Keystone-assigned unique identifier, deprecated - -HTTP_X_USER - *Deprecated* in favor of HTTP_X_USER_ID and HTTP_X_USER_NAME - Unique user name, string - -HTTP_X_ROLE - *Deprecated* in favor of HTTP_X_ROLES - This is being renamed, and the new header contains the same data. - +The actual code for auth_token has now been moved python-keystoneclient. It is +imported back here to ensure backward combatibility for old paste.ini files +that might still refer to here as opposed to keystoneclient """ -import datetime -import httplib -import json -import logging -import os -import stat -import time -import webob -import webob.exc - -from keystone.openstack.common import jsonutils -from keystone.common import cms -from keystone.common import utils -from keystone.openstack.common import timeutils - -CONF = None -try: - from openstack.common import cfg - CONF = cfg.CONF -except ImportError: - # cfg is not a library yet, try application copies - for app in 'nova', 'glance', 'quantum', 'cinder': - try: - cfg = __import__('%s.openstack.common.cfg' % app, - fromlist=['%s.openstack.common' % app]) - # test which application middleware is running in - if hasattr(cfg, 'CONF') and 'config_file' in cfg.CONF: - CONF = cfg.CONF - break - except ImportError: - pass -if not CONF: - from keystone.openstack.common import cfg - CONF = cfg.CONF -LOG = logging.getLogger(__name__) - -# alternative middleware configuration in the main application's -# configuration file e.g. in nova.conf -# [keystone_authtoken] -# auth_host = 127.0.0.1 -# auth_port = 35357 -# auth_protocol = http -# admin_tenant_name = admin -# admin_user = admin -# admin_password = badpassword -opts = [ - cfg.StrOpt('auth_admin_prefix', default=''), - cfg.StrOpt('auth_host', default='127.0.0.1'), - cfg.IntOpt('auth_port', default=35357), - cfg.StrOpt('auth_protocol', default='https'), - cfg.StrOpt('auth_uri', default=None), - cfg.BoolOpt('delay_auth_decision', default=False), - cfg.StrOpt('admin_token'), - cfg.StrOpt('admin_user'), - cfg.StrOpt('admin_password'), - cfg.StrOpt('admin_tenant_name', default='admin'), - cfg.StrOpt('certfile'), - cfg.StrOpt('keyfile'), - cfg.StrOpt('signing_dir'), - cfg.ListOpt('memcache_servers'), - cfg.IntOpt('token_cache_time', default=300), -] -CONF.register_opts(opts, group='keystone_authtoken') - - -def will_expire_soon(expiry): - """ Determines if expiration is about to occur. - - :param expiry: a datetime of the expected expiration - :returns: boolean : true if expiration is within 30 seconds - """ - soon = (timeutils.utcnow() + datetime.timedelta(seconds=30)) - return expiry < soon - - -class InvalidUserToken(Exception): - pass - - -class ServiceError(Exception): - pass - - -class ConfigurationError(Exception): - pass - - -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls.""" - - def __init__(self, app, conf): - LOG.info('Starting keystone auth_token middleware') - self.conf = conf - self.app = app - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = (self._conf_get('delay_auth_decision') in - (True, 'true', 't', '1', 'on', 'yes', 'y')) - - # where to find the auth service (we use this to validate tokens) - self.auth_host = self._conf_get('auth_host') - self.auth_port = int(self._conf_get('auth_port')) - self.auth_protocol = self._conf_get('auth_protocol') - if self.auth_protocol == 'http': - self.http_client_class = httplib.HTTPConnection - else: - self.http_client_class = httplib.HTTPSConnection - - self.auth_admin_prefix = self._conf_get('auth_admin_prefix') - self.auth_uri = self._conf_get('auth_uri') - if self.auth_uri is None: - self.auth_uri = '%s://%s:%s' % (self.auth_protocol, - self.auth_host, - self.auth_port) - - # SSL - self.cert_file = self._conf_get('certfile') - self.key_file = self._conf_get('keyfile') - - #signing - self.signing_dirname = self._conf_get('signing_dir') - if self.signing_dirname is None: - self.signing_dirname = '%s/keystone-signing' % os.environ['HOME'] - LOG.info('Using %s as cache directory for signing certificate' % - self.signing_dirname) - if (os.path.exists(self.signing_dirname) and - not os.access(self.signing_dirname, os.W_OK)): - raise ConfigurationError("unable to access signing dir %s" % - self.signing_dirname) - - if not os.path.exists(self.signing_dirname): - os.makedirs(self.signing_dirname) - #will throw IOError if it cannot change permissions - os.chmod(self.signing_dirname, stat.S_IRWXU) - - val = '%s/signing_cert.pem' % self.signing_dirname - self.signing_cert_file_name = val - val = '%s/cacert.pem' % self.signing_dirname - self.ca_file_name = val - val = '%s/revoked.pem' % self.signing_dirname - self.revoked_file_name = val - - # Credentials used to verify this component with the Auth service since - # validating tokens is a privileged call - self.admin_token = self._conf_get('admin_token') - self.admin_token_expiry = None - self.admin_user = self._conf_get('admin_user') - self.admin_password = self._conf_get('admin_password') - self.admin_tenant_name = self._conf_get('admin_tenant_name') - - # Token caching via memcache - self._cache = None - self._iso8601 = None - memcache_servers = self._conf_get('memcache_servers') - # By default the token will be cached for 5 minutes - self.token_cache_time = int(self._conf_get('token_cache_time')) - self._token_revocation_list = None - self._token_revocation_list_fetched_time = None - cache_timeout = datetime.timedelta(seconds=0) - self.token_revocation_list_cache_timeout = cache_timeout - if memcache_servers: - try: - import memcache - import iso8601 - LOG.info('Using memcache for caching token') - self._cache = memcache.Client(memcache_servers.split(',')) - self._iso8601 = iso8601 - except ImportError as e: - LOG.warn('disabled caching due to missing libraries %s', e) - - def _conf_get(self, name): - # try config from paste-deploy first - if name in self.conf: - return self.conf[name] - else: - return CONF.keystone_authtoken[name] - - def __call__(self, env, start_response): - """Handle incoming request. - - Authenticate send downstream on success. Reject request if - we can't authenticate. - - """ - LOG.debug('Authenticating user token') - try: - self._remove_auth_headers(env) - user_token = self._get_user_token_from_header(env) - token_info = self._validate_user_token(user_token) - user_headers = self._build_user_headers(token_info) - self._add_headers(env, user_headers) - return self.app(env, start_response) - - except InvalidUserToken: - if self.delay_auth_decision: - LOG.info('Invalid user token - deferring reject downstream') - self._add_headers(env, {'X-Identity-Status': 'Invalid'}) - return self.app(env, start_response) - else: - LOG.info('Invalid user token - rejecting request') - return self._reject_request(env, start_response) - - except ServiceError as e: - LOG.critical('Unable to obtain admin token: %s' % e) - resp = webob.exc.HTTPServiceUnavailable() - return resp(env, start_response) - - def _remove_auth_headers(self, env): - """Remove headers so a user can't fake authentication. - - :param env: wsgi request environment - - """ - auth_headers = ( - 'X-Identity-Status', - 'X-Tenant-Id', - 'X-Tenant-Name', - 'X-User-Id', - 'X-User-Name', - 'X-Roles', - 'X-Service-Catalog', - # Deprecated - 'X-User', - 'X-Tenant', - 'X-Role', - ) - LOG.debug('Removing headers from request environment: %s' % - ','.join(auth_headers)) - self._remove_headers(env, auth_headers) - - def _get_user_token_from_header(self, env): - """Get token id from request. - - :param env: wsgi request environment - :return token id - :raises InvalidUserToken if no token is provided in request - - """ - token = self._get_header(env, 'X-Auth-Token', - self._get_header(env, 'X-Storage-Token')) - if token: - return token - else: - LOG.warn("Unable to find authentication token in headers: %s", env) - raise InvalidUserToken('Unable to find token in headers') - - def _reject_request(self, env, start_response): - """Redirect client to auth server. - - :param env: wsgi request environment - :param start_response: wsgi response callback - :returns HTTPUnauthorized http response - - """ - headers = [('WWW-Authenticate', 'Keystone uri=\'%s\'' % self.auth_uri)] - resp = webob.exc.HTTPUnauthorized('Authentication required', headers) - return resp(env, start_response) - - def get_admin_token(self): - """Return admin token, possibly fetching a new one. - - if self.admin_token_expiry is set from fetching an admin token, check - it for expiration, and request a new token is the existing token - is about to expire. - - :return admin token id - :raise ServiceError when unable to retrieve token from keystone - - """ - if self.admin_token_expiry: - if will_expire_soon(self.admin_token_expiry): - self.admin_token = None - - if not self.admin_token: - (self.admin_token, - self.admin_token_expiry) = self._request_admin_token() - - return self.admin_token - - def _get_http_connection(self): - if self.auth_protocol == 'http': - return self.http_client_class(self.auth_host, self.auth_port) - else: - return self.http_client_class(self.auth_host, - self.auth_port, - self.key_file, - self.cert_file) - - def _http_request(self, method, path): - """HTTP request helper used to make unspecified content type requests. - - :param method: http method - :param path: relative request url - :return (http response object) - :raise ServerError when unable to communicate with keystone - - """ - conn = self._get_http_connection() - - try: - conn.request(method, path) - response = conn.getresponse() - body = response.read() - except Exception as e: - LOG.error('HTTP connection exception: %s' % e) - raise ServiceError('Unable to communicate with keystone') - finally: - conn.close() - - return response, body - - def _json_request(self, method, path, body=None, additional_headers=None): - """HTTP request helper used to make json requests. - - :param method: http method - :param path: relative request url - :param body: dict to encode to json as request body. Optional. - :param additional_headers: dict of additional headers to send with - http request. Optional. - :return (http response object, response body parsed as json) - :raise ServerError when unable to communicate with keystone - - """ - conn = self._get_http_connection() - - kwargs = { - 'headers': { - 'Content-type': 'application/json', - 'Accept': 'application/json', - }, - } - - if additional_headers: - kwargs['headers'].update(additional_headers) - - if body: - kwargs['body'] = jsonutils.dumps(body) - - full_path = self.auth_admin_prefix + path - try: - conn.request(method, full_path, **kwargs) - response = conn.getresponse() - body = response.read() - except Exception as e: - LOG.error('HTTP connection exception: %s' % e) - raise ServiceError('Unable to communicate with keystone') - finally: - conn.close() - - try: - data = jsonutils.loads(body) - except ValueError: - LOG.debug('Keystone did not return json-encoded body') - data = {} - - return response, data - - def _request_admin_token(self): - """Retrieve new token as admin user from keystone. - - :return token id upon success - :raises ServerError when unable to communicate with keystone - - """ - params = { - 'auth': { - 'passwordCredentials': { - 'username': self.admin_user, - 'password': self.admin_password, - }, - 'tenantName': self.admin_tenant_name, - } - } - - response, data = self._json_request('POST', - '/v2.0/tokens', - body=params) - - try: - token = data['access']['token']['id'] - expiry = data['access']['token']['expires'] - assert token - assert expiry - datetime_expiry = timeutils.parse_isotime(expiry) - return (token, timeutils.normalize_time(datetime_expiry)) - except (AssertionError, KeyError): - LOG.warn("Unexpected response from keystone service: %s", data) - raise ServiceError('invalid json response') - except (ValueError): - LOG.warn("Unable to parse expiration time from token: %s", data) - raise ServiceError('invalid json response') - - def _validate_user_token(self, user_token, retry=True): - """Authenticate user using PKI - - :param user_token: user's token id - :param retry: Ignored, as it is not longer relevant - :return uncrypted body of the token if the token is valid - :raise InvalidUserToken if token is rejected - :no longer raises ServiceError since it no longer makes RPC - - """ - try: - token_id = cms.cms_hash_token(user_token) - cached = self._cache_get(token_id) - if cached: - return cached - if cms.is_ans1_token(user_token): - verified = self.verify_signed_token(user_token) - data = json.loads(verified) - else: - data = self.verify_uuid_token(user_token, retry) - self._cache_put(token_id, data) - return data - except Exception as e: - LOG.debug('Token validation failure.', exc_info=True) - self._cache_store_invalid(user_token) - LOG.warn("Authorization failed for token %s", user_token) - raise InvalidUserToken('Token authorization failed') - - def _build_user_headers(self, token_info): - """Convert token object into headers. - - Build headers that represent authenticated user: - * X_IDENTITY_STATUS: Confirmed or Invalid - * X_TENANT_ID: id of tenant if tenant is present - * X_TENANT_NAME: name of tenant if tenant is present - * X_USER_ID: id of user - * X_USER_NAME: name of user - * X_ROLES: list of roles - * X_SERVICE_CATALOG: service catalog - - Additional (deprecated) headers include: - * X_USER: name of user - * X_TENANT: For legacy compatibility before we had ID and Name - * X_ROLE: list of roles - - :param token_info: token object returned by keystone on authentication - :raise InvalidUserToken when unable to parse token object - - """ - user = token_info['access']['user'] - token = token_info['access']['token'] - roles = ','.join([role['name'] for role in user.get('roles', [])]) - - def get_tenant_info(): - """Returns a (tenant_id, tenant_name) tuple from context.""" - def essex(): - """Essex puts the tenant ID and name on the token.""" - return (token['tenant']['id'], token['tenant']['name']) - - def pre_diablo(): - """Pre-diablo, Keystone only provided tenantId.""" - return (token['tenantId'], token['tenantId']) - - def default_tenant(): - """Assume the user's default tenant.""" - return (user['tenantId'], user['tenantName']) - - for method in [essex, pre_diablo, default_tenant]: - try: - return method() - except KeyError: - pass - - raise InvalidUserToken('Unable to determine tenancy.') - - tenant_id, tenant_name = get_tenant_info() - - user_id = user['id'] - user_name = user['name'] - - rval = { - 'X-Identity-Status': 'Confirmed', - 'X-Tenant-Id': tenant_id, - 'X-Tenant-Name': tenant_name, - 'X-User-Id': user_id, - 'X-User-Name': user_name, - 'X-Roles': roles, - # Deprecated - 'X-User': user_name, - 'X-Tenant': tenant_name, - 'X-Role': roles, - } - - try: - catalog = token_info['access']['serviceCatalog'] - rval['X-Service-Catalog'] = jsonutils.dumps(catalog) - except KeyError: - pass - - return rval - - def _header_to_env_var(self, key): - """Convert header to wsgi env variable. - - :param key: http header name (ex. 'X-Auth-Token') - :return wsgi env variable name (ex. 'HTTP_X_AUTH_TOKEN') - - """ - return 'HTTP_%s' % key.replace('-', '_').upper() - - def _add_headers(self, env, headers): - """Add http headers to environment.""" - for (k, v) in headers.iteritems(): - env_key = self._header_to_env_var(k) - env[env_key] = v - - def _remove_headers(self, env, keys): - """Remove http headers from environment.""" - for k in keys: - env_key = self._header_to_env_var(k) - try: - del env[env_key] - except KeyError: - pass - - def _get_header(self, env, key, default=None): - """Get http header from environment.""" - env_key = self._header_to_env_var(key) - return env.get(env_key, default) - - def _cache_get(self, token): - """Return token information from cache. - - If token is invalid raise InvalidUserToken - return token only if fresh (not expired). - """ - if self._cache and token: - key = 'tokens/%s' % token - cached = self._cache.get(key) - if cached == 'invalid': - LOG.debug('Cached Token %s is marked unauthorized', token) - raise InvalidUserToken('Token authorization failed') - if cached: - data, expires = cached - if time.time() < float(expires): - LOG.debug('Returning cached token %s', token) - return data - else: - LOG.debug('Cached Token %s seems expired', token) - - def _cache_put(self, token, data): - """Put token data into the cache. - - Stores the parsed expire date in cache allowing - quick check of token freshness on retrieval. - """ - if self._cache and data: - key = 'tokens/%s' % token - if 'token' in data.get('access', {}): - timestamp = data['access']['token']['expires'] - expires = self._iso8601.parse_date(timestamp).strftime('%s') - else: - LOG.error('invalid token format') - return - LOG.debug('Storing %s token in memcache', token) - self._cache.set(key, - (data, expires), - time=self.token_cache_time) - - def _cache_store_invalid(self, token): - """Store invalid token in cache.""" - if self._cache: - key = 'tokens/%s' % token - LOG.debug('Marking token %s as unauthorized in memcache', token) - self._cache.set(key, - 'invalid', - time=self.token_cache_time) - - def cert_file_missing(self, called_proc_err, file_name): - return (called_proc_err.output.find(file_name) - and not os.path.exists(file_name)) - - def verify_uuid_token(self, user_token, retry=True): - """Authenticate user token with keystone. - - :param user_token: user's token id - :param retry: flag that forces the middleware to retry - user authentication when an indeterminate - response is received. Optional. - :return token object received from keystone on success - :raise InvalidUserToken if token is rejected - :raise ServiceError if unable to authenticate token - - """ - - headers = {'X-Auth-Token': self.get_admin_token()} - response, data = self._json_request('GET', - '/v2.0/tokens/%s' % user_token, - additional_headers=headers) - - if response.status == 200: - self._cache_put(user_token, data) - return data - if response.status == 404: - # FIXME(ja): I'm assuming the 404 status means that user_token is - # invalid - not that the admin_token is invalid - self._cache_store_invalid(user_token) - LOG.warn("Authorization failed for token %s", user_token) - raise InvalidUserToken('Token authorization failed') - if response.status == 401: - LOG.info('Keystone rejected admin token %s, resetting', headers) - self.admin_token = None - else: - LOG.error('Bad response code while validating token: %s' % - response.status) - if retry: - LOG.info('Retrying validation') - return self._validate_user_token(user_token, False) - else: - LOG.warn("Invalid user token: %s. Keystone response: %s.", - user_token, data) - - raise InvalidUserToken() - - def is_signed_token_revoked(self, signed_text): - """Indicate whether the token appears in the revocation list.""" - revocation_list = self.token_revocation_list - revoked_tokens = revocation_list.get('revoked', []) - if not revoked_tokens: - return - revoked_ids = (x['id'] for x in revoked_tokens) - token_id = utils.hash_signed_token(signed_text) - for revoked_id in revoked_ids: - if token_id == revoked_id: - LOG.debug('Token %s is marked as having been revoked', - token_id) - return True - return False - - def cms_verify(self, data): - """Verifies the signature of the provided data's IAW CMS syntax. - - If either of the certificate files are missing, fetch them and - retry. - """ - while True: - try: - output = cms.cms_verify(data, self.signing_cert_file_name, - self.ca_file_name) - except cms.subprocess.CalledProcessError as err: - if self.cert_file_missing(err, self.signing_cert_file_name): - self.fetch_signing_cert() - continue - if self.cert_file_missing(err, self.ca_file_name): - self.fetch_ca_cert() - continue - raise err - return output - - def verify_signed_token(self, signed_text): - """Check that the token is unrevoked and has a valid signature.""" - if self.is_signed_token_revoked(signed_text): - raise InvalidUserToken('Token has been revoked') - - formatted = cms.token_to_cms(signed_text) - return self.cms_verify(formatted) - - @property - def token_revocation_list_fetched_time(self): - if not self._token_revocation_list_fetched_time: - # If the fetched list has been written to disk, use its - # modification time. - if os.path.exists(self.revoked_file_name): - mtime = os.path.getmtime(self.revoked_file_name) - fetched_time = datetime.datetime.fromtimestamp(mtime) - # Otherwise the list will need to be fetched. - else: - fetched_time = datetime.datetime.min - self._token_revocation_list_fetched_time = fetched_time - return self._token_revocation_list_fetched_time - - @token_revocation_list_fetched_time.setter - def token_revocation_list_fetched_time(self, value): - self._token_revocation_list_fetched_time = value - - @property - def token_revocation_list(self): - timeout = (self.token_revocation_list_fetched_time + - self.token_revocation_list_cache_timeout) - list_is_current = timeutils.utcnow() < timeout - if list_is_current: - # Load the list from disk if required - if not self._token_revocation_list: - with open(self.revoked_file_name, 'r') as f: - self._token_revocation_list = jsonutils.loads(f.read()) - else: - self.token_revocation_list = self.fetch_revocation_list() - return self._token_revocation_list - - @token_revocation_list.setter - def token_revocation_list(self, value): - """Save a revocation list to memory and to disk. - - :param value: A json-encoded revocation list - - """ - self._token_revocation_list = jsonutils.loads(value) - self.token_revocation_list_fetched_time = timeutils.utcnow() - with open(self.revoked_file_name, 'w') as f: - f.write(value) - - def fetch_revocation_list(self, retry=True): - headers = {'X-Auth-Token': self.get_admin_token()} - response, data = self._json_request('GET', '/v2.0/tokens/revoked', - additional_headers=headers) - if response.status == 401: - if retry: - LOG.info('Keystone rejected admin token %s, resetting admin ' - 'token', headers) - self.admin_token = None - return self.fetch_revocation_list(retry=False) - if response.status != 200: - raise ServiceError('Unable to fetch token revocation list.') - if (not 'signed' in data): - raise ServiceError('Revocation list inmproperly formatted.') - return self.cms_verify(data['signed']) - - def fetch_signing_cert(self): - response, data = self._http_request('GET', - '/v2.0/certificates/signing') - try: - #todo check response - certfile = open(self.signing_cert_file_name, 'w') - certfile.write(data) - certfile.close() - except (AssertionError, KeyError): - LOG.warn("Unexpected response from keystone service: %s", data) - raise ServiceError('invalid json response') - - def fetch_ca_cert(self): - response, data = self._http_request('GET', - '/v2.0/certificates/ca') - try: - #todo check response - certfile = open(self.ca_file_name, 'w') - certfile.write(data) - certfile.close() - except (AssertionError, KeyError): - LOG.warn("Unexpected response from keystone service: %s", data) - raise ServiceError('invalid json response') - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return AuthProtocol(app, conf) - return auth_filter +from keystoneclient.middleware import auth_token as client_auth_token +will_expire_soon = client_auth_token.will_expire_soon +InvalidUserToken = client_auth_token.InvalidUserToken +ServiceError = client_auth_token.ServiceError +ConfigurationError = client_auth_token.ConfigurationError +AuthProtocol = client_auth_token.AuthProtocol -def app_factory(global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) +filter_factory = client_auth_token.filter_factory +app_factory = client_auth_token.app_factory diff --git a/tools/pip-requires b/tools/pip-requires index 572d5d47..ae6885bd 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -11,3 +11,4 @@ sqlalchemy-migrate>=0.7.2 passlib lxml iso8601>=0.1.4 +python-keystoneclient>=0.2,<0.3 diff --git a/tools/test-requires b/tools/test-requires index cb7723e8..dd60ef22 100644 --- a/tools/test-requires +++ b/tools/test-requires @@ -20,7 +20,6 @@ distribute>=0.6.24 # for python-keystoneclient httplib2 -python-keystoneclient>=0.1,<0.2 # swift_auth test dependencies http://tarballs.openstack.org/swift/swift-master.tar.gz#egg=swift |
