Example #1
0
    def listen(self, key=None, backlog=128):
        """Create and start listening on socket.

        Call before forking worker processes.

        Raises Exception if this has already been called.
        """

        # TODO(dims): eventlet's green dns/socket module does not actually
        # support IPv6 in getaddrinfo(). We need to get around this in the
        # future or monitor upstream for a fix.
        # Please refer below link
        # (https://bitbucket.org/eventlet/eventlet/
        # src/e0f578180d7d82d2ed3d8a96d520103503c524ec/eventlet/support/
        # greendns.py?at=0.12#cl-163)
        info = socket.getaddrinfo(self.host,
                                  self.port,
                                  socket.AF_UNSPEC,
                                  socket.SOCK_STREAM)[0]

        try:
            self.socket = eventlet.listen(info[-1], family=info[0],
                                          backlog=backlog)
        except EnvironmentError:
            LOG.error(_("Could not bind to %(host)s:%(port)s"),
                      {'host': self.host, 'port': self.port})
            raise

        LOG.info(_('Starting %(arg0)s on %(host)s:%(port)s'),
                 {'arg0': sys.argv[0],
                  'host': self.host,
                  'port': self.port})
Example #2
0
    def _is_valid_token(self, token):
        """Verify the token is valid format and has not expired."""
        current_time = timeutils.normalize_time(timeutils.utcnow())

        try:
            # Get the data we need from the correct location (V2 and V3 tokens
            # differ in structure, Try V3 first, fall back to V2 second)
            token_data = token.get('token', token.get('access'))
            expires_at = token_data.get('expires_at',
                                        token_data.get('expires'))
            if not expires_at:
                expires_at = token_data['token']['expires']
            expiry = timeutils.normalize_time(
                timeutils.parse_isotime(expires_at))
        except Exception:
            LOG.exception(_LE('Unexpected error or malformed token '
                              'determining token expiry: %s'), token)
            raise exception.TokenNotFound(_('Failed to validate token'))

        if current_time < expiry:
            self.check_revocation(token)
            # Token has not expired and has not been revoked.
            return None
        else:
            raise exception.TokenNotFound(_('Failed to validate token'))
Example #3
0
    def format_project_list(self, tenant_refs, **kwargs):
        """Format a v2 style project list, including marker/limits."""
        marker = kwargs.get('marker')
        first_index = 0
        if marker is not None:
            for (marker_index, tenant) in enumerate(tenant_refs):
                if tenant['id'] == marker:
                    # we start pagination after the marker
                    first_index = marker_index + 1
                    break
            else:
                msg = _('Marker could not be found')
                raise exception.ValidationError(message=msg)

        limit = kwargs.get('limit')
        last_index = None
        if limit is not None:
            try:
                limit = int(limit)
                if limit < 0:
                    raise AssertionError()
            except (ValueError, AssertionError):
                msg = _('Invalid limit value')
                raise exception.ValidationError(message=msg)
            last_index = first_index + limit

        tenant_refs = tenant_refs[first_index:last_index]

        for x in tenant_refs:
            if 'enabled' not in x:
                x['enabled'] = True
        o = {'tenants': tenant_refs,
             'tenants_links': []}
        return o
Example #4
0
def register_event_callback(event, resource_type, callbacks):
    if event not in ACTIONS:
        raise ValueError(_('%(event)s is not a valid notification event, must '
                           'be one of: %(actions)s') %
                         {'event': event, 'actions': ', '.join(ACTIONS)})

    if not hasattr(callbacks, '__iter__'):
        callbacks = [callbacks]

    for callback in callbacks:
        if not callable(callback):
            msg = _('Method not callable: %s') % callback
            LOG.error(msg)
            raise TypeError(msg)
        _SUBSCRIBERS.setdefault(event, {}).setdefault(resource_type, set())
        _SUBSCRIBERS[event][resource_type].add(callback)

        if LOG.logger.getEffectiveLevel() <= logging.INFO:
            # Do this only if its going to appear in the logs.
            msg = _('Callback: `%(callback)s` subscribed to event '
                    '`%(event)s`.')
            callback_info = _get_callback_info(callback)
            callback_str = '.'.join(i for i in callback_info if i is not None)
            event_str = '.'.join(['identity', resource_type, event])
            LOG.info(msg, {'callback': callback_str, 'event': event_str})
