"Fossies" - the Fresh Open Source Software Archive

Member "keystone-17.0.0/keystone/federation/core.py" (13 May 2020, 7785 Bytes) of package /linux/misc/openstack/keystone-17.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "core.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 16.0.1_vs_17.0.0.

    1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    2 # not use this file except in compliance with the License. You may obtain
    3 # a copy of the License at
    4 #
    5 #      http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 # Unless required by applicable law or agreed to in writing, software
    8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   10 # License for the specific language governing permissions and limitations
   11 # under the License.
   12 
   13 """Main entry point into the Federation service."""
   14 
   15 import uuid
   16 
   17 from oslo_log import log
   18 
   19 from keystone.common import cache
   20 from keystone.common import driver_hints
   21 from keystone.common import manager
   22 from keystone.common import provider_api
   23 import keystone.conf
   24 from keystone import exception
   25 from keystone.federation import utils
   26 from keystone.i18n import _
   27 from keystone import notifications
   28 
   29 LOG = log.getLogger(__name__)
   30 
   31 # This is a general cache region for service providers.
   32 MEMOIZE = cache.get_memoization_decorator(group='federation')
   33 
   34 CONF = keystone.conf.CONF
   35 PROVIDERS = provider_api.ProviderAPIs
   36 
   37 
   38 class Manager(manager.Manager):
   39     """Default pivot point for the Federation backend.
   40 
   41     See :mod:`keystone.common.manager.Manager` for more details on how this
   42     dynamically calls the backend.
   43 
   44     """
   45 
   46     driver_namespace = 'keystone.federation'
   47     _provides_api = 'federation_api'
   48 
   49     def __init__(self):
   50         super(Manager, self).__init__(CONF.federation.driver)
   51         notifications.register_event_callback(
   52             notifications.ACTIONS.internal, notifications.DOMAIN_DELETED,
   53             self._cleanup_identity_provider
   54         )
   55 
   56     def _cleanup_identity_provider(self, service, resource_type, operation,
   57                                    payload):
   58         domain_id = payload['resource_info']
   59         hints = driver_hints.Hints()
   60         hints.add_filter('domain_id', domain_id)
   61         idps = self.driver.list_idps(hints=hints)
   62         for idp in idps:
   63             try:
   64                 self.delete_idp(idp['id'])
   65             except exception.IdentityProviderNotFound:
   66                 LOG.debug(('Identity Provider %(idpid)s not found when '
   67                            'deleting domain contents for %(domainid)s, '
   68                            'continuing with cleanup.'),
   69                           {'idpid': idp['id'], 'domainid': domain_id})
   70 
   71     def create_idp(self, idp_id, idp):
   72         auto_created_domain = False
   73         if not idp.get('domain_id'):
   74             idp['domain_id'] = self._create_idp_domain(idp_id)
   75             auto_created_domain = True
   76         else:
   77             self._assert_valid_domain_id(idp['domain_id'])
   78 
   79         try:
   80             return self.driver.create_idp(idp_id, idp)
   81         except exception.Conflict:
   82             # If there is a conflict storing the Identity Provider in the
   83             # backend, then we need to make sure we clean up the domain we just
   84             # created for it and raise the Conflict exception afterwards.
   85             if auto_created_domain:
   86                 self._cleanup_idp_domain(idp['domain_id'])
   87             raise
   88 
   89     def delete_idp(self, idp_id):
   90         self.driver.delete_idp(idp_id)
   91         # NOTE(lbragstad): If an identity provider is removed from the system,
   92         # then we need to invalidate the token cache. Otherwise it will be
   93         # possible for federated tokens to be considered valid after a service
   94         # provider removes a federated identity provider resource.
   95         reason = (
   96             'The token cache is being invalidated because identity provider '
   97             '%(idp_id)s has been deleted. Authorization for federated users '
   98             'will be recalculated and enforced accordingly the next time '
   99             'they authenticate or validate a token.' % {'idp_id': idp_id}
  100         )
  101         notifications.invalidate_token_cache_notification(reason)
  102 
  103     def _cleanup_idp_domain(self, domain_id):
  104         domain = {'enabled': False}
  105         PROVIDERS.resource_api.update_domain(domain_id, domain)
  106         PROVIDERS.resource_api.delete_domain(domain_id)
  107 
  108     def _create_idp_domain(self, idp_id):
  109         domain_id = uuid.uuid4().hex
  110         desc = 'Auto generated federated domain for Identity Provider: '
  111         desc += idp_id
  112         domain = {
  113             'id': domain_id,
  114             'name': domain_id,
  115             'description': desc,
  116             'enabled': True
  117         }
  118         PROVIDERS.resource_api.create_domain(domain['id'], domain)
  119         return domain_id
  120 
  121     def _assert_valid_domain_id(self, domain_id):
  122         PROVIDERS.resource_api.get_domain(domain_id)
  123 
  124     @MEMOIZE
  125     def get_enabled_service_providers(self):
  126         """List enabled service providers for Service Catalog.
  127 
  128         Service Provider in a catalog contains three attributes: ``id``,
  129         ``auth_url``, ``sp_url``, where:
  130 
  131         - id is a unique, user defined identifier for service provider object
  132         - auth_url is an authentication URL of remote Keystone
  133         - sp_url a URL accessible at the remote service provider where SAML
  134           assertion is transmitted.
  135 
  136         :returns: list of dictionaries with enabled service providers
  137         :rtype: list of dicts
  138 
  139         """
  140         def normalize(sp):
  141             ref = {
  142                 'auth_url': sp.auth_url,
  143                 'id': sp.id,
  144                 'sp_url': sp.sp_url
  145             }
  146             return ref
  147 
  148         service_providers = self.driver.get_enabled_service_providers()
  149         return [normalize(sp) for sp in service_providers]
  150 
  151     def create_sp(self, sp_id, service_provider):
  152         sp_ref = self.driver.create_sp(sp_id, service_provider)
  153         self.get_enabled_service_providers.invalidate(self)
  154         return sp_ref
  155 
  156     def delete_sp(self, sp_id):
  157         self.driver.delete_sp(sp_id)
  158         self.get_enabled_service_providers.invalidate(self)
  159 
  160     def update_sp(self, sp_id, service_provider):
  161         sp_ref = self.driver.update_sp(sp_id, service_provider)
  162         self.get_enabled_service_providers.invalidate(self)
  163         return sp_ref
  164 
  165     def evaluate(self, idp_id, protocol_id, assertion_data):
  166         mapping = self.get_mapping_from_idp_and_protocol(idp_id, protocol_id)
  167         rules = mapping['rules']
  168         rule_processor = utils.RuleProcessor(mapping['id'], rules)
  169         mapped_properties = rule_processor.process(assertion_data)
  170         return mapped_properties, mapping['id']
  171 
  172     def create_protocol(self, idp_id, protocol_id, protocol):
  173         self._validate_mapping_exists(protocol['mapping_id'])
  174         return self.driver.create_protocol(idp_id, protocol_id, protocol)
  175 
  176     def delete_protocol(self, idp_id, protocol_id):
  177         hints = driver_hints.Hints()
  178         hints.add_filter('protocol_id', protocol_id)
  179         shadow_users = PROVIDERS.shadow_users_api.list_federated_users_info(
  180             hints)
  181 
  182         self.driver.delete_protocol(idp_id, protocol_id)
  183 
  184         for shadow_user in shadow_users:
  185             PROVIDERS.identity_api._shadow_federated_user.invalidate(
  186                 PROVIDERS.identity_api, shadow_user['idp_id'],
  187                 shadow_user['protocol_id'], shadow_user['unique_id'],
  188                 shadow_user['display_name'],
  189                 shadow_user.get('extra', {}).get('email'))
  190 
  191     def update_protocol(self, idp_id, protocol_id, protocol):
  192         self._validate_mapping_exists(protocol['mapping_id'])
  193         return self.driver.update_protocol(idp_id, protocol_id, protocol)
  194 
  195     def _validate_mapping_exists(self, mapping_id):
  196         try:
  197             self.driver.get_mapping(mapping_id)
  198         except exception.MappingNotFound:
  199             msg = _('Invalid mapping id: %s')
  200             raise exception.ValidationError(message=(msg % mapping_id))