From 920a49cfa055733d575282973e23558c33087a4a Mon Sep 17 00:00:00 2001 From: RHE Date: Fri, 24 Nov 2017 13:54:26 +0100 Subject: remove keystone-moon Change-Id: I80d7c9b669f19d5f6607e162de8e0e55c2f80fdd Signed-off-by: RHE --- keystone-moon/keystone/federation/controllers.py | 519 ----------------------- 1 file changed, 519 deletions(-) delete mode 100644 keystone-moon/keystone/federation/controllers.py (limited to 'keystone-moon/keystone/federation/controllers.py') diff --git a/keystone-moon/keystone/federation/controllers.py b/keystone-moon/keystone/federation/controllers.py deleted file mode 100644 index b9e2d883..00000000 --- a/keystone-moon/keystone/federation/controllers.py +++ /dev/null @@ -1,519 +0,0 @@ -# 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. - -"""Workflow logic for the Federation service.""" - -import string - -from oslo_config import cfg -from oslo_log import log -import six -from six.moves import urllib -import webob - -from keystone.auth import controllers as auth_controllers -from keystone.common import authorization -from keystone.common import controller -from keystone.common import dependency -from keystone.common import utils as k_utils -from keystone.common import validation -from keystone.common import wsgi -from keystone import exception -from keystone.federation import idp as keystone_idp -from keystone.federation import schema -from keystone.federation import utils -from keystone.i18n import _ -from keystone.models import token_model - - -CONF = cfg.CONF -LOG = log.getLogger(__name__) - - -class _ControllerBase(controller.V3Controller): - """Base behaviors for federation controllers.""" - - @classmethod - def base_url(cls, context, path=None): - """Construct a path and pass it to V3Controller.base_url method.""" - path = '/OS-FEDERATION/' + cls.collection_name - return super(_ControllerBase, cls).base_url(context, path=path) - - -@dependency.requires('federation_api') -class IdentityProvider(_ControllerBase): - """Identity Provider representation.""" - - collection_name = 'identity_providers' - member_name = 'identity_provider' - - _public_parameters = frozenset(['id', 'enabled', 'description', - 'remote_ids', 'links' - ]) - - @classmethod - def _add_related_links(cls, context, ref): - """Add URLs for entities related with Identity Provider. - - Add URLs pointing to: - - protocols tied to the Identity Provider - - """ - ref.setdefault('links', {}) - base_path = ref['links'].get('self') - if base_path is None: - base_path = '/'.join([IdentityProvider.base_url(context), - ref['id']]) - for name in ['protocols']: - ref['links'][name] = '/'.join([base_path, name]) - - @classmethod - def _add_self_referential_link(cls, context, ref): - id = ref['id'] - self_path = '/'.join([cls.base_url(context), id]) - ref.setdefault('links', {}) - ref['links']['self'] = self_path - - @classmethod - def wrap_member(cls, context, ref): - cls._add_self_referential_link(context, ref) - cls._add_related_links(context, ref) - ref = cls.filter_params(ref) - return {cls.member_name: ref} - - @controller.protected() - @validation.validated(schema.identity_provider_create, 'identity_provider') - def create_identity_provider(self, context, idp_id, identity_provider): - identity_provider = self._normalize_dict(identity_provider) - identity_provider.setdefault('enabled', False) - idp_ref = self.federation_api.create_idp(idp_id, identity_provider) - response = IdentityProvider.wrap_member(context, idp_ref) - return wsgi.render_response(body=response, status=('201', 'Created')) - - @controller.filterprotected('id', 'enabled') - def list_identity_providers(self, context, filters): - hints = self.build_driver_hints(context, filters) - ref = self.federation_api.list_idps(hints=hints) - ref = [self.filter_params(x) for x in ref] - return IdentityProvider.wrap_collection(context, ref, hints=hints) - - @controller.protected() - def get_identity_provider(self, context, idp_id): - ref = self.federation_api.get_idp(idp_id) - return IdentityProvider.wrap_member(context, ref) - - @controller.protected() - def delete_identity_provider(self, context, idp_id): - self.federation_api.delete_idp(idp_id) - - @controller.protected() - @validation.validated(schema.identity_provider_update, 'identity_provider') - def update_identity_provider(self, context, idp_id, identity_provider): - identity_provider = self._normalize_dict(identity_provider) - idp_ref = self.federation_api.update_idp(idp_id, identity_provider) - return IdentityProvider.wrap_member(context, idp_ref) - - -@dependency.requires('federation_api') -class FederationProtocol(_ControllerBase): - """A federation protocol representation. - - See keystone.common.controller.V3Controller docstring for explanation - on _public_parameters class attributes. - - """ - - collection_name = 'protocols' - member_name = 'protocol' - - _public_parameters = frozenset(['id', 'mapping_id', 'links']) - - @classmethod - def _add_self_referential_link(cls, context, ref): - """Add 'links' entry to the response dictionary. - - Calls IdentityProvider.base_url() class method, as it constructs - proper URL along with the 'identity providers' part included. - - :param ref: response dictionary - - """ - ref.setdefault('links', {}) - base_path = ref['links'].get('identity_provider') - if base_path is None: - base_path = [IdentityProvider.base_url(context), ref['idp_id']] - base_path = '/'.join(base_path) - self_path = [base_path, 'protocols', ref['id']] - self_path = '/'.join(self_path) - ref['links']['self'] = self_path - - @classmethod - def _add_related_links(cls, context, ref): - """Add new entries to the 'links' subdictionary in the response. - - Adds 'identity_provider' key with URL pointing to related identity - provider as a value. - - :param ref: response dictionary - - """ - ref.setdefault('links', {}) - base_path = '/'.join([IdentityProvider.base_url(context), - ref['idp_id']]) - ref['links']['identity_provider'] = base_path - - @classmethod - def wrap_member(cls, context, ref): - cls._add_related_links(context, ref) - cls._add_self_referential_link(context, ref) - ref = cls.filter_params(ref) - return {cls.member_name: ref} - - @controller.protected() - @validation.validated(schema.federation_protocol_schema, 'protocol') - def create_protocol(self, context, idp_id, protocol_id, protocol): - ref = self._normalize_dict(protocol) - ref = self.federation_api.create_protocol(idp_id, protocol_id, ref) - response = FederationProtocol.wrap_member(context, ref) - return wsgi.render_response(body=response, status=('201', 'Created')) - - @controller.protected() - @validation.validated(schema.federation_protocol_schema, 'protocol') - def update_protocol(self, context, idp_id, protocol_id, protocol): - ref = self._normalize_dict(protocol) - ref = self.federation_api.update_protocol(idp_id, protocol_id, - protocol) - return FederationProtocol.wrap_member(context, ref) - - @controller.protected() - def get_protocol(self, context, idp_id, protocol_id): - ref = self.federation_api.get_protocol(idp_id, protocol_id) - return FederationProtocol.wrap_member(context, ref) - - @controller.protected() - def list_protocols(self, context, idp_id): - protocols_ref = self.federation_api.list_protocols(idp_id) - protocols = list(protocols_ref) - return FederationProtocol.wrap_collection(context, protocols) - - @controller.protected() - def delete_protocol(self, context, idp_id, protocol_id): - self.federation_api.delete_protocol(idp_id, protocol_id) - - -@dependency.requires('federation_api') -class MappingController(_ControllerBase): - collection_name = 'mappings' - member_name = 'mapping' - - @controller.protected() - def create_mapping(self, context, mapping_id, mapping): - ref = self._normalize_dict(mapping) - utils.validate_mapping_structure(ref) - mapping_ref = self.federation_api.create_mapping(mapping_id, ref) - response = MappingController.wrap_member(context, mapping_ref) - return wsgi.render_response(body=response, status=('201', 'Created')) - - @controller.protected() - def list_mappings(self, context): - ref = self.federation_api.list_mappings() - return MappingController.wrap_collection(context, ref) - - @controller.protected() - def get_mapping(self, context, mapping_id): - ref = self.federation_api.get_mapping(mapping_id) - return MappingController.wrap_member(context, ref) - - @controller.protected() - def delete_mapping(self, context, mapping_id): - self.federation_api.delete_mapping(mapping_id) - - @controller.protected() - def update_mapping(self, context, mapping_id, mapping): - mapping = self._normalize_dict(mapping) - utils.validate_mapping_structure(mapping) - mapping_ref = self.federation_api.update_mapping(mapping_id, mapping) - return MappingController.wrap_member(context, mapping_ref) - - -@dependency.requires('federation_api') -class Auth(auth_controllers.Auth): - - def _get_sso_origin_host(self, context): - """Validate and return originating dashboard URL. - - Make sure the parameter is specified in the request's URL as well its - value belongs to a list of trusted dashboards. - - :param context: request's context - :raises keystone.exception.ValidationError: ``origin`` query parameter - was not specified. The URL is deemed invalid. - :raises keystone.exception.Unauthorized: URL specified in origin query - parameter does not exist in list of websso trusted dashboards. - :returns: URL with the originating dashboard - - """ - if 'origin' in context['query_string']: - origin = context['query_string']['origin'] - host = urllib.parse.unquote_plus(origin) - else: - msg = _('Request must have an origin query parameter') - LOG.error(msg) - raise exception.ValidationError(msg) - - # change trusted_dashboard hostnames to lowercase before comparison - trusted_dashboards = [k_utils.lower_case_hostname(trusted) - for trusted in CONF.federation.trusted_dashboard] - - if host not in trusted_dashboards: - msg = _('%(host)s is not a trusted dashboard host') - msg = msg % {'host': host} - LOG.error(msg) - raise exception.Unauthorized(msg) - - return host - - def federated_authentication(self, context, idp_id, protocol_id): - """Authenticate from dedicated url endpoint. - - Build HTTP request body for federated authentication and inject - it into the ``authenticate_for_token`` function. - - """ - auth = { - 'identity': { - 'methods': [protocol_id], - protocol_id: { - 'identity_provider': idp_id, - 'protocol': protocol_id - } - } - } - - return self.authenticate_for_token(context, auth=auth) - - def federated_sso_auth(self, context, protocol_id): - try: - remote_id_name = utils.get_remote_id_parameter(protocol_id) - remote_id = context['environment'][remote_id_name] - except KeyError: - msg = _('Missing entity ID from environment') - LOG.error(msg) - raise exception.Unauthorized(msg) - - host = self._get_sso_origin_host(context) - - ref = self.federation_api.get_idp_from_remote_id(remote_id) - # NOTE(stevemar): the returned object is a simple dict that - # contains the idp_id and remote_id. - identity_provider = ref['idp_id'] - res = self.federated_authentication(context, identity_provider, - protocol_id) - 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.""" - headers = [('Content-Type', 'text/html')] - - with open(CONF.federation.sso_callback_template) as template: - src = string.Template(template.read()) - - subs = {'host': host, 'token': token_id} - body = src.substitute(subs) - return webob.Response(body=body, status='200', - headerlist=headers) - - def _create_base_saml_assertion(self, context, auth): - issuer = CONF.saml.idp_entity_id - sp_id = auth['scope']['service_provider']['id'] - service_provider = self.federation_api.get_sp(sp_id) - utils.assert_enabled_service_provider_object(service_provider) - sp_url = service_provider['sp_url'] - - token_id = auth['identity']['token']['id'] - token_data = self.token_provider_api.validate_token(token_id) - token_ref = token_model.KeystoneToken(token_id, token_data) - - if not token_ref.project_scoped: - action = _('Use a project scoped token when attempting to create ' - 'a SAML assertion') - raise exception.ForbiddenAction(action=action) - - subject = token_ref.user_name - roles = token_ref.role_names - project = token_ref.project_name - # NOTE(rodrigods): the domain name is necessary in order to distinguish - # between projects and users with the same name in different domains. - project_domain_name = token_ref.project_domain_name - subject_domain_name = token_ref.user_domain_name - - generator = keystone_idp.SAMLGenerator() - response = generator.samlize_token( - issuer, sp_url, subject, subject_domain_name, - roles, project, project_domain_name) - return (response, service_provider) - - def _build_response_headers(self, service_provider): - return [('Content-Type', 'text/xml'), - ('X-sp-url', six.binary_type(service_provider['sp_url'])), - ('X-auth-url', six.binary_type(service_provider['auth_url']))] - - @validation.validated(schema.saml_create, 'auth') - def create_saml_assertion(self, context, auth): - """Exchange a scoped token for a SAML assertion. - - :param auth: Dictionary that contains a token and service provider ID - :returns: SAML Assertion based on properties from the token - """ - t = self._create_base_saml_assertion(context, auth) - (response, service_provider) = t - - headers = self._build_response_headers(service_provider) - return wsgi.render_response(body=response.to_string(), - status=('200', 'OK'), - headers=headers) - - @validation.validated(schema.saml_create, 'auth') - def create_ecp_assertion(self, context, auth): - """Exchange a scoped token for an ECP assertion. - - :param auth: Dictionary that contains a token and service provider ID - :returns: ECP Assertion based on properties from the token - """ - t = self._create_base_saml_assertion(context, auth) - (saml_assertion, service_provider) = t - relay_state_prefix = service_provider['relay_state_prefix'] - - generator = keystone_idp.ECPGenerator() - ecp_assertion = generator.generate_ecp(saml_assertion, - relay_state_prefix) - - headers = self._build_response_headers(service_provider) - return wsgi.render_response(body=ecp_assertion.to_string(), - status=('200', 'OK'), - headers=headers) - - -@dependency.requires('assignment_api', 'resource_api') -class DomainV3(controller.V3Controller): - collection_name = 'domains' - member_name = 'domain' - - def __init__(self): - super(DomainV3, self).__init__() - self.get_member_from_driver = self.resource_api.get_domain - - @controller.protected() - def list_domains_for_groups(self, context): - """List all domains available to an authenticated user's groups. - - :param context: request context - :returns: list of accessible domains - - """ - auth_context = context['environment'][authorization.AUTH_CONTEXT_ENV] - domains = self.assignment_api.list_domains_for_groups( - auth_context['group_ids']) - return DomainV3.wrap_collection(context, domains) - - -@dependency.requires('assignment_api', 'resource_api') -class ProjectAssignmentV3(controller.V3Controller): - collection_name = 'projects' - member_name = 'project' - - def __init__(self): - super(ProjectAssignmentV3, self).__init__() - self.get_member_from_driver = self.resource_api.get_project - - @controller.protected() - def list_projects_for_groups(self, context): - """List all projects available to an authenticated user's groups. - - :param context: request context - :returns: list of accessible projects - - """ - auth_context = context['environment'][authorization.AUTH_CONTEXT_ENV] - projects = self.assignment_api.list_projects_for_groups( - auth_context['group_ids']) - return ProjectAssignmentV3.wrap_collection(context, projects) - - -@dependency.requires('federation_api') -class ServiceProvider(_ControllerBase): - """Service Provider representation.""" - - collection_name = 'service_providers' - member_name = 'service_provider' - - _public_parameters = frozenset(['auth_url', 'id', 'enabled', 'description', - '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) - sp_ref = self.federation_api.create_sp(sp_id, service_provider) - response = ServiceProvider.wrap_member(context, sp_ref) - return wsgi.render_response(body=response, status=('201', 'Created')) - - @controller.filterprotected('id', 'enabled') - def list_service_providers(self, context, filters): - hints = self.build_driver_hints(context, filters) - ref = self.federation_api.list_sps(hints=hints) - ref = [self.filter_params(x) for x in ref] - return ServiceProvider.wrap_collection(context, ref, hints=hints) - - @controller.protected() - def get_service_provider(self, context, sp_id): - ref = self.federation_api.get_sp(sp_id) - return ServiceProvider.wrap_member(context, ref) - - @controller.protected() - def delete_service_provider(self, context, sp_id): - self.federation_api.delete_sp(sp_id) - - @controller.protected() - @validation.validated(schema.service_provider_update, 'service_provider') - def update_service_provider(self, context, sp_id, service_provider): - service_provider = self._normalize_dict(service_provider) - sp_ref = self.federation_api.update_sp(sp_id, service_provider) - return ServiceProvider.wrap_member(context, sp_ref) - - -class SAMLMetadataV3(_ControllerBase): - member_name = 'metadata' - - def get_metadata(self, context): - metadata_path = CONF.saml.idp_metadata_path - try: - with open(metadata_path, 'r') as metadata_handler: - metadata = metadata_handler.read() - except IOError as e: - # Raise HTTP 500 in case Metadata file cannot be read. - raise exception.MetadataFileError(reason=e) - return wsgi.render_response(body=metadata, status=('200', 'OK'), - headers=[('Content-Type', 'text/xml')]) -- cgit 1.2.3-korg