summaryrefslogtreecommitdiffstats
path: root/keystone/middleware
diff options
context:
space:
mode:
authorHenry Nash <henryn@linux.vnet.ibm.com>2012-11-17 14:45:18 +0000
committerBrian Waldon <bcwaldon@gmail.com>2012-11-19 09:50:17 -0800
commite59360da677c4cd3f6a6391cfebb973c11e2ee47 (patch)
tree4d0d2685c709c73454af82bbb3ee4c803b0af1ab /keystone/middleware
parent71692f7805b62329f7367a120700b6ed050b20b4 (diff)
downloadkeystone-e59360da677c4cd3f6a6391cfebb973c11e2ee47.tar.gz
keystone-e59360da677c4cd3f6a6391cfebb973c11e2ee47.tar.xz
keystone-e59360da677c4cd3f6a6391cfebb973c11e2ee47.zip
Import auth_token middleware from keystoneclient
Although the master auth_token file is now in keystoneclient, it will take some time to get all the paste files to point to it there rather than here. Hence, we import it back here to provide backward compatibility for a release or so, after which we will remove it from the server. Change-Id: Iccdb7839a611cdda233e4ea96f68c64d6d82f49c
Diffstat (limited to 'keystone/middleware')
-rw-r--r--keystone/middleware/auth_token.py843
1 files changed, 11 insertions, 832 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