Example #5
0
 def wrapper(*args, **kwargs):
     try:
         return method(*args, **kwargs)
     except db_exception.DBDuplicateEntry as e:
         # LOG the exception for debug purposes, do not send the
         # exception details out with the raised Conflict exception
         # as it can contain raw SQL.
         LOG.debug(_conflict_msg, {'conflict_type': conflict_type,
                                   'details': six.text_type(e)})
         raise exception.Conflict(type=conflict_type,
                                  details=_('Duplicate Entry'))
     except db_exception.DBError as e:
         # TODO(blk-u): inspecting inner_exception breaks encapsulation;
         # oslo_db should provide exception we need.
         if isinstance(e.inner_exception, IntegrityError):
             # LOG the exception for debug purposes, do not send the
             # exception details out with the raised Conflict exception
             # as it can contain raw SQL.
             LOG.debug(_conflict_msg, {'conflict_type': conflict_type,
                                       'details': six.text_type(e)})
             # NOTE(morganfainberg): This is really a case where the SQL
             # failed to store the data. This is not something that the
             # user has done wrong. Example would be a ForeignKey is
             # missing; the code that is executed before reaching the
             # SQL writing to the DB should catch the issue.
             raise exception.UnexpectedError(
                 _('An unexpected error occurred when trying to '
                   'store %s') % conflict_type)
         raise
Example #6
0
def register_event_callback(event, resource_type, callbacks):
    """Register each callback with the event.

    :param event: Action being registered
    :type event: keystone.notifications.ACTIONS
    :param resource_type: Type of resource being operated on
    :type resource_type: str
    :param callbacks: Callback items to be registered with event
    :type callbacks: list
    :raises ValueError: If event is not a valid ACTION
    :raises TypeError: If callback is not callable
    """
    if event not in ACTIONS:
        raise ValueError(_('%(event)s is not a valid notification event, must '
                           'be one of: %(actions)s') %
                         {'event': event, 'actions': ', '.join(ACTIONS)})

    if not hasattr(callbacks, '__iter__'):
        callbacks = [callbacks]

    for callback in callbacks:
        if not callable(callback):
            msg = _('Method not callable: %s') % callback
            LOG.error(msg)
            raise TypeError(msg)
        _SUBSCRIBERS.setdefault(event, {}).setdefault(resource_type, set())
        _SUBSCRIBERS[event][resource_type].add(callback)

        if LOG.logger.getEffectiveLevel() <= logging.DEBUG:
            # Do this only if its going to appear in the logs.
            msg = 'Callback: `%(callback)s` subscribed to event `%(event)s`.'
            callback_info = _get_callback_info(callback)
            callback_str = '.'.join(i for i in callback_info if i is not None)
            event_str = '.'.join(['identity', resource_type, event])
            LOG.debug(msg, {'callback': callback_str, 'event': event_str})
Example #7
0
    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'].get('origin')
            host = urllib.parse.unquote_plus(origin)
        else:
            msg = _('Request must have an origin query parameter')
            LOG.error(msg)
            raise exception.ValidationError(msg)

        if host not in CONF.federation.trusted_dashboard:
            msg = _('%(host)s is not a trusted dashboard host')
            msg = msg % {'host': host}
            LOG.error(msg)
            raise exception.Unauthorized(msg)

        return host
Example #8
0
 def check_signature(self, creds_ref, credentials):
     signer = ec2_utils.Ec2Signer(creds_ref["secret"])
     signature = signer.generate(credentials)
     # NOTE(davechen): credentials.get('signature') is not guaranteed to
     # exist, we need check it explicitly.
     if credentials.get("signature"):
         if utils.auth_str_equal(credentials["signature"], signature):
             return True
         # NOTE(vish): Some client libraries don't use the port when signing
         #             requests, so try again without port.
         elif ":" in credentials["host"]:
             hostname, _port = credentials["host"].split(":")
             credentials["host"] = hostname
             # NOTE(davechen): we need reinitialize 'signer' to avoid
             # contaminated status of signature, this is similar with
             # other programming language libraries, JAVA for example.
             signer = ec2_utils.Ec2Signer(creds_ref["secret"])
             signature = signer.generate(credentials)
             if utils.auth_str_equal(credentials["signature"], signature):
                 return True
             raise exception.Unauthorized(message=_("Invalid EC2 signature."))
         else:
             raise exception.Unauthorized(message=_("EC2 signature not supplied."))
     # Raise the exception when credentials.get('signature') is None
     else:
         raise exception.Unauthorized(message=_("EC2 signature not supplied."))
Example #9
0
    def create_user(self, context, user):
        user = self._normalize_OSKSADM_password_on_request(user)
        user = self.normalize_username_in_request(user)
        user = self._normalize_dict(user)
        self.assert_admin(context)

        if 'name' not in user or not user['name']:
            msg = _('Name field is required and cannot be empty')
            raise exception.ValidationError(message=msg)
        if 'enabled' in user and not isinstance(user['enabled'], bool):
            msg = _('Enabled field must be a boolean')
            raise exception.ValidationError(message=msg)

        default_project_id = user.pop('tenantId', None)
        if default_project_id is not None:
            # Check to see if the project is valid before moving on.
            self.resource_api.get_project(default_project_id)
            user['default_project_id'] = default_project_id

        # The manager layer will generate the unique ID for users
        user_ref = self._normalize_domain_id(context, user.copy())
        new_user_ref = self.v3_to_v2_user(
            self.identity_api.create_user(user_ref))

        if default_project_id is not None:
            self.assignment_api.add_user_to_project(default_project_id,
                                                    new_user_ref['id'])
        return {'user': new_user_ref}
