keystone.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright 2016 Cloudbase Solutions Srl
  2. # All Rights Reserved.
  3. from keystoneauth1 import exceptions as ks_exceptions
  4. from keystoneauth1 import loading
  5. from keystoneauth1 import session as ks_session
  6. from keystoneclient.v3 import client as kc_v3
  7. from oslo_config import cfg
  8. from oslo_log import log as logging
  9. from coriolis import exception
  10. opts = [
  11. cfg.StrOpt('auth_url',
  12. default=None,
  13. help='Default auth URL to be used when not specified in the'
  14. ' migration\'s connection info.'),
  15. cfg.IntOpt('identity_api_version',
  16. min=2, max=3,
  17. default=2,
  18. help='Default Keystone API version.'),
  19. cfg.BoolOpt('allow_untrusted',
  20. default=False,
  21. help='Allow untrusted SSL/TLS certificates.'),
  22. ]
  23. CONF = cfg.CONF
  24. CONF.register_opts(opts, 'keystone')
  25. LOG = logging.getLogger(__name__)
  26. TRUSTEE_CONF_GROUP = 'trustee'
  27. loading.register_auth_conf_options(CONF, TRUSTEE_CONF_GROUP, )
  28. def _get_trusts_auth_plugin(trust_id=None):
  29. return loading.load_auth_from_conf_options(
  30. CONF, TRUSTEE_CONF_GROUP, trust_id=trust_id)
  31. def create_trust(ctxt):
  32. if ctxt.trust_id:
  33. return
  34. LOG.debug("Creating Keystone trust")
  35. trusts_auth_plugin = _get_trusts_auth_plugin()
  36. loader = loading.get_plugin_loader("v3token")
  37. auth = loader.load_from_options(
  38. auth_url=trusts_auth_plugin.auth_url,
  39. token=ctxt.auth_token,
  40. project_name=ctxt.project_name,
  41. project_domain_name=ctxt.project_domain_name)
  42. session = ks_session.Session(
  43. auth=auth, verify=not CONF.keystone.allow_untrusted)
  44. try:
  45. trustee_user_id = trusts_auth_plugin.get_user_id(session)
  46. except ks_exceptions.Unauthorized as ex:
  47. LOG.exception(ex)
  48. raise exception.NotAuthorized("Trustee authentication failed")
  49. trustor_user_id = ctxt.user
  50. trustor_proj_id = ctxt.tenant
  51. roles = ctxt.roles
  52. LOG.debug("Granting Keystone trust. Trustor: %(trustor_user_id)s, trustee:"
  53. " %(trustee_user_id)s, project: %(trustor_proj_id)s, roles:"
  54. " %(roles)s",
  55. {"trustor_user_id": trustor_user_id,
  56. "trustee_user_id": trustee_user_id,
  57. "trustor_proj_id": trustor_proj_id,
  58. "roles": roles})
  59. # Trusts are not supported before Keystone v3
  60. client = kc_v3.Client(session=session)
  61. trust = client.trusts.create(trustor_user=trustor_user_id,
  62. trustee_user=trustee_user_id,
  63. project=trustor_proj_id,
  64. impersonation=True,
  65. role_names=roles)
  66. LOG.debug("Trust id: %s" % trust.id)
  67. ctxt.trust_id = trust.id
  68. def delete_trust(ctxt):
  69. if ctxt.trust_id:
  70. LOG.debug("Deleting trust id: %s", ctxt.trust_id)
  71. auth = _get_trusts_auth_plugin(ctxt.trust_id)
  72. session = ks_session.Session(
  73. auth=auth, verify=not CONF.keystone.allow_untrusted)
  74. client = kc_v3.Client(session=session)
  75. try:
  76. client.trusts.delete(ctxt.trust_id)
  77. except ks_exceptions.NotFound:
  78. LOG.debug("Trust id not found: %s", ctxt.trust_id)
  79. ctxt.trust_id = None
  80. def create_keystone_session(ctxt, connection_info={}):
  81. allow_untrusted = connection_info.get(
  82. "allow_untrusted", CONF.keystone.allow_untrusted)
  83. # TODO(alexpilotti): add "ca_cert" to connection_info
  84. verify = not allow_untrusted
  85. username = connection_info.get("username")
  86. auth = None
  87. if not username:
  88. # Using directly the caller's token is not feasible for long running
  89. # tasks as once it expires it cannot be automatically renewed. This is
  90. # solved by using a Keystone trust, which must have been set priorly.
  91. if ctxt.trust_id:
  92. auth = _get_trusts_auth_plugin(ctxt.trust_id)
  93. else:
  94. plugin_name = "token"
  95. plugin_args = {
  96. "token": ctxt.auth_token
  97. }
  98. else:
  99. plugin_name = "password"
  100. password = connection_info.get("password")
  101. plugin_args = {
  102. "username": username,
  103. "password": password,
  104. }
  105. if not auth:
  106. project_name = connection_info.get("project_name", ctxt.project_name)
  107. auth_url = connection_info.get("auth_url", CONF.keystone.auth_url)
  108. if not auth_url:
  109. raise exception.CoriolisException(
  110. '"auth_url" not provided in "connection_info" and option '
  111. '"auth_url" in group "[openstack_migration_provider]" '
  112. 'not set')
  113. plugin_args.update({
  114. "auth_url": auth_url,
  115. "project_name": project_name,
  116. })
  117. keystone_version = connection_info.get(
  118. "identity_api_version", CONF.keystone.identity_api_version)
  119. if keystone_version == 3:
  120. plugin_name = "v3" + plugin_name
  121. project_domain_name = connection_info.get(
  122. "project_domain_name", ctxt.project_domain_name)
  123. # NOTE: only set the kwarg if proper argument is provided:
  124. if project_domain_name:
  125. plugin_args["project_domain_name"] = project_domain_name
  126. project_domain_id = connection_info.get(
  127. "project_domain_id", ctxt.project_domain_id)
  128. if project_domain_id:
  129. plugin_args["project_domain_id"] = project_domain_id
  130. if not project_domain_name and not project_domain_id:
  131. raise exception.CoriolisException(
  132. "Either 'project_domain_name' or 'project_domain_id' is "
  133. "required for Keystone v3 Auth.")
  134. # NOTE: The v3token plugin does not allow the user_domain_name
  135. # or user_domain_id options, while the v3password plugin
  136. # requires at least any of these.
  137. if plugin_name != "v3token":
  138. user_domain_name = connection_info.get(
  139. "user_domain_name", ctxt.user_domain_name)
  140. if user_domain_name:
  141. plugin_args["user_domain_name"] = user_domain_name
  142. user_domain_id = connection_info.get(
  143. "user_domain_id", ctxt.user_domain_id)
  144. if user_domain_id:
  145. plugin_args["user_domain_id"] = user_domain_id
  146. if not user_domain_name and not user_domain_id:
  147. raise exception.CoriolisException(
  148. "Either 'user_domain_name' or 'user_domain_id' is "
  149. "required for Keystone v3 Auth.")
  150. loader = loading.get_plugin_loader(plugin_name)
  151. auth = loader.load_from_options(**plugin_args)
  152. return ks_session.Session(auth=auth, verify=verify)