summaryrefslogtreecommitdiffstats
path: root/keystone/auth/token_factory.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone/auth/token_factory.py')
-rw-r--r--keystone/auth/token_factory.py238
1 files changed, 238 insertions, 0 deletions
diff --git a/keystone/auth/token_factory.py b/keystone/auth/token_factory.py
new file mode 100644
index 00000000..e803ae79
--- /dev/null
+++ b/keystone/auth/token_factory.py
@@ -0,0 +1,238 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+"""Token Factory"""
+
+import json
+import uuid
+import webob
+
+from keystone.common import cms
+from keystone.common import logging
+from keystone.common import utils
+from keystone import catalog
+from keystone import config
+from keystone import exception
+from keystone import identity
+from keystone import token as token_module
+from keystone.openstack.common import jsonutils
+from keystone.openstack.common import timeutils
+
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class TokenDataHelper(object):
+ """Token data helper."""
+ def __init__(self, context):
+ self.identity_api = identity.Manager()
+ self.catalog_api = catalog.Manager()
+ self.context = context
+
+ def _get_filtered_domain(self, domain_id):
+ domain_ref = self.identity_api.get_domain(self.context,
+ domain_id)
+ return {'id': domain_ref['id'], 'name': domain_ref['name']}
+
+ def _populate_scope(self, token_data, domain_id, project_id):
+ if domain_id:
+ token_data['domain'] = self._get_filtered_domain(domain_id)
+ if project_id:
+ project_ref = self.identity_api.get_project(
+ self.context, project_id)
+ filtered_project = {
+ 'id': project_ref['id'],
+ 'name': project_ref['name']}
+ filtered_project['domain'] = self._get_filtered_domain(
+ project_ref['domain_id'])
+ token_data['project'] = filtered_project
+
+ def _get_project_roles_for_user(self, user_id, project_id):
+ roles = self.identity_api.get_roles_for_user_and_project(
+ self.context, user_id, project_id)
+ roles_ref = []
+ for role_id in roles:
+ role_ref = self.identity_api.get_role(self.context, role_id)
+ role_ref.setdefault('project_id', project_id)
+ roles_ref.append(role_ref)
+ # user have no project roles, therefore access denied
+ if len(roles_ref) == 0:
+ msg = _('User have no access to project')
+ LOG.debug(msg)
+ raise exception.Unauthorized(msg)
+ return roles_ref
+
+ def _get_roles_for_user(self, user_id, domain_id, project_id):
+ roles = []
+ if domain_id:
+ # TODO(gyee): get domain roles
+ pass
+ if project_id:
+ roles = self._get_project_roles_for_user(user_id, project_id)
+ return roles
+
+ def _populate_user(self, token_data, user_id, domain_id, project_id):
+ user_ref = self.identity_api.get_user(self.context,
+ user_id)
+ filtered_user = {
+ 'id': user_ref['id'],
+ 'name': user_ref['name'],
+ 'domain': self._get_filtered_domain(user_ref['domain_id'])}
+ token_data['user'] = filtered_user
+
+ def _populate_roles(self, token_data, user_id, domain_id, project_id):
+ if domain_id or project_id:
+ roles = self._get_roles_for_user(user_id, domain_id, project_id)
+ # we only care about id and name
+ filtered_roles = []
+ for role in roles:
+ filtered_roles.append({'id': role['id'], 'name': role['name']})
+ token_data['roles'] = filtered_roles
+
+ def _populate_service_catalog(self, token_data, user_id,
+ domain_id, project_id):
+ service_catalog = self.catalog_api.get_v3_catalog(self.context,
+ user_id,
+ project_id)
+ # TODO(gyee): v3 service catalog is not quite completed yet
+ token_data['catalog'] = service_catalog
+
+ def _populate_token(self, token_data, expires=None):
+ if not expires:
+ expires = token_module.default_expire_time()
+ if not isinstance(expires, unicode):
+ expires = timeutils.isotime(expires)
+ token_data['expires'] = expires
+ token_data['issued_at'] = timeutils.strtime()
+
+ def get_token_data(self, user_id, method_names, extras,
+ domain_id=None, project_id=None, expires=None):
+ token_data = {'methods': method_names,
+ 'extras': extras}
+ self._populate_scope(token_data, domain_id, project_id)
+ self._populate_user(token_data, user_id, domain_id, project_id)
+ self._populate_roles(token_data, user_id, domain_id, project_id)
+ self._populate_service_catalog(token_data, user_id, domain_id,
+ project_id)
+ self._populate_token(token_data, expires)
+ return token_data
+
+
+def recreate_token_data(context, token_data=None, expires=None,
+ user_ref=None, project_ref=None):
+ """ Recreate token from an existing token.
+
+ Repopulate the ephemeral data and return the new token data.
+
+ """
+ new_expires = expires
+ project_id = None
+ user_id = None
+ domain_id = None
+ methods = ['password', 'token']
+ extras = {}
+ if token_data:
+ domain_id = (token_data['domain']['id'] if 'domain' in token_data
+ else None)
+ project_id = (token_data['project']['id'] if 'project' in token_data
+ else None)
+ if not new_expires:
+ new_expires = token_data['expires']
+ user_id = token_data['user']['id']
+ methods = token_data['methods']
+ extras = token_data['extras']
+ else:
+ project_id = project_ref['id']
+ user_id = user_ref['id']
+ token_data_helper = TokenDataHelper(context)
+ return token_data_helper.get_token_data(user_id,
+ methods,
+ extras,
+ domain_id,
+ project_id,
+ new_expires)
+
+
+def create_token(context, auth_context, auth_info):
+ token_data_helper = TokenDataHelper(context)
+ (domain_id, project_id) = auth_info.get_scope()
+ method_names = list(set(auth_info.get_method_names() +
+ auth_context.get('method_names', [])))
+ token_data = token_data_helper.get_token_data(auth_context['user_id'],
+ method_names,
+ auth_context['extras'],
+ domain_id,
+ project_id,
+ auth_context.get('expires',
+ None))
+ if CONF.signing.token_format == 'UUID':
+ token_id = uuid.uuid4().hex
+ elif CONF.signing.token_format == 'PKI':
+ token_id = cms.cms_sign_token(json.dumps(token_data),
+ CONF.signing.certfile,
+ CONF.signing.keyfile)
+ else:
+ raise exception.UnexpectedError(
+ 'Invalid value for token_format: %s.'
+ ' Allowed values are PKI or UUID.' %
+ CONF.signing.token_format)
+ token_api = token_module.Manager()
+ try:
+ expiry = token_data['expires']
+ if isinstance(expiry, basestring):
+ expiry = timeutils.parse_isotime(expiry)
+ role_ids = []
+ if 'project' in token_data:
+ # project-scoped token, fill in the v2 token data
+ # all we care are the role IDs
+ role_ids = [role['id'] for role in token_data['roles']]
+ metadata_ref = {'roles': role_ids}
+ data = dict(key=token_id,
+ id=token_id,
+ expires=expiry,
+ user=token_data['user'],
+ tenant=token_data.get('project'),
+ metadata=metadata_ref,
+ token_data=token_data)
+ token_api.create_token(context, token_id, data)
+ except Exception as e:
+ # an identical token may have been created already.
+ # if so, return the token_data as it is also identical
+ try:
+ token_api.get_token(context=context,
+ token_id=token_id)
+ except exception.TokenNotFound:
+ raise e
+
+ return (token_id, token_data)
+
+
+def render_token_data_response(token_id, token_data):
+ """ Render token data HTTP response.
+
+ Stash token ID into the X-Auth-Token header.
+
+ """
+ headers = [('X-Subject-Token', token_id)]
+ headers.append(('Vary', 'X-Auth-Token'))
+ headers.append(('Content-Type', 'application/json'))
+ status = (200, 'OK')
+ body = jsonutils.dumps(token_data, cls=utils.SmarterEncoder)
+ return webob.Response(body=body,
+ status='%s %s' % status,
+ headerlist=headers)