Example #10
0
    def authenticate(self, context, auth_payload, auth_context):
        """Two factor authentication"""

        if not self.two_factor_auth_api:
            raise exception.Unauthorized(_('%s not supported') % self.method)

        user_info = UserAuthInfo.create(auth_payload)
        user_id = user_info.user_id

        try:
            self.two_factor_auth_api.is_two_factor_enabled(user_id)
        except exception.NotFound:
            return super(TwoFactor, self).authenticate(context, auth_payload, auth_context)

        user_info = UserTwoFactorAuthInfo.create(auth_payload)

        if user_info.verification_code:
            if not self.two_factor_auth_api.verify_code(user_id, user_info.verification_code):
                raise exception.Unauthorized(_('Invalid time based code'))
        elif user_info.device_data:
            device_id = user_info.device_data['device_id']
            device_token = user_info.device_data['device_token']
            user_id = user_info.user_id

            try:
                if not self.two_factor_auth_api.is_device_valid(device_id=device_id, device_token=device_token, user_id=user_id):
                    raise exception.Unauthorized(_('Invalid device data: old token'))
            except exception.NotFound:
                raise exception.Unauthorized(_('Invalid device data: wrong data'))
        
        return super(TwoFactor, self).authenticate(context, auth_payload, auth_context)
        
            
Example #11
0
            def register_event_callbacks(self):
                # NOTE(morganfainberg): A provider who has an implicit
                # dependency on other providers may utilize the event callback
                # mechanism to react to any changes in those providers. This is
                # performed at the .provider() mechanism so that we can ensure
                # that the callback is only ever called once and guaranteed
                # to be on the properly configured and instantiated backend.
                if not hasattr(self, 'event_callbacks'):
                    return

                if not isinstance(self.event_callbacks, dict):
                    msg = _('event_callbacks must be a dict')
                    raise ValueError(msg)

                for event in self.event_callbacks:
                    if not isinstance(self.event_callbacks[event], dict):
                        msg = _('event_callbacks[%s] must be a dict') % event
                        raise ValueError(msg)
                    for resource_type in self.event_callbacks[event]:
                        # Make sure we register the provider for each event it
                        # cares to call back.
                        callbacks = self.event_callbacks[event][resource_type]
                        if not callbacks:
                            continue
                        if not hasattr(callbacks, '__iter__'):
                            # ensure the callback information is a list
                            # allowing multiple callbacks to exist
                            callbacks = [callbacks]
                        notifications.register_event_callback(event,
                                                              resource_type,
                                                              callbacks)
Example #12
0
    def _calculate_signature_v4(self, string_to_sign, secret_key):
        """Calculates a v4 signature.

        :param bytes string_to_sign: String that contains request params and
                                     is used for calculate signature of request
        :param text secret_key: Second auth key of EC2 account that is used to
                                sign requests
        """
        parts = string_to_sign.split(b'\n')
        if len(parts) != 4 or parts[0] != b'AWS4-HMAC-SHA256':
            raise exception.Unauthorized(message=_('Invalid EC2 signature.'))
        scope = parts[2].split(b'/')
        if len(scope) != 4 or scope[2] != b's3' or scope[3] != b'aws4_request':
            raise exception.Unauthorized(message=_('Invalid EC2 signature.'))

        def _sign(key, msg):
            return hmac.new(key, msg, hashlib.sha256).digest()

        signed = _sign(('AWS4' + secret_key).encode('utf-8'), scope[0])
        signed = _sign(signed, scope[1])
        signed = _sign(signed, scope[2])
        signed = _sign(signed, b'aws4_request')

        signature = hmac.new(signed, string_to_sign, hashlib.sha256)
        return signature.hexdigest()
Example #13
0
 def _assert_default_domain(self, token_ref):
     """Make sure we are operating on default domain only."""
     if (token_ref.get('token_data') and
             self.get_token_version(token_ref.get('token_data')) ==
             token.provider.V3):
         # this is a V3 token
         msg = _('Non-default domain is not supported')
         # domain scoping is prohibited
         if token_ref['token_data']['token'].get('domain'):
             raise exception.Unauthorized(
                 _('Domain scoped token is not supported'))
         # if token is scoped to trust, both trustor and trustee must
         # be in the default domain. Furthermore, the delegated project
         # must also be in the default domain
         metadata_ref = token_ref['metadata']
         if CONF.trust.enabled and 'trust_id' in metadata_ref:
             trust_ref = self.trust_api.get_trust(metadata_ref['trust_id'])
             trustee_user_ref = self.identity_api.get_user(
                 trust_ref['trustee_user_id'])
             if (trustee_user_ref['domain_id'] !=
                     CONF.identity.default_domain_id):
                 raise exception.Unauthorized(msg)
             trustor_user_ref = self.identity_api.get_user(
                 trust_ref['trustor_user_id'])
             if (trustor_user_ref['domain_id'] !=
                     CONF.identity.default_domain_id):
                 raise exception.Unauthorized(msg)
             project_ref = self.resource_api.get_project(
                 trust_ref['project_id'])
             if (project_ref['domain_id'] !=
                     CONF.identity.default_domain_id):
                 raise exception.Unauthorized(msg)
