aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/contrib/federation
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/contrib/federation')
-rw-r--r--keystone-moon/keystone/contrib/federation/backends/sql.py2
-rw-r--r--keystone-moon/keystone/contrib/federation/controllers.py9
-rw-r--r--keystone-moon/keystone/contrib/federation/core.py5
-rw-r--r--keystone-moon/keystone/contrib/federation/idp.py13
-rw-r--r--keystone-moon/keystone/contrib/federation/routers.py19
-rw-r--r--keystone-moon/keystone/contrib/federation/utils.py55
6 files changed, 64 insertions, 39 deletions
diff --git a/keystone-moon/keystone/contrib/federation/backends/sql.py b/keystone-moon/keystone/contrib/federation/backends/sql.py
index ed07c08f..dbd17025 100644
--- a/keystone-moon/keystone/contrib/federation/backends/sql.py
+++ b/keystone-moon/keystone/contrib/federation/backends/sql.py
@@ -155,7 +155,7 @@ class ServiceProviderModel(sql.ModelBase, sql.DictBase):
return d
-class Federation(core.Driver):
+class Federation(core.FederationDriverV8):
# Identity Provider CRUD
@sql.handle_conflicts(conflict_type='identity_provider')
diff --git a/keystone-moon/keystone/contrib/federation/controllers.py b/keystone-moon/keystone/contrib/federation/controllers.py
index 912d45d5..d0bd2bce 100644
--- a/keystone-moon/keystone/contrib/federation/controllers.py
+++ b/keystone-moon/keystone/contrib/federation/controllers.py
@@ -316,6 +316,15 @@ class Auth(auth_controllers.Auth):
token_id = res.headers['X-Subject-Token']
return self.render_html_response(host, token_id)
+ def federated_idp_specific_sso_auth(self, context, idp_id, protocol_id):
+ host = self._get_sso_origin_host(context)
+
+ # NOTE(lbragstad): We validate that the Identity Provider actually
+ # exists in the Mapped authentication plugin.
+ res = self.federated_authentication(context, idp_id, protocol_id)
+ token_id = res.headers['X-Subject-Token']
+ return self.render_html_response(host, token_id)
+
def render_html_response(self, host, token_id):
"""Forms an HTML Form from a template with autosubmit."""
diff --git a/keystone-moon/keystone/contrib/federation/core.py b/keystone-moon/keystone/contrib/federation/core.py
index 2ab75ecb..1595be1d 100644
--- a/keystone-moon/keystone/contrib/federation/core.py
+++ b/keystone-moon/keystone/contrib/federation/core.py
@@ -92,7 +92,7 @@ class Manager(manager.Manager):
@six.add_metaclass(abc.ABCMeta)
-class Driver(object):
+class FederationDriverV8(object):
@abc.abstractmethod
def create_idp(self, idp_id, idp):
@@ -350,3 +350,6 @@ class Driver(object):
"""
raise exception.NotImplemented() # pragma: no cover
+
+
+Driver = manager.create_legacy_driver(FederationDriverV8)
diff --git a/keystone-moon/keystone/contrib/federation/idp.py b/keystone-moon/keystone/contrib/federation/idp.py
index 739fc01a..51689989 100644
--- a/keystone-moon/keystone/contrib/federation/idp.py
+++ b/keystone-moon/keystone/contrib/federation/idp.py
@@ -12,7 +12,6 @@
import datetime
import os
-import subprocess
import uuid
from oslo_config import cfg
@@ -32,11 +31,14 @@ xmldsig = importutils.try_import("saml2.xmldsig")
if not xmldsig:
xmldsig = importutils.try_import("xmldsig")
+from keystone.common import environment
from keystone.common import utils
from keystone import exception
from keystone.i18n import _, _LE
+subprocess = environment.subprocess
+
LOG = log.getLogger(__name__)
CONF = cfg.CONF
@@ -426,11 +428,10 @@ def _sign_assertion(assertion):
stdout = subprocess.check_output(command_list,
stderr=subprocess.STDOUT)
except Exception as e:
- msg = _LE('Error when signing assertion, reason: %(reason)s')
- msg = msg % {'reason': e}
- if hasattr(e, 'output'):
- msg += ' output: %(output)s' % {'output': e.output}
- LOG.error(msg)
+ msg = _LE('Error when signing assertion, reason: %(reason)s%(output)s')
+ LOG.error(msg,
+ {'reason': e,
+ 'output': ' ' + e.output if hasattr(e, 'output') else ''})
raise exception.SAMLSigningError(reason=e)
finally:
try:
diff --git a/keystone-moon/keystone/contrib/federation/routers.py b/keystone-moon/keystone/contrib/federation/routers.py
index d8fa8175..ddf2f61f 100644
--- a/keystone-moon/keystone/contrib/federation/routers.py
+++ b/keystone-moon/keystone/contrib/federation/routers.py
@@ -72,6 +72,13 @@ class FederationExtension(wsgi.V3ExtensionRouter):
protocols/{protocol}/auth
POST /OS-FEDERATION/identity_providers/{identity_provider}/
protocols/{protocol}/auth
+ GET /auth/OS-FEDERATION/identity_providers/
+ {idp_id}/protocols/{protocol_id}/websso
+ ?origin=https%3A//horizon.example.com
+ POST /auth/OS-FEDERATION/identity_providers/
+ {idp_id}/protocols/{protocol_id}/websso
+ ?origin=https%3A//horizon.example.com
+
POST /auth/OS-FEDERATION/saml2
POST /auth/OS-FEDERATION/saml2/ecp
@@ -185,11 +192,13 @@ class FederationExtension(wsgi.V3ExtensionRouter):
self._add_resource(
mapper, domain_controller,
path=self._construct_url('domains'),
+ new_path='/auth/domains',
get_action='list_domains_for_groups',
rel=build_resource_relation(resource_name='domains'))
self._add_resource(
mapper, project_controller,
path=self._construct_url('projects'),
+ new_path='/auth/projects',
get_action='list_projects_for_groups',
rel=build_resource_relation(resource_name='projects'))
@@ -223,6 +232,16 @@ class FederationExtension(wsgi.V3ExtensionRouter):
path_vars={
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
+ self._add_resource(
+ mapper, auth_controller,
+ path='/auth' + self._construct_url(
+ 'identity_providers/{idp_id}/protocols/{protocol_id}/websso'),
+ get_post_action='federated_idp_specific_sso_auth',
+ rel=build_resource_relation(resource_name='identity_providers'),
+ path_vars={
+ 'idp_id': IDP_ID_PARAMETER_RELATION,
+ 'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
+ })
# Keystone-Identity-Provider metadata endpoint
self._add_resource(
diff --git a/keystone-moon/keystone/contrib/federation/utils.py b/keystone-moon/keystone/contrib/federation/utils.py
index b0db3cdd..bde19cfd 100644
--- a/keystone-moon/keystone/contrib/federation/utils.py
+++ b/keystone-moon/keystone/contrib/federation/utils.py
@@ -672,15 +672,18 @@ class RuleProcessor(object):
for requirement in requirements:
requirement_type = requirement['type']
+ direct_map_values = assertion.get(requirement_type)
regex = requirement.get('regex', False)
+ if not direct_map_values:
+ return None
+
any_one_values = requirement.get(self._EvalType.ANY_ONE_OF)
if any_one_values is not None:
if self._evaluate_requirement(any_one_values,
- requirement_type,
+ direct_map_values,
self._EvalType.ANY_ONE_OF,
- regex,
- assertion):
+ regex):
continue
else:
return None
@@ -688,10 +691,9 @@ class RuleProcessor(object):
not_any_values = requirement.get(self._EvalType.NOT_ANY_OF)
if not_any_values is not None:
if self._evaluate_requirement(not_any_values,
- requirement_type,
+ direct_map_values,
self._EvalType.NOT_ANY_OF,
- regex,
- assertion):
+ regex):
continue
else:
return None
@@ -699,23 +701,21 @@ class RuleProcessor(object):
# If 'any_one_of' or 'not_any_of' are not found, then values are
# within 'type'. Attempt to find that 'type' within the assertion,
# and filter these values if 'whitelist' or 'blacklist' is set.
- direct_map_values = assertion.get(requirement_type)
- if direct_map_values:
- blacklisted_values = requirement.get(self._EvalType.BLACKLIST)
- whitelisted_values = requirement.get(self._EvalType.WHITELIST)
+ blacklisted_values = requirement.get(self._EvalType.BLACKLIST)
+ whitelisted_values = requirement.get(self._EvalType.WHITELIST)
- # If a blacklist or whitelist is used, we want to map to the
- # whole list instead of just its values separately.
- if blacklisted_values is not None:
- direct_map_values = [v for v in direct_map_values
- if v not in blacklisted_values]
- elif whitelisted_values is not None:
- direct_map_values = [v for v in direct_map_values
- if v in whitelisted_values]
+ # If a blacklist or whitelist is used, we want to map to the
+ # whole list instead of just its values separately.
+ if blacklisted_values is not None:
+ direct_map_values = [v for v in direct_map_values
+ if v not in blacklisted_values]
+ elif whitelisted_values is not None:
+ direct_map_values = [v for v in direct_map_values
+ if v in whitelisted_values]
- direct_maps.add(direct_map_values)
+ direct_maps.add(direct_map_values)
- LOG.debug('updating a direct mapping: %s', direct_map_values)
+ LOG.debug('updating a direct mapping: %s', direct_map_values)
return direct_maps
@@ -726,8 +726,8 @@ class RuleProcessor(object):
return True
return False
- def _evaluate_requirement(self, values, requirement_type,
- eval_type, regex, assertion):
+ def _evaluate_requirement(self, values, assertion_values,
+ eval_type, regex):
"""Evaluate the incoming requirement and assertion.
If the requirement type does not exist in the assertion data, then
@@ -737,23 +737,16 @@ class RuleProcessor(object):
:param values: list of allowed values, defined in the requirement
:type values: list
- :param requirement_type: key to look for in the assertion
- :type requirement_type: string
+ :param assertion_values: The values from the assertion to evaluate
+ :type assertion_values: list/string
:param eval_type: determine how to evaluate requirements
:type eval_type: string
:param regex: perform evaluation with regex
:type regex: boolean
- :param assertion: dict of attributes from the IdP
- :type assertion: dict
:returns: boolean, whether requirement is valid or not.
"""
-
- assertion_values = assertion.get(requirement_type)
- if not assertion_values:
- return False
-
if regex:
any_match = self._evaluate_values_by_regex(values,
assertion_values)