Merge "Filter SAML2 assertion parameters with certain prefix."
This commit is contained in:
@@ -724,6 +724,10 @@
|
|||||||
# Keystone Federation backend driver. (string value)
|
# Keystone Federation backend driver. (string value)
|
||||||
#driver=keystone.contrib.federation.backends.sql.Federation
|
#driver=keystone.contrib.federation.backends.sql.Federation
|
||||||
|
|
||||||
|
# Value to be used when filtering assertion parameters from
|
||||||
|
# the environment. (string value)
|
||||||
|
#assertion_prefix=
|
||||||
|
|
||||||
|
|
||||||
[identity]
|
[identity]
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class Saml2(auth.AuthMethodHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _handle_unscoped_token(self, context, auth_payload):
|
def _handle_unscoped_token(self, context, auth_payload):
|
||||||
assertion = context['environment']
|
assertion = dict(self._get_assertion_params_from_env(context))
|
||||||
|
|
||||||
identity_provider = auth_payload['identity_provider']
|
identity_provider = auth_payload['identity_provider']
|
||||||
protocol = auth_payload['protocol']
|
protocol = auth_payload['protocol']
|
||||||
@@ -104,3 +104,9 @@ class Saml2(auth.AuthMethodHandler):
|
|||||||
except exception.GroupNotFound:
|
except exception.GroupNotFound:
|
||||||
raise exception.MappedGroupNotFound(
|
raise exception.MappedGroupNotFound(
|
||||||
group_id=group_id, mapping_id=mapping_id)
|
group_id=group_id, mapping_id=mapping_id)
|
||||||
|
|
||||||
|
def _get_assertion_params_from_env(self, context):
|
||||||
|
prefix = CONF.federation.assertion_prefix
|
||||||
|
for k, v in context['environment'].items():
|
||||||
|
if k.startswith(prefix):
|
||||||
|
yield (k, v)
|
||||||
|
|||||||
@@ -359,7 +359,10 @@ FILE_OPTIONS = {
|
|||||||
cfg.StrOpt('driver',
|
cfg.StrOpt('driver',
|
||||||
default='keystone.contrib.federation.'
|
default='keystone.contrib.federation.'
|
||||||
'backends.sql.Federation',
|
'backends.sql.Federation',
|
||||||
help='Keystone Federation backend driver.')],
|
help='Keystone Federation backend driver.'),
|
||||||
|
cfg.StrOpt('assertion_prefix', default='',
|
||||||
|
help='Value to be used when filtering assertion parameters '
|
||||||
|
'from the environment.')],
|
||||||
|
|
||||||
'policy': [
|
'policy': [
|
||||||
cfg.StrOpt('driver',
|
cfg.StrOpt('driver',
|
||||||
|
|||||||
@@ -425,6 +425,14 @@ EMPLOYEE_ASSERTION = {
|
|||||||
'orgPersonType': 'Employee;BuildingX;'
|
'orgPersonType': 'Employee;BuildingX;'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMPLOYEE_ASSERTION_PREFIXED = {
|
||||||
|
'PREFIX_Email': 'tim@example.com',
|
||||||
|
'PREFIX_UserName': 'tbo',
|
||||||
|
'PREFIX_FirstName': 'Tim',
|
||||||
|
'PREFIX_LastName': 'Bo',
|
||||||
|
'PREFIX_orgPersonType': 'SuperEmployee;BuildingX;'
|
||||||
|
}
|
||||||
|
|
||||||
CONTRACTOR_ASSERTION = {
|
CONTRACTOR_ASSERTION = {
|
||||||
'Email': 'jill@example.com',
|
'Email': 'jill@example.com',
|
||||||
'UserName': 'jsmith',
|
'UserName': 'jsmith',
|
||||||
|
|||||||
@@ -748,6 +748,7 @@ class FederatedTokenTests(FederationTests):
|
|||||||
PROTOCOL = 'saml2'
|
PROTOCOL = 'saml2'
|
||||||
AUTH_METHOD = 'saml2'
|
AUTH_METHOD = 'saml2'
|
||||||
USER = 'user@ORGANIZATION'
|
USER = 'user@ORGANIZATION'
|
||||||
|
ASSERTION_PREFIX = 'PREFIX_'
|
||||||
|
|
||||||
UNSCOPED_V3_SAML2_REQ = {
|
UNSCOPED_V3_SAML2_REQ = {
|
||||||
"identity": {
|
"identity": {
|
||||||
@@ -1142,6 +1143,37 @@ class FederatedTokenTests(FederationTests):
|
|||||||
body=scoped_token,
|
body=scoped_token,
|
||||||
expected_status=500)
|
expected_status=500)
|
||||||
|
|
||||||
|
def test_assertion_prefix_parameter(self):
|
||||||
|
"""Test parameters filtering based on the prefix.
|
||||||
|
|
||||||
|
With ``assertion_prefix`` set to fixed, non defailt value,
|
||||||
|
issue an unscoped token from assertion EMPLOYEE_ASSERTION_PREFIXED.
|
||||||
|
Expect server to return unscoped token.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.config_fixture.config(group='federation',
|
||||||
|
assertion_prefix=self.ASSERTION_PREFIX)
|
||||||
|
r = self._issue_unscoped_token(assertion='EMPLOYEE_ASSERTION_PREFIXED')
|
||||||
|
self.assertIsNotNone(r.headers.get('X-Subject-Token'))
|
||||||
|
|
||||||
|
def test_assertion_prefix_parameter_expect_fail(self):
|
||||||
|
"""Test parameters filtering based on the prefix.
|
||||||
|
|
||||||
|
With ``assertion_prefix`` default value set to empty string
|
||||||
|
issue an unscoped token from assertion EMPLOYEE_ASSERTION.
|
||||||
|
Next, configure ``assertion_prefix`` to value ``UserName``.
|
||||||
|
Try issuing unscoped token with EMPLOYEE_ASSERTION.
|
||||||
|
Expect server to raise exception.Unathorized exception.
|
||||||
|
|
||||||
|
"""
|
||||||
|
r = self._issue_unscoped_token()
|
||||||
|
self.assertIsNotNone(r.headers.get('X-Subject-Token'))
|
||||||
|
self.config_fixture.config(group='federation',
|
||||||
|
assertion_prefix='UserName')
|
||||||
|
|
||||||
|
self.assertRaises(exception.Unauthorized,
|
||||||
|
self._issue_unscoped_token)
|
||||||
|
|
||||||
def load_federation_sample_data(self):
|
def load_federation_sample_data(self):
|
||||||
"""Inject additional data."""
|
"""Inject additional data."""
|
||||||
|
|
||||||
@@ -1292,6 +1324,31 @@ class FederatedTokenTests(FederationTests):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'local': [
|
||||||
|
{
|
||||||
|
'group': {
|
||||||
|
'id': self.group_employees['id']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'user': {
|
||||||
|
'name': '{0}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'remote': [
|
||||||
|
{
|
||||||
|
'type': self.ASSERTION_PREFIX + 'UserName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': self.ASSERTION_PREFIX + 'orgPersonType',
|
||||||
|
'any_one_of': [
|
||||||
|
'SuperEmployee'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'local': [
|
'local': [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user