Example #14
0
 def _require_user_xor_group(self, user_id, group_id):
     if user_id and group_id:
         msg = _('Specify a user or group, not both')
         raise exception.ValidationError(msg)
     if not user_id and not group_id:
         msg = _('Specify one of user or group')
         raise exception.ValidationError(msg)
Example #15
0
    def create_user(self, user_id, user):
        try:
            self.get_user(user_id)
        except exception.UserNotFound:
            pass
        else:
            msg = _("Duplicate ID, %s.") % user_id
            raise exception.Conflict(type="user", details=msg)

        try:
            self.get_user_by_name(user["name"], user["domain_id"])
        except exception.UserNotFound:
            pass
        else:
            msg = _("Duplicate name, %s.") % user["name"]
            raise exception.Conflict(type="user", details=msg)

        user = utils.hash_user_password(user)
        new_user = user.copy()

        new_user.setdefault("groups", [])

        self.db.set("user-%s" % user_id, new_user)
        domain_id = user["domain_id"]
        user_name_key = self._calc_user_name_key(new_user["name"], domain_id)
        self.db.set(user_name_key, new_user)
        self._user_id_to_domain_id.notify_user_created(user_id, domain_id)
        user_list = set(self.db.get("user_list", []))
        user_list.add(user_id)
        self.db.set("user_list", list(user_list))
        return identity.filter_user(new_user)
Example #16
0
    def get_token_provider(cls):
        """Return package path to the configured token provider.

        The value should come from ``keystone.conf`` ``[token] provider``,
        however this method ensures backwards compatibility for
        ``keystone.conf`` ``[signing] token_format`` until Havana + 2.

        Return the provider based on ``token_format`` if ``provider`` is not
        set. Otherwise, ignore ``token_format`` and return the configured
        ``provider`` instead.

        """

        if CONF.signing.token_format:
            LOG.warn(_('[signing] token_format is deprecated. '
                       'Please change to setting the [token] provider '
                       'configuration value instead'))
            try:

                mapped = _FORMAT_TO_PROVIDER[CONF.signing.token_format]
            except KeyError:
                raise exception.UnexpectedError(
                    _('Unrecognized keystone.conf [signing] token_format: '
                      'expected either \'UUID\' or \'PKI\''))
            return mapped

        if CONF.token.provider is None:
            return PKIZ_PROVIDER
        else:
            return CONF.token.provider
Example #17
0
 def _require_domain_xor_project(self, domain_id, project_id):
     if domain_id and project_id:
         msg = _('Specify a domain or project, not both')
         raise exception.ValidationError(msg)
     if not domain_id and not project_id:
         msg = _('Specify one of domain or project')
         raise exception.ValidationError(msg)
Example #18
0
    def _get_domain_id_for_list_request(self, context):
        """Get the domain_id for a v3 list call.

        If we running with multiple domain drivers, then the caller must
        specify a domain_id either as a filter or as part of the token scope.

        """
        if not CONF.identity.domain_specific_drivers_enabled:
            # We don't need to specify a domain ID in this case
            return

        if context['query_string'].get('domain_id') is not None:
            return context['query_string'].get('domain_id')

        try:
            token_ref = self.token_api.get_token(context['token_id'])
            token = token_ref['token_data']['token']
        except KeyError:
            raise exception.ValidationError(
                _('domain_id is required as part of entity'))
        except exception.TokenNotFound:
            LOG.warning(_('Invalid token found while getting domain ID '
                          'for list request'))
            raise exception.Unauthorized()

        if 'domain' in token:
            return token['domain']['id']
        else:
            LOG.warning(
                _('No domain information specified as part of list request'))
            raise exception.Unauthorized()
