OpenStack Identity (Keystone)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

737 lines
31KB

  1. # Copyright 2013 OpenStack Foundation
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. from __future__ import absolute_import
  15. import base64
  16. import datetime
  17. import itertools
  18. import uuid
  19. from oslo_log import log
  20. from oslo_serialization import jsonutils
  21. from oslo_utils import timeutils
  22. import six
  23. from six.moves.urllib import parse
  24. from keystone.common import provider_api
  25. from keystone.common import utils
  26. import keystone.conf
  27. from keystone import exception
  28. from keystone.federation import constants as federation_constants
  29. from keystone.i18n import _
  30. from keystone.models import token_model
  31. from keystone.token.providers import base
  32. LOG = log.getLogger(__name__)
  33. CONF = keystone.conf.CONF
  34. PROVIDERS = provider_api.ProviderAPIs
  35. def default_expire_time():
  36. """Determine when a fresh token should expire.
  37. Expiration time varies based on configuration (see ``[token] expiration``).
  38. :returns: a naive UTC datetime.datetime object
  39. """
  40. expire_delta = datetime.timedelta(seconds=CONF.token.expiration)
  41. expires_at = timeutils.utcnow() + expire_delta
  42. return expires_at.replace(microsecond=0)
  43. def random_urlsafe_str():
  44. """Generate a random URL-safe string.
  45. :rtype: six.text_type
  46. """
  47. # chop the padding (==) off the end of the encoding to save space
  48. return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2].decode('utf-8')
  49. def build_audit_info(parent_audit_id=None):
  50. """Build the audit data for a token.
  51. If ``parent_audit_id`` is None, the list will be one element in length
  52. containing a newly generated audit_id.
  53. If ``parent_audit_id`` is supplied, the list will be two elements in length
  54. containing a newly generated audit_id and the ``parent_audit_id``. The
  55. ``parent_audit_id`` will always be element index 1 in the resulting
  56. list.
  57. :param parent_audit_id: the audit of the original token in the chain
  58. :type parent_audit_id: str
  59. :returns: Keystone token audit data
  60. """
  61. audit_id = random_urlsafe_str()
  62. if parent_audit_id is not None:
  63. return [audit_id, parent_audit_id]
  64. return [audit_id]
  65. class V3TokenDataHelper(provider_api.ProviderAPIMixin, object):
  66. """Token data helper."""
  67. def __init__(self):
  68. # Keep __init__ around to ensure dependency injection works.
  69. super(V3TokenDataHelper, self).__init__()
  70. def _get_filtered_domain(self, domain_id):
  71. """Ensure the domain is enabled and return domain id and name.
  72. :param domain_id: The ID of the domain to validate
  73. :returns: A dictionary containing two keys, the `id` of the domain and
  74. the `name` of the domain.
  75. """
  76. domain_ref = PROVIDERS.resource_api.get_domain(domain_id)
  77. if not domain_ref.get('enabled'):
  78. msg = _('Unable to validate token because domain %(id)s is '
  79. 'disabled') % {'id': domain_ref['id']}
  80. LOG.warning(msg)
  81. raise exception.DomainNotFound(msg)
  82. return {'id': domain_ref['id'], 'name': domain_ref['name']}
  83. def _get_filtered_project(self, project_id):
  84. """Ensure the project and parent domain is enabled.
  85. :param project_id: The ID of the project to validate
  86. :return: A dictionary containing up to three keys, the `id` of the
  87. project, the `name` of the project, and the parent `domain`.
  88. """
  89. project_ref = PROVIDERS.resource_api.get_project(project_id)
  90. if not project_ref.get('enabled'):
  91. msg = _('Unable to validate token because project %(id)s is '
  92. 'disabled') % {'id': project_ref['id']}
  93. LOG.warning(msg)
  94. raise exception.ProjectNotFound(msg)
  95. filtered_project = {
  96. 'id': project_ref['id'],
  97. 'name': project_ref['name']}
  98. if project_ref['domain_id'] is not None:
  99. filtered_project['domain'] = (
  100. self._get_filtered_domain(project_ref['domain_id']))
  101. else:
  102. # Projects acting as a domain do not have a domain_id attribute
  103. filtered_project['domain'] = None
  104. return filtered_project
  105. def _populate_scope(self, token_data, system, domain_id, project_id):
  106. if 'domain' in token_data or 'project' in token_data:
  107. # scope already exist, no need to populate it again
  108. return
  109. if domain_id:
  110. token_data['domain'] = self._get_filtered_domain(domain_id)
  111. elif project_id:
  112. token_data['project'] = self._get_filtered_project(project_id)
  113. project_ref = PROVIDERS.resource_api.get_project(project_id)
  114. token_data['is_domain'] = project_ref['is_domain']
  115. elif system == 'all':
  116. # NOTE(lbragstad): This might have to be more elegant in the future
  117. # if, or when, keystone supports scoping a token to a specific
  118. # service or region.
  119. token_data['system'] = {'all': True}
  120. def _populate_is_admin_project(self, token_data):
  121. # TODO(ayoung): Support the ability for a project acting as a domain
  122. # to be the admin project once the rest of the code for projects
  123. # acting as domains is merged. Code will likely be:
  124. # (r.admin_project_name == None and project['is_domain'] == True
  125. # and project['name'] == r.admin_project_domain_name)
  126. admin_project_name = CONF.resource.admin_project_name
  127. admin_project_domain_name = CONF.resource.admin_project_domain_name
  128. if not (admin_project_name and admin_project_domain_name):
  129. return # admin project not enabled
  130. project = token_data['project']
  131. token_data['is_admin_project'] = (
  132. project['name'] == admin_project_name and
  133. project['domain']['name'] == admin_project_domain_name)
  134. def _get_roles_for_user(self, user_id, system, domain_id, project_id):
  135. roles = []
  136. if system:
  137. group_ids = [
  138. group['id'] for
  139. group in PROVIDERS.identity_api.list_groups_for_user(user_id)
  140. ]
  141. group_roles = []
  142. for group_id in group_ids:
  143. roles = PROVIDERS.assignment_api.list_system_grants_for_group(
  144. group_id
  145. )
  146. for role in roles:
  147. group_roles.append(role)
  148. user_roles = PROVIDERS.assignment_api.list_system_grants_for_user(
  149. user_id
  150. )
  151. return itertools.chain(group_roles, user_roles)
  152. if domain_id:
  153. roles = PROVIDERS.assignment_api.get_roles_for_user_and_domain(
  154. user_id, domain_id)
  155. if project_id:
  156. roles = PROVIDERS.assignment_api.get_roles_for_user_and_project(
  157. user_id, project_id)
  158. return [PROVIDERS.role_api.get_role(role_id) for role_id in roles]
  159. def _get_app_cred_roles(self, app_cred, user_id, domain_id, project_id):
  160. roles = app_cred['roles']
  161. token_roles = []
  162. assignment_list = PROVIDERS.assignment_api.list_role_assignments(
  163. user_id=user_id, project_id=project_id, domain_id=domain_id,
  164. effective=True
  165. )
  166. user_roles = list(set([x['role_id'] for x in assignment_list]))
  167. for role in roles:
  168. if role['id'] in user_roles:
  169. token_roles.append({'id': role['id'], 'name': role['name']})
  170. return roles
  171. def populate_roles_for_federated_user(self, token_data, group_ids,
  172. project_id=None, domain_id=None,
  173. user_id=None, system=None):
  174. """Populate roles basing on provided groups and assignments.
  175. Used for federated users with dynamically assigned groups.
  176. This method does not return anything, yet it modifies token_data in
  177. place.
  178. :param token_data: a dictionary used for building token response
  179. :param group_ids: list of group IDs a user is a member of
  180. :param project_id: project ID to scope to
  181. :param domain_id: domain ID to scope to
  182. :param user_id: user ID
  183. :param system: system scope if applicable
  184. :raises keystone.exception.Unauthorized: when no roles were found
  185. """
  186. def check_roles(roles, user_id, project_id, domain_id):
  187. # User was granted roles so simply exit this function.
  188. if roles:
  189. return
  190. if project_id:
  191. msg = _('User %(user_id)s has no access '
  192. 'to project %(project_id)s') % {
  193. 'user_id': user_id,
  194. 'project_id': project_id}
  195. elif domain_id:
  196. msg = _('User %(user_id)s has no access '
  197. 'to domain %(domain_id)s') % {
  198. 'user_id': user_id,
  199. 'domain_id': domain_id}
  200. # Since no roles were found a user is not authorized to
  201. # perform any operations. Raise an exception with
  202. # appropriate error message.
  203. raise exception.Unauthorized(msg)
  204. roles = PROVIDERS.assignment_api.get_roles_for_groups(
  205. group_ids, project_id, domain_id
  206. )
  207. roles = roles + self._get_roles_for_user(
  208. user_id, system, domain_id, project_id
  209. )
  210. # NOTE(lbragstad): Remove duplicate role references from a list of
  211. # roles. It is often suggested that this be done with:
  212. #
  213. # roles = [dict(t) for t in set([tuple(d.items()) for d in roles])]
  214. #
  215. # But that doesn't actually remove duplicates in all cases and causes
  216. # transient failures because dictionaries are unordered objects. This
  217. # means {'id': 1, 'foo': 'bar'} and {'foo': 'bar', 'id': 1} won't
  218. # actually resolve to a single entity in the above logic since they are
  219. # both considered unique. By using `in` we're performing a containment
  220. # check, which also does a deep comparison of the objects, which is
  221. # what we want.
  222. unique_roles = []
  223. for role in roles:
  224. if role not in unique_roles:
  225. unique_roles.append(role)
  226. check_roles(unique_roles, user_id, project_id, domain_id)
  227. token_data['roles'] = unique_roles
  228. def _populate_user(self, token_data, user_id, trust):
  229. if 'user' in token_data:
  230. # no need to repopulate user if it already exists
  231. return
  232. user_ref = PROVIDERS.identity_api.get_user(user_id)
  233. if CONF.trust.enabled and trust and 'OS-TRUST:trust' not in token_data:
  234. trustor_user_ref = (PROVIDERS.identity_api.get_user(
  235. trust['trustor_user_id']))
  236. trustee_user_ref = (PROVIDERS.identity_api.get_user(
  237. trust['trustee_user_id']))
  238. try:
  239. PROVIDERS.resource_api.assert_domain_enabled(
  240. trustor_user_ref['domain_id'])
  241. except AssertionError:
  242. raise exception.TokenNotFound(_('Trustor domain is disabled.'))
  243. try:
  244. PROVIDERS.resource_api.assert_domain_enabled(
  245. trustee_user_ref['domain_id'])
  246. except AssertionError:
  247. raise exception.TokenNotFound(_('Trustee domain is disabled.'))
  248. try:
  249. PROVIDERS.identity_api.assert_user_enabled(
  250. trust['trustor_user_id']
  251. )
  252. except AssertionError:
  253. raise exception.Forbidden(_('Trustor is disabled.'))
  254. if trust['impersonation']:
  255. user_ref = trustor_user_ref
  256. token_data['OS-TRUST:trust'] = (
  257. {
  258. 'id': trust['id'],
  259. 'trustor_user': {'id': trust['trustor_user_id']},
  260. 'trustee_user': {'id': trust['trustee_user_id']},
  261. 'impersonation': trust['impersonation']
  262. })
  263. filtered_user = {
  264. 'id': user_ref['id'],
  265. 'name': user_ref['name'],
  266. 'domain': self._get_filtered_domain(user_ref['domain_id']),
  267. 'password_expires_at': user_ref['password_expires_at']}
  268. token_data['user'] = filtered_user
  269. def _populate_oauth_section(self, token_data, access_token):
  270. if access_token:
  271. access_token_id = access_token['id']
  272. consumer_id = access_token['consumer_id']
  273. token_data['OS-OAUTH1'] = ({'access_token_id': access_token_id,
  274. 'consumer_id': consumer_id})
  275. def _populate_roles(self, token_data, user_id, system, domain_id,
  276. project_id, trust, app_cred_id, access_token):
  277. if 'roles' in token_data:
  278. # no need to repopulate roles
  279. return
  280. if access_token:
  281. filtered_roles = []
  282. access_token_ref = PROVIDERS.oauth_api.get_access_token(
  283. access_token['id']
  284. )
  285. authed_role_ids = jsonutils.loads(access_token_ref['role_ids'])
  286. all_roles = PROVIDERS.role_api.list_roles()
  287. for role in all_roles:
  288. for authed_role in authed_role_ids:
  289. if authed_role == role['id']:
  290. filtered_roles.append({'id': role['id'],
  291. 'name': role['name']})
  292. token_data['roles'] = filtered_roles
  293. return
  294. if CONF.trust.enabled and trust:
  295. # If redelegated_trust_id is set, then we must traverse the
  296. # trust_chain in order to determine who the original trustor is. We
  297. # need to do this because the user ID of the original trustor helps
  298. # us determine scope in the redelegated context.
  299. if trust.get('redelegated_trust_id'):
  300. trust_chain = PROVIDERS.trust_api.get_trust_pedigree(
  301. trust['id']
  302. )
  303. token_user_id = trust_chain[-1]['trustor_user_id']
  304. else:
  305. token_user_id = trust['trustor_user_id']
  306. token_project_id = trust['project_id']
  307. # trusts do not support domains yet
  308. token_domain_id = None
  309. else:
  310. token_user_id = user_id
  311. token_project_id = project_id
  312. token_domain_id = domain_id
  313. if system or token_domain_id or token_project_id:
  314. filtered_roles = []
  315. if CONF.trust.enabled and trust:
  316. # First expand out any roles that were in the trust to include
  317. # any implied roles, whether global or domain specific
  318. refs = [{'role_id': role['id']} for role in trust['roles']]
  319. effective_trust_roles = (
  320. PROVIDERS.assignment_api.add_implied_roles(refs))
  321. # Now get the current role assignments for the trustor,
  322. # including any domain specific roles.
  323. assignments = PROVIDERS.assignment_api.list_role_assignments(
  324. user_id=token_user_id,
  325. system=system,
  326. project_id=token_project_id,
  327. effective=True, strip_domain_roles=False)
  328. current_effective_trustor_roles = (
  329. list(set([x['role_id'] for x in assignments])))
  330. # Go through each of the effective trust roles, making sure the
  331. # trustor still has them, if any have been removed, then we
  332. # will treat the trust as invalid
  333. for trust_role in effective_trust_roles:
  334. match_roles = [x for x in current_effective_trustor_roles
  335. if x == trust_role['role_id']]
  336. if match_roles:
  337. role = PROVIDERS.role_api.get_role(match_roles[0])
  338. if role['domain_id'] is None:
  339. filtered_roles.append(role)
  340. else:
  341. raise exception.Forbidden(
  342. _('Trustee has no delegated roles.'))
  343. elif app_cred_id:
  344. app_cred_api = PROVIDERS.application_credential_api
  345. app_cred_ref = app_cred_api.get_application_credential(
  346. app_cred_id)
  347. for role in self._get_app_cred_roles(app_cred_ref,
  348. token_user_id,
  349. token_domain_id,
  350. token_project_id):
  351. filtered_roles.append({'id': role['id'],
  352. 'name': role['name']})
  353. else:
  354. for role in self._get_roles_for_user(token_user_id,
  355. system,
  356. token_domain_id,
  357. token_project_id):
  358. filtered_roles.append({'id': role['id'],
  359. 'name': role['name']})
  360. # user has no project or domain roles, therefore access denied
  361. if not filtered_roles:
  362. if token_project_id:
  363. msg = _('User %(user_id)s has no access '
  364. 'to project %(project_id)s') % {
  365. 'user_id': user_id,
  366. 'project_id': token_project_id}
  367. elif token_domain_id:
  368. msg = _('User %(user_id)s has no access '
  369. 'to domain %(domain_id)s') % {
  370. 'user_id': user_id,
  371. 'domain_id': token_domain_id}
  372. elif system:
  373. msg = _('User %(user_id)s has no access '
  374. 'to the system') % {'user_id': user_id}
  375. LOG.debug(msg)
  376. raise exception.Unauthorized(msg)
  377. token_data['roles'] = filtered_roles
  378. def _populate_service_catalog(self, token_data, user_id, system, domain_id,
  379. project_id, trust):
  380. if 'catalog' in token_data:
  381. # no need to repopulate service catalog
  382. return
  383. if CONF.trust.enabled and trust:
  384. user_id = trust['trustor_user_id']
  385. # NOTE(lbragstad): The catalog API requires a project in order to
  386. # generate a service catalog, but that appears to be only if there are
  387. # endpoint -> project relationships. In the event we're dealing with a
  388. # system_scoped token, we should pass None to the catalog API and just
  389. # get a catalog anyway.
  390. if project_id or domain_id or system:
  391. service_catalog = PROVIDERS.catalog_api.get_v3_catalog(
  392. user_id, project_id)
  393. token_data['catalog'] = service_catalog
  394. def _populate_service_providers(self, token_data):
  395. if 'service_providers' in token_data:
  396. return
  397. service_providers = (
  398. PROVIDERS.federation_api.get_enabled_service_providers()
  399. )
  400. if service_providers:
  401. token_data['service_providers'] = service_providers
  402. def _validate_identity_provider(self, token_data):
  403. federated_info = token_data['user'].get('OS-FEDERATION')
  404. if federated_info:
  405. idp_id = federated_info['identity_provider']['id']
  406. PROVIDERS.federation_api.get_idp(idp_id)
  407. def _populate_token_dates(self, token_data, expires=None, issued_at=None):
  408. if not expires:
  409. expires = default_expire_time()
  410. if not isinstance(expires, six.string_types):
  411. expires = utils.isotime(expires, subsecond=True)
  412. token_data['expires_at'] = expires
  413. token_data['issued_at'] = (issued_at or
  414. utils.isotime(subsecond=True))
  415. def _populate_audit_info(self, token_data, audit_info=None):
  416. if audit_info is None or isinstance(audit_info, six.string_types):
  417. token_data['audit_ids'] = build_audit_info(audit_info)
  418. elif isinstance(audit_info, list):
  419. token_data['audit_ids'] = audit_info
  420. else:
  421. msg = (_('Invalid audit info data type: %(data)s (%(type)s)') %
  422. {'data': audit_info, 'type': type(audit_info)})
  423. LOG.error(msg)
  424. raise exception.UnexpectedError(msg)
  425. def _populate_app_cred(self, token_data, app_cred_id):
  426. if app_cred_id:
  427. app_cred_api = PROVIDERS.application_credential_api
  428. app_cred = app_cred_api.get_application_credential(app_cred_id)
  429. restricted = not app_cred['unrestricted']
  430. token_data['application_credential'] = {}
  431. token_data['application_credential']['id'] = app_cred['id']
  432. token_data['application_credential']['name'] = app_cred['name']
  433. token_data['application_credential']['restricted'] = restricted
  434. def get_token_data(self, user_id, method_names, system=None,
  435. domain_id=None, project_id=None, expires=None,
  436. app_cred_id=None, trust=None, token=None,
  437. include_catalog=True, bind=None, access_token=None,
  438. issued_at=None, audit_info=None):
  439. token_data = {'methods': method_names}
  440. # We've probably already written these to the token
  441. if token:
  442. for x in ('roles', 'user', 'catalog', 'project', 'domain'):
  443. if x in token:
  444. token_data[x] = token[x]
  445. if bind:
  446. token_data['bind'] = bind
  447. self._populate_scope(token_data, system, domain_id, project_id)
  448. if token_data.get('project'):
  449. self._populate_is_admin_project(token_data)
  450. self._populate_user(token_data, user_id, trust)
  451. self._populate_roles(token_data, user_id, system, domain_id,
  452. project_id, trust, app_cred_id, access_token)
  453. self._populate_audit_info(token_data, audit_info)
  454. if include_catalog:
  455. self._populate_service_catalog(
  456. token_data, user_id, system, domain_id, project_id, trust
  457. )
  458. self._populate_service_providers(token_data)
  459. self._validate_identity_provider(token_data)
  460. self._populate_token_dates(token_data, expires=expires,
  461. issued_at=issued_at)
  462. self._populate_oauth_section(token_data, access_token)
  463. self._populate_app_cred(token_data, app_cred_id)
  464. return {'token': token_data}
  465. class BaseProvider(provider_api.ProviderAPIMixin, base.Provider):
  466. def __init__(self, *args, **kwargs):
  467. super(BaseProvider, self).__init__(*args, **kwargs)
  468. self.v3_token_data_helper = V3TokenDataHelper()
  469. def get_token_version(self, token_data):
  470. if token_data and isinstance(token_data, dict):
  471. if 'token_version' in token_data:
  472. if token_data['token_version'] in token_model.VERSIONS:
  473. return token_data['token_version']
  474. # FIXME(morganfainberg): deprecate the following logic in future
  475. # revisions. It is better to just specify the token_version in
  476. # the token_data itself. This way we can support future versions
  477. # that might have the same fields.
  478. if 'access' in token_data:
  479. return token_model.V2
  480. if 'token' in token_data and 'methods' in token_data['token']:
  481. return token_model.V3
  482. raise exception.UnsupportedTokenVersionException()
  483. def _is_mapped_token(self, auth_context):
  484. return (federation_constants.IDENTITY_PROVIDER in auth_context and
  485. federation_constants.PROTOCOL in auth_context)
  486. def issue_token(self, user_id, method_names, expires_at=None,
  487. system=None, project_id=None, domain_id=None,
  488. auth_context=None, trust=None, app_cred_id=None,
  489. include_catalog=True, parent_audit_id=None):
  490. if auth_context and auth_context.get('bind'):
  491. # NOTE(lbragstad): Check if the token provider being used actually
  492. # supports bind authentication methods before proceeding.
  493. if not self._supports_bind_authentication:
  494. raise exception.NotImplemented(_(
  495. 'The configured token provider does not support bind '
  496. 'authentication.'))
  497. if CONF.trust.enabled and trust:
  498. if user_id != trust['trustee_user_id']:
  499. raise exception.Forbidden(_('User is not a trustee.'))
  500. token_ref = None
  501. if auth_context and self._is_mapped_token(auth_context):
  502. token_ref = self._handle_mapped_tokens(
  503. auth_context, project_id, domain_id)
  504. access_token = None
  505. if 'oauth1' in method_names:
  506. access_token_id = auth_context['access_token_id']
  507. access_token = PROVIDERS.oauth_api.get_access_token(
  508. access_token_id
  509. )
  510. token_data = self.v3_token_data_helper.get_token_data(
  511. user_id,
  512. method_names,
  513. system=system,
  514. domain_id=domain_id,
  515. project_id=project_id,
  516. expires=expires_at,
  517. trust=trust,
  518. app_cred_id=app_cred_id,
  519. bind=auth_context.get('bind') if auth_context else None,
  520. token=token_ref,
  521. include_catalog=include_catalog,
  522. access_token=access_token,
  523. audit_info=parent_audit_id)
  524. token_id = self._get_token_id(token_data)
  525. return token_id, token_data
  526. def _handle_mapped_tokens(self, auth_context, project_id, domain_id):
  527. user_id = auth_context['user_id']
  528. group_ids = auth_context['group_ids']
  529. idp = auth_context[federation_constants.IDENTITY_PROVIDER]
  530. protocol = auth_context[federation_constants.PROTOCOL]
  531. user_dict = PROVIDERS.identity_api.get_user(user_id)
  532. user_name = user_dict['name']
  533. token_data = {
  534. 'user': {
  535. 'id': user_id,
  536. 'name': parse.unquote(user_name),
  537. federation_constants.FEDERATION: {
  538. 'groups': [{'id': x} for x in group_ids],
  539. 'identity_provider': {'id': idp},
  540. 'protocol': {'id': protocol}
  541. },
  542. 'domain': {
  543. 'id': CONF.federation.federated_domain_name,
  544. 'name': CONF.federation.federated_domain_name
  545. }
  546. }
  547. }
  548. # FIXME(lbragstad): This will have to account for system-scoping, too.
  549. if project_id or domain_id:
  550. self.v3_token_data_helper.populate_roles_for_federated_user(
  551. token_data, group_ids, project_id, domain_id, user_id)
  552. return token_data
  553. def _verify_token_ref(self, token_ref):
  554. """Verify and return the given token_ref."""
  555. if not token_ref:
  556. raise exception.Unauthorized(_('Token is absent'))
  557. return token_ref
  558. def validate_token(self, token_id):
  559. if self.needs_persistence():
  560. token_ref = token_id
  561. token_data = token_ref.get('token_data')
  562. user_id = token_ref['user_id']
  563. methods = token_data['token']['methods']
  564. bind = token_data['token'].get('bind')
  565. issued_at = token_data['token']['issued_at']
  566. expires_at = token_data['token']['expires_at']
  567. audit_ids = token_data['token'].get('audit_ids')
  568. system = token_data['token'].get('system', {}).get('all')
  569. if system:
  570. system = 'all'
  571. domain_id = token_data['token'].get('domain', {}).get('id')
  572. project_id = token_data['token'].get('project', {}).get('id')
  573. access_token = None
  574. if token_data['token'].get('OS-OAUTH1'):
  575. access_token = {
  576. 'id': token_data['token'].get('OS-OAUTH1', {}).get(
  577. 'access_token_id'
  578. ),
  579. 'consumer_id': token_data['token'].get(
  580. 'OS-OAUTH1', {}
  581. ).get('consumer_id')
  582. }
  583. trust_ref = None
  584. trust_id = token_ref.get('trust_id')
  585. if trust_id:
  586. trust_ref = PROVIDERS.trust_api.get_trust(trust_id)
  587. app_cred_id = token_data['token'].get(
  588. 'application_credential', {}).get('id')
  589. token_dict = None
  590. if token_data['token']['user'].get(
  591. federation_constants.FEDERATION):
  592. token_dict = {'user': token_ref['user']}
  593. else:
  594. try:
  595. (user_id, methods, audit_ids, system, domain_id,
  596. project_id, trust_id, federated_info, access_token_id,
  597. app_cred_id, issued_at, expires_at) = (
  598. self.token_formatter.validate_token(token_id))
  599. except exception.ValidationError as e:
  600. raise exception.TokenNotFound(e)
  601. bind = None
  602. token_dict = None
  603. trust_ref = None
  604. if federated_info:
  605. # NOTE(lbragstad): We need to rebuild information about the
  606. # federated token as well as the federated token roles. This is
  607. # because when we validate a non-persistent token, we don't
  608. # have a token reference to pull the federated token
  609. # information out of. As a result, we have to extract it from
  610. # the token itself and rebuild the federated context. These
  611. # private methods currently live in the
  612. # keystone.token.providers.fernet.Provider() class.
  613. token_dict = self._rebuild_federated_info(
  614. federated_info, user_id
  615. )
  616. if project_id or domain_id:
  617. self._rebuild_federated_token_roles(
  618. token_dict,
  619. federated_info,
  620. user_id,
  621. project_id,
  622. domain_id
  623. )
  624. if trust_id:
  625. trust_ref = PROVIDERS.trust_api.get_trust(trust_id)
  626. access_token = None
  627. if access_token_id:
  628. access_token = PROVIDERS.oauth_api.get_access_token(
  629. access_token_id
  630. )
  631. return self.v3_token_data_helper.get_token_data(
  632. user_id,
  633. method_names=methods,
  634. system=system,
  635. domain_id=domain_id,
  636. project_id=project_id,
  637. issued_at=issued_at,
  638. expires=expires_at,
  639. trust=trust_ref,
  640. token=token_dict,
  641. bind=bind,
  642. access_token=access_token,
  643. audit_info=audit_ids,
  644. app_cred_id=app_cred_id)