Add relay_state_prefix to Service Provider
To correctly create an ECP assertion, a keystone acting as an identity provider needs to know the relay state's prefix used by the service provider. This is usually 'ss:mem', but is configurable with mod_shib. Co-Authored-By: Rodrigo Duarte Sousa <rodrigods@lsd.ufcg.edu.br> Change-Id: I55fe059df8b73e8c6d1f195a958e73ee0a321015 Partial-Bug: 1426128 bp: ecp-wrapped-saml-assertions
This commit is contained in:
parent
8c42166c12
commit
659529a4ad
|
@ -978,6 +978,11 @@ FILE_OPTIONS = {
|
|||
help='Path to the Identity Provider Metadata file. '
|
||||
'This file should be generated with the '
|
||||
'keystone-manage saml_idp_metadata command.'),
|
||||
cfg.StrOpt('relay_state_prefix',
|
||||
default='ss:mem:',
|
||||
help='The prefix to use for the RelayState SAML '
|
||||
'attribute, used when generating ECP wrapped '
|
||||
'assertions.'),
|
||||
],
|
||||
'eventlet_server': [
|
||||
cfg.IntOpt('public_workers',
|
||||
|
|
|
@ -126,15 +126,17 @@ class MappingModel(sql.ModelBase, sql.DictBase):
|
|||
|
||||
class ServiceProviderModel(sql.ModelBase, sql.DictBase):
|
||||
__tablename__ = 'service_provider'
|
||||
attributes = ['auth_url', 'id', 'enabled', 'description', 'sp_url']
|
||||
attributes = ['auth_url', 'id', 'enabled', 'description',
|
||||
'relay_state_prefix', 'sp_url']
|
||||
mutable_attributes = frozenset(['auth_url', 'description', 'enabled',
|
||||
'sp_url'])
|
||||
'relay_state_prefix', 'sp_url'])
|
||||
|
||||
id = sql.Column(sql.String(64), primary_key=True)
|
||||
enabled = sql.Column(sql.Boolean, nullable=False)
|
||||
description = sql.Column(sql.Text(), nullable=True)
|
||||
auth_url = sql.Column(sql.String(256), nullable=False)
|
||||
sp_url = sql.Column(sql.String(256), nullable=False)
|
||||
relay_state_prefix = sql.Column(sql.String(256), nullable=False)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dictionary):
|
||||
|
|
|
@ -406,15 +406,17 @@ class ServiceProvider(_ControllerBase):
|
|||
member_name = 'service_provider'
|
||||
|
||||
_mutable_parameters = frozenset(['auth_url', 'description', 'enabled',
|
||||
'sp_url'])
|
||||
'relay_state_prefix', 'sp_url'])
|
||||
_public_parameters = frozenset(['auth_url', 'id', 'enabled', 'description',
|
||||
'links', 'sp_url'])
|
||||
'links', 'relay_state_prefix', 'sp_url'])
|
||||
|
||||
@controller.protected()
|
||||
@validation.validated(schema.service_provider_create, 'service_provider')
|
||||
def create_service_provider(self, context, sp_id, service_provider):
|
||||
service_provider = self._normalize_dict(service_provider)
|
||||
service_provider.setdefault('enabled', False)
|
||||
service_provider.setdefault('relay_state_prefix',
|
||||
CONF.saml.relay_state_prefix)
|
||||
ServiceProvider.check_immutable_params(service_provider)
|
||||
sp_ref = self.federation_api.create_sp(sp_id, service_provider)
|
||||
response = ServiceProvider.wrap_member(context, sp_ref)
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db.sqlalchemy import utils
|
||||
import sqlalchemy as sql
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
_SP_TABLE_NAME = 'service_provider'
|
||||
_RELAY_STATE_PREFIX = 'relay_state_prefix'
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
idp_table = utils.get_table(migrate_engine, _SP_TABLE_NAME)
|
||||
relay_state_prefix_default = CONF.saml.relay_state_prefix
|
||||
relay_state_prefix = sql.Column(_RELAY_STATE_PREFIX, sql.String(256),
|
||||
nullable=False,
|
||||
server_default=relay_state_prefix_default)
|
||||
idp_table.create_column(relay_state_prefix)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
idp_table = utils.get_table(migrate_engine, _SP_TABLE_NAME)
|
||||
idp_table.drop_column(_RELAY_STATE_PREFIX)
|
|
@ -58,7 +58,8 @@ _service_provider_properties = {
|
|||
'auth_url': parameter_types.url,
|
||||
'sp_url': parameter_types.url,
|
||||
'description': validation.nullable(parameter_types.description),
|
||||
'enabled': parameter_types.boolean
|
||||
'enabled': parameter_types.boolean,
|
||||
'relay_state_prefix': validation.nullable(parameter_types.description)
|
||||
}
|
||||
|
||||
service_provider_create = {
|
||||
|
|
|
@ -46,5 +46,6 @@ class SqlFederation(test_backend_sql.SqlModels):
|
|||
('id', sql.String, 64),
|
||||
('enabled', sql.Boolean, None),
|
||||
('description', sql.Text, None),
|
||||
('relay_state_prefix', sql.String, 256),
|
||||
('sp_url', sql.String, 256))
|
||||
self.assertExpectedSchema('service_provider', cols)
|
||||
|
|
|
@ -310,6 +310,12 @@ class FederationExtension(test_sql_upgrade.SqlMigrateBase):
|
|||
self.assertEqual('', sp.auth_url)
|
||||
self.assertEqual('', sp.sp_url)
|
||||
|
||||
def test_add_relay_state_column(self):
|
||||
self.upgrade(8, repository=self.repo_path)
|
||||
self.assertTableColumns(self.service_provider,
|
||||
['id', 'description', 'enabled', 'auth_url',
|
||||
'relay_state_prefix', 'sp_url'])
|
||||
|
||||
|
||||
class RevokeExtension(test_sql_upgrade.SqlMigrateBase):
|
||||
|
||||
|
|
|
@ -2947,6 +2947,7 @@ class SAMLGenerationTests(FederationTests):
|
|||
'enabled': True,
|
||||
'description': uuid.uuid4().hex,
|
||||
'sp_url': self.RECIPIENT,
|
||||
'relay_state_prefix': CONF.saml.relay_state_prefix,
|
||||
|
||||
}
|
||||
return ref
|
||||
|
@ -3388,7 +3389,8 @@ class ServiceProviderTests(FederationTests):
|
|||
MEMBER_NAME = 'service_provider'
|
||||
COLLECTION_NAME = 'service_providers'
|
||||
SERVICE_PROVIDER_ID = 'ACME'
|
||||
SP_KEYS = ['auth_url', 'id', 'enabled', 'description', 'sp_url']
|
||||
SP_KEYS = ['auth_url', 'id', 'enabled', 'description',
|
||||
'relay_state_prefix', 'sp_url']
|
||||
|
||||
def setUp(self):
|
||||
super(FederationTests, self).setUp()
|
||||
|
@ -3405,6 +3407,7 @@ class ServiceProviderTests(FederationTests):
|
|||
'enabled': True,
|
||||
'description': uuid.uuid4().hex,
|
||||
'sp_url': 'https://' + uuid.uuid4().hex + '.com',
|
||||
'relay_state_prefix': CONF.saml.relay_state_prefix
|
||||
}
|
||||
return ref
|
||||
|
||||
|
@ -3431,6 +3434,29 @@ class ServiceProviderTests(FederationTests):
|
|||
self.assertValidEntity(resp.result['service_provider'],
|
||||
keys_to_check=self.SP_KEYS)
|
||||
|
||||
def test_create_sp_relay_state_default(self):
|
||||
"""Create an SP without relay state, should default to `ss:mem`."""
|
||||
url = self.base_url(suffix=uuid.uuid4().hex)
|
||||
sp = self.sp_ref()
|
||||
del sp['relay_state_prefix']
|
||||
resp = self.put(url, body={'service_provider': sp},
|
||||
expected_status=201)
|
||||
sp_result = resp.result['service_provider']
|
||||
self.assertEqual(CONF.saml.relay_state_prefix,
|
||||
sp_result['relay_state_prefix'])
|
||||
|
||||
def test_create_sp_relay_state_non_default(self):
|
||||
"""Create an SP with custom relay state."""
|
||||
url = self.base_url(suffix=uuid.uuid4().hex)
|
||||
sp = self.sp_ref()
|
||||
non_default_prefix = uuid.uuid4().hex
|
||||
sp['relay_state_prefix'] = non_default_prefix
|
||||
resp = self.put(url, body={'service_provider': sp},
|
||||
expected_status=201)
|
||||
sp_result = resp.result['service_provider']
|
||||
self.assertEqual(non_default_prefix,
|
||||
sp_result['relay_state_prefix'])
|
||||
|
||||
def test_create_service_provider_fail(self):
|
||||
"""Try adding SP object with unallowed attribute."""
|
||||
url = self.base_url(suffix=uuid.uuid4().hex)
|
||||
|
@ -3520,6 +3546,18 @@ class ServiceProviderTests(FederationTests):
|
|||
self.patch(url, body={'service_provider': new_sp_ref},
|
||||
expected_status=404)
|
||||
|
||||
def test_update_sp_relay_state(self):
|
||||
"""Update an SP with custome relay state."""
|
||||
new_sp_ref = self.sp_ref()
|
||||
non_default_prefix = uuid.uuid4().hex
|
||||
new_sp_ref['relay_state_prefix'] = non_default_prefix
|
||||
url = self.base_url(suffix=self.SERVICE_PROVIDER_ID)
|
||||
resp = self.patch(url, body={'service_provider': new_sp_ref},
|
||||
expected_status=200)
|
||||
sp_result = resp.result['service_provider']
|
||||
self.assertEqual(non_default_prefix,
|
||||
sp_result['relay_state_prefix'])
|
||||
|
||||
def test_delete_service_provider(self):
|
||||
url = self.base_url(suffix=self.SERVICE_PROVIDER_ID)
|
||||
self.delete(url, expected_status=204)
|
||||
|
@ -3641,6 +3679,7 @@ class K2KServiceCatalogTests(FederationTests):
|
|||
def sp_response(self, id, ref):
|
||||
ref.pop('enabled')
|
||||
ref.pop('description')
|
||||
ref.pop('relay_state_prefix')
|
||||
ref['id'] = id
|
||||
return ref
|
||||
|
||||
|
@ -3650,6 +3689,7 @@ class K2KServiceCatalogTests(FederationTests):
|
|||
'enabled': True,
|
||||
'description': uuid.uuid4().hex,
|
||||
'sp_url': uuid.uuid4().hex,
|
||||
'relay_state_prefix': CONF.saml.relay_state_prefix,
|
||||
}
|
||||
return ref
|
||||
|
||||
|
|
Loading…
Reference in New Issue