Example #19
0
    def _get_domain_id_from_token(self, context):
        """Get the domain_id for a v3 create call.

        In the case of a v3 create entity call that does not specify a domain
        ID, the spec says that we should use the domain scoping from the token
        being used.

        """
        # We could make this more efficient by loading the domain_id
        # into the context in the wrapper function above (since
        # this version of normalize_domain will only be called inside
        # a v3 protected call).  However, this optimization is probably not
        # worth the duplication of state
        try:
            token_ref = self.token_api.get_token(context['token_id'])
        except KeyError:
            # This might happen if we use the Admin token, for instance
            raise exception.ValidationError(
                _('A domain-scoped token must be used'))
        except exception.TokenNotFound:
            LOG.warning(_('Invalid token found while getting domain ID '
                          'for list request'))
            raise exception.Unauthorized()

        if token_ref.get('token_data', {}).get('token', {}).get('domain', {}):
            return token_ref['token_data']['token']['domain']['id']
        else:
            # TODO(henry-nash): We should issue an exception here since if
            # a v3 call does not explicitly specify the domain_id in the
            # entity, it should be using a domain scoped token.  However,
            # the current tempest heat tests issue a v3 call without this.
            # This is raised as bug #1283539.  Once this is fixed, we
            # should remove the line below and replace it with an error.
            return CONF.identity.default_domain_id
Example #20
0
 def _assert_default_domain(self, token_ref):
     """Make sure we are operating on default domain only."""
     if token_ref.get("token_data") and self.get_token_version(token_ref.get("token_data")) == token.provider.V3:
         # this is a V3 token
         msg = _("Non-default domain is not supported")
         # user in a non-default is prohibited
         if token_ref["token_data"]["token"]["user"]["domain"]["id"] != CONF.identity.default_domain_id:
             raise exception.Unauthorized(msg)
         # domain scoping is prohibited
         if token_ref["token_data"]["token"].get("domain"):
             raise exception.Unauthorized(_("Domain scoped token is not supported"))
         # project in non-default domain is prohibited
         if token_ref["token_data"]["token"].get("project"):
             project = token_ref["token_data"]["token"]["project"]
             project_domain_id = project["domain"]["id"]
             # scoped to project in non-default domain is prohibited
             if project_domain_id != CONF.identity.default_domain_id:
                 raise exception.Unauthorized(msg)
         # if token is scoped to trust, both trustor and trustee must
         # be in the default domain. Furthermore, the delegated project
         # must also be in the default domain
         metadata_ref = token_ref["metadata"]
         if CONF.trust.enabled and "trust_id" in metadata_ref:
             trust_ref = self.trust_api.get_trust(metadata_ref["trust_id"])
             trustee_user_ref = self.identity_api.get_user(trust_ref["trustee_user_id"])
             if trustee_user_ref["domain_id"] != CONF.identity.default_domain_id:
                 raise exception.Unauthorized(msg)
             trustor_user_ref = self.identity_api.get_user(trust_ref["trustor_user_id"])
             if trustor_user_ref["domain_id"] != CONF.identity.default_domain_id:
                 raise exception.Unauthorized(msg)
             project_ref = self.resource_api.get_project(trust_ref["project_id"])
             if project_ref["domain_id"] != CONF.identity.default_domain_id:
                 raise exception.Unauthorized(msg)
Example #21
0
    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)

        if 'origin' in context['query_string']:
            origin = context['query_string'].get('origin')
            host = urllib.parse.unquote_plus(origin)
        else:
            msg = _('Request must have an origin query parameter')
            LOG.error(msg)
            raise exception.ValidationError(msg)

        if self._is_trusted_dashboard(host):
            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)
        else:
            msg = _('%(host)s is not a trusted dashboard host')
            msg = msg % {'host': host}
            LOG.error(msg)
            raise exception.Unauthorized(msg)
Example #22
0
    def _get_sso_origin_host(self, request):
        """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

        """
        origin = request.params.get('origin')

        if not origin:
            msg = _('Request must have an origin query parameter')
            LOG.error(msg)
            raise exception.ValidationError(msg)

        host = urllib.parse.unquote_plus(origin)

        # 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
Example #23
0
def format_url(url, substitutions):
    """Formats a user-defined URL with the given substitutions.

    :param string url: the URL to be formatted
    :param dict substitutions: the dictionary used for substitution
    :returns: a formatted URL

    """
    try:
        result = url.replace('$(', '%(') % substitutions
    except AttributeError:
        LOG.error(_('Malformed endpoint - %(url)r is not a string'),
                  {"url": url})
        raise exception.MalformedEndpoint(endpoint=url)
    except KeyError as e:
        LOG.error(_("Malformed endpoint %(url)s - unknown key %(keyerror)s"),
                  {"url": url,
                   "keyerror": e})
        raise exception.MalformedEndpoint(endpoint=url)
    except TypeError as e:
        LOG.error(_("Malformed endpoint '%(url)s'. The following type error "
                    "occurred during string substitution: %(typeerror)s"),
                  {"url": url,
                   "typeerror": e})
        raise exception.MalformedEndpoint(endpoint=url)
    except ValueError as e:
        LOG.error(_("Malformed endpoint %s - incomplete format "
                    "(are you missing a type notifier ?)"), url)
        raise exception.MalformedEndpoint(endpoint=url)
    return result
Example #24
0
def load_auth_methods():
    global AUTH_PLUGINS_LOADED

    if AUTH_PLUGINS_LOADED:
        # Only try and load methods a single time.
        return
    # config.setup_authentication should be idempotent, call it to ensure we
    # have setup all the appropriate configuration options we may need.
    config.setup_authentication()
    for plugin in CONF.auth.methods:
        if '.' in plugin:
            # NOTE(morganfainberg): if '.' is in the plugin name, it should be
            # imported rather than used as a plugin identifier.
            plugin_class = plugin
            driver = importutils.import_object(plugin)
            if not hasattr(driver, 'method'):
                raise ValueError(_('Cannot load an auth-plugin by class-name '
                                   'without a "method" attribute defined: %s'),
                                 plugin_class)

            LOG.info(_LI('Loading auth-plugins by class-name is deprecated.'))
            plugin_name = driver.method
        else:
            plugin_name = plugin
            plugin_class = CONF.auth.get(plugin)
            driver = importutils.import_object(plugin_class)
        if plugin_name in AUTH_METHODS:
            raise ValueError(_('Auth plugin %(plugin)s is requesting '
                               'previously registered method %(method)s') %
                             {'plugin': plugin_class, 'method': driver.method})
        AUTH_METHODS[plugin_name] = driver
    AUTH_PLUGINS_LOADED = True
Example #25
0
    def _delete_tokens_for_role(self, role_id):
        assignments = self.list_role_assignments_for_role(role_id=role_id)

        # Iterate over the assignments for this role and build the list of
        # user or user+project IDs for the tokens we need to delete
        user_ids = set()
        user_and_project_ids = list()
        for assignment in assignments:
            # If we have a project assignment, then record both the user and
            # project IDs so we can target the right token to delete. If it is
            # a domain assignment, we might as well kill all the tokens for
            # the user, since in the vast majority of cases all the tokens
            # for a user will be within one domain anyway, so not worth
            # trying to delete tokens for each project in the domain.
            if 'user_id' in assignment:
                if 'project_id' in assignment:
                    user_and_project_ids.append(
                        (assignment['user_id'], assignment['project_id']))
                elif 'domain_id' in assignment:
                    user_ids.add(assignment['user_id'])
            elif 'group_id' in assignment:
                # Add in any users for this group, being tolerant of any
                # cross-driver database integrity errors.
                try:
                    users = self.identity_api.list_users_in_group(
                        assignment['group_id'])
                except exception.GroupNotFound:
                    # Ignore it, but log a debug message
                    if 'project_id' in assignment:
                        target = _('Project (%s)') % assignment['project_id']
                    elif 'domain_id' in assignment:
                        target = _('Domain (%s)') % assignment['domain_id']
                    else:
                        target = _('Unknown Target')
                    msg = ('Group (%(group)s), referenced in assignment '
                           'for %(target)s, not found - ignoring.')
                    LOG.debug(msg, {'group': assignment['group_id'],
                                    'target': target})
                    continue

                if 'project_id' in assignment:
                    for user in users:
                        user_and_project_ids.append(
                            (user['id'], assignment['project_id']))
                elif 'domain_id' in assignment:
                    for user in users:
                        user_ids.add(user['id'])

        # Now process the built up lists.  Before issuing calls to delete any
        # tokens, let's try and minimize the number of calls by pruning out
        # any user+project deletions where a general token deletion for that
        # same user is also planned.
        user_and_project_ids_to_action = []
        for user_and_project_id in user_and_project_ids:
            if user_and_project_id[0] not in user_ids:
                user_and_project_ids_to_action.append(user_and_project_id)

        self.token_api.delete_tokens_for_users(user_ids)
        for user_id, project_id in user_and_project_ids_to_action:
            self.token_api.delete_tokens_for_user(user_id, project_id)
Example #26
0
    def _validate_token_user(self):
        if self.trust_scoped:
            if self.user_id != self.trustee['id']:
                raise exception.Forbidden(_('User is not a trustee.'))
            try:
                PROVIDERS.resource_api.assert_domain_enabled(
                    self.trustor['domain_id']
                )
            except AssertionError:
                raise exception.TokenNotFound(_('Trustor domain is disabled.'))
            try:
                PROVIDERS.resource_api.assert_domain_enabled(
                    self.trustee['domain_id']
                )
            except AssertionError:
                raise exception.TokenNotFound(_('Trustee domain is disabled.'))

            try:
                PROVIDERS.identity_api.assert_user_enabled(
                    self.trustor['id']
                )
            except AssertionError:
                raise exception.Forbidden(_('Trustor is disabled.'))

        if not self.user_domain.get('enabled'):
            msg = ('Unable to validate token because domain %(id)s is '
                   'disabled') % {'id': self.user_domain['id']}
            tr_msg = _('Unable to validate token because domain %(id)s is '
                       'disabled') % {'id': self.user_domain['id']}
            LOG.warning(msg)
            raise exception.DomainNotFound(tr_msg)
Example #27
0
    def create_role(self, role_id, role):
        self.role.check_allow_create()
        try:
            self.get_role(role_id)
        except exception.NotFound:  # nosec
            # The call to self.get_role() raises this exception when a role
            # with the given ID doesn't exist. This was done to ensure that
            # a role with the new role's ID doesn't already exist. As such this
            # exception is expected to happen in the normal case. The abnormal
            # case would be if the role does already exist. So this exception
            # is expected to be ignored and there's no security issue with
            # ignoring it.
            pass
        else:
            msg = _('Duplicate ID, %s.') % role_id
            raise exception.Conflict(type='role', details=msg)

        try:
            self.role.get_by_name(role['name'])
        except exception.NotFound:  # nosec
            # The call to self.role.get_by_name() raises this exception when a
            # role with the given name doesn't exist. This was done to ensure
            # that a role with the new role's name doesn't already exist. As
            # such this exception is expected to happen in the normal case. The
            # abnormal case would be if a role with the same name does already
            # exist. So this exception is expected to be ignored and there's no
            # security issue with ignoring it.
            pass
        else:
            msg = _('Duplicate name, %s.') % role['name']
            raise exception.Conflict(type='role', details=msg)

        return self.role.create(role)
Example #28
0
    def create_project(self, project_id, project, initiator=None):
        project = project.copy()
        project.setdefault('enabled', True)
        project['enabled'] = clean.project_enabled(project['enabled'])
        project.setdefault('description', '')
        project.setdefault('parent_id', None)

        if project.get('parent_id') is not None:
            parent_ref = self.get_project(project.get('parent_id'))
            parents_list = self.list_project_parents(parent_ref['id'])
            parents_list.append(parent_ref)
            for ref in parents_list:
                if ref.get('domain_id') != project.get('domain_id'):
                    raise exception.ForbiddenAction(
                        action=_('cannot create a project within a different '
                                 'domain than its parents.'))
                if not ref.get('enabled', True):
                    raise exception.ForbiddenAction(
                        action=_('cannot create a project in a '
                                 'branch containing a disabled '
                                 'project: %s') % ref['id'])
            self._assert_max_hierarchy_depth(project.get('parent_id'),
                                             parents_list)

        ret = self.driver.create_project(project_id, project)
        notifications.Audit.created(self._PROJECT, project_id, initiator)
        if MEMOIZE.should_cache(ret):
            self.get_project.set(ret, self, project_id)
            self.get_project_by_name.set(ret, self, ret['name'],
                                         ret['domain_id'])
        return ret
Example #29
0
    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)

        if "origin" in context["query_string"]:
            origin = context["query_string"].get("origin")
            host = urllib.parse.unquote_plus(origin)
        else:
            msg = _("Request must have an origin query parameter")
            LOG.error(msg)
            raise exception.ValidationError(msg)

        if host in CONF.federation.trusted_dashboard:
            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)
        else:
            msg = _("%(host)s is not a trusted dashboard host")
            msg = msg % {"host": host}
            LOG.error(msg)
            raise exception.Unauthorized(msg)
Example #30
0
    def _expand_project_ref(self, request, ref):
        params = request.params
        context = request.context_dict

        parents_as_list = "parents_as_list" in params and (self.query_filter_is_true(params["parents_as_list"]))
        parents_as_ids = "parents_as_ids" in params and (self.query_filter_is_true(params["parents_as_ids"]))

        subtree_as_list = "subtree_as_list" in params and (self.query_filter_is_true(params["subtree_as_list"]))
        subtree_as_ids = "subtree_as_ids" in params and (self.query_filter_is_true(params["subtree_as_ids"]))

        # parents_as_list and parents_as_ids are mutually exclusive
        if parents_as_list and parents_as_ids:
            msg = _("Cannot use parents_as_list and parents_as_ids query " "params at the same time.")
            raise exception.ValidationError(msg)

        # subtree_as_list and subtree_as_ids are mutually exclusive
        if subtree_as_list and subtree_as_ids:
            msg = _("Cannot use subtree_as_list and subtree_as_ids query " "params at the same time.")
            raise exception.ValidationError(msg)

        if parents_as_list:
            parents = self.resource_api.list_project_parents(ref["id"], request.context.user_id)
            ref["parents"] = [ProjectV3.wrap_member(context, p) for p in parents]
        elif parents_as_ids:
            ref["parents"] = self.resource_api.get_project_parents_as_ids(ref)

        if subtree_as_list:
            subtree = self.resource_api.list_projects_in_subtree(ref["id"], request.context.user_id)
            ref["subtree"] = [ProjectV3.wrap_member(context, p) for p in subtree]
        elif subtree_as_ids:
            ref["subtree"] = self.resource_api.get_projects_in_subtree_as_ids(ref["id"])
Example #31
0
class NotImplemented(Error):
    message_format = _("The action you have requested has not"
                       " been implemented.")
    code = 501
    title = 'Not Implemented'
Example #32
0
class UnsupportedTokenVersionException(UnexpectedError):
    debug_message_format = _('Token version is unrecognizable or '
                             'unsupported.')
Example #33
0
 def __init__(self, mod_name, path):
     super(MigrationNotProvided, self).__init__(_(
         "%(mod_name)s doesn't provide database migrations. The migration"
         " repository path at %(path)s doesn't exist or isn't a directory."
     ) % {'mod_name': mod_name, 'path': path})
Example #34
0
class MultipleSQLDriversInConfig(UnexpectedError):
    debug_message_format = _('The Keystone domain-specific configuration has '
                             'specified more than one SQL driver (only one is '
                             'permitted): %(source)s.')
Example #35
0
class KeysNotFound(UnexpectedError):
    debug_message_format = _('No encryption keys found; run keystone-manage '
                             'fernet_setup to bootstrap one.')
Example #36
0
class ConfigFileNotFound(UnexpectedError):
    debug_message_format = _("The Keystone configuration file %(config_file)s "
                             "could not be found.")
Example #37
0
class Gone(Error):
    message_format = _("The service you have requested is no"
                       " longer available on this server.")
    code = 410
    title = 'Gone'
Example #38
0
class DirectMappingError(UnexpectedError):
    message_format = _("Local section in mapping %(mapping_id)s refers to a "
                       "remote match that doesn't exist "
                       "(e.g. {0} in a local section).")
Example #39
0
class MalformedEndpoint(UnexpectedError):
    debug_message_format = _("Malformed endpoint URL (%(endpoint)s),"
                             " see ERROR log for details.")
Example #40
0
class AssignmentTypeCalculationError(UnexpectedError):
    debug_message_format = _(
        'Unexpected combination of grant attributes - '
        'User: %(user_id)s, Group: %(group_id)s, Project: %(project_id)s, '
        'Domain: %(domain_id)s.')
Example #41
0
class TrustConsumeMaximumAttempt(UnexpectedError):
    debug_message_format = _("Unable to consume trust %(trust_id)s. Unable to "
                             "acquire lock.")
Example #42
0
class MappedGroupNotFound(UnexpectedError):
    debug_message_format = _("Group %(group_id)s returned by mapping "
                             "%(mapping_id)s was not found in the backend.")
Example #43
0
class DomainConfigNotFound(NotFound):
    message_format = _('Could not find %(group_or_option)s in domain '
                       'configuration for domain %(domain_id)s.')
Example #44
0
class CertificateFilesUnavailable(UnexpectedError):
    debug_message_format = _("Expected signing certificates are not available "
                             "on the server. Please check Keystone "
                             "configuration.")
Example #45
0
class ServiceProviderNotFound(NotFound):
    message_format = _("Could not find Service Provider: %(sp_id)s.")
Example #46
0
class Conflict(Error):
    message_format = _("Conflict occurred attempting to store %(type)s -"
                       " %(details)s.")
    code = 409
    title = 'Conflict'
Example #47
0
class EndpointGroupNotFound(NotFound):
    message_format = _("Could not find Endpoint Group: %(endpoint_group_id)s.")
Example #48
0
class FederatedProtocolNotFound(NotFound):
    message_format = _("Could not find federated protocol %(protocol_id)s for"
                       " Identity Provider: %(idp_id)s.")
Example #49
0
class CredentialNotFound(NotFound):
    message_format = _("Could not find credential: %(credential_id)s.")
Example #50
0
class IdentityProviderNotFound(NotFound):
    message_format = _("Could not find Identity Provider: %(idp_id)s.")
Example #51
0
class TrustNotFound(NotFound):
    message_format = _("Could not find trust: %(trust_id)s.")
Example #52
0
class VersionNotFound(NotFound):
    message_format = _("Could not find version: %(version)s.")
Example #53
0
class GroupNotFound(NotFound):
    message_format = _("Could not find group: %(group_id)s.")
Example #54
0
class TrustUseLimitReached(Forbidden):
    message_format = _("No remaining uses for trust: %(trust_id)s.")
Example #55
0
class TokenNotFound(NotFound):
    message_format = _("Could not find token: %(token_id)s.")
Example #56
0
class MappingNotFound(NotFound):
    message_format = _("Could not find mapping: %(mapping_id)s.")
Example #57
0
class DomainNotFound(NotFound):
    message_format = _("Could not find domain: %(domain_id)s.")
Example #58
0
class UserNotFound(NotFound):
    message_format = _("Could not find user: %(user_id)s.")
Example #59
0
class ServiceNotFound(NotFound):
    message_format = _("Could not find service: %(service_id)s.")
Example #60
0
class ProjectNotFound(NotFound):
    message_format = _("Could not find project: %(project_id)s.")