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})
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'))
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
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})
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
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})
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
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."))
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}
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)
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)
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()
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)
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)
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)
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
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)
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()
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
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)
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)
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
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
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
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)
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)
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)
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
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)
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"])
class NotImplemented(Error): message_format = _("The action you have requested has not" " been implemented.") code = 501 title = 'Not Implemented'
class UnsupportedTokenVersionException(UnexpectedError): debug_message_format = _('Token version is unrecognizable or ' 'unsupported.')
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})
class MultipleSQLDriversInConfig(UnexpectedError): debug_message_format = _('The Keystone domain-specific configuration has ' 'specified more than one SQL driver (only one is ' 'permitted): %(source)s.')
class KeysNotFound(UnexpectedError): debug_message_format = _('No encryption keys found; run keystone-manage ' 'fernet_setup to bootstrap one.')
class ConfigFileNotFound(UnexpectedError): debug_message_format = _("The Keystone configuration file %(config_file)s " "could not be found.")
class Gone(Error): message_format = _("The service you have requested is no" " longer available on this server.") code = 410 title = 'Gone'
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).")
class MalformedEndpoint(UnexpectedError): debug_message_format = _("Malformed endpoint URL (%(endpoint)s)," " see ERROR log for details.")
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.')
class TrustConsumeMaximumAttempt(UnexpectedError): debug_message_format = _("Unable to consume trust %(trust_id)s. Unable to " "acquire lock.")
class MappedGroupNotFound(UnexpectedError): debug_message_format = _("Group %(group_id)s returned by mapping " "%(mapping_id)s was not found in the backend.")
class DomainConfigNotFound(NotFound): message_format = _('Could not find %(group_or_option)s in domain ' 'configuration for domain %(domain_id)s.')
class CertificateFilesUnavailable(UnexpectedError): debug_message_format = _("Expected signing certificates are not available " "on the server. Please check Keystone " "configuration.")
class ServiceProviderNotFound(NotFound): message_format = _("Could not find Service Provider: %(sp_id)s.")
class Conflict(Error): message_format = _("Conflict occurred attempting to store %(type)s -" " %(details)s.") code = 409 title = 'Conflict'
class EndpointGroupNotFound(NotFound): message_format = _("Could not find Endpoint Group: %(endpoint_group_id)s.")
class FederatedProtocolNotFound(NotFound): message_format = _("Could not find federated protocol %(protocol_id)s for" " Identity Provider: %(idp_id)s.")
class CredentialNotFound(NotFound): message_format = _("Could not find credential: %(credential_id)s.")
class IdentityProviderNotFound(NotFound): message_format = _("Could not find Identity Provider: %(idp_id)s.")
class TrustNotFound(NotFound): message_format = _("Could not find trust: %(trust_id)s.")
class VersionNotFound(NotFound): message_format = _("Could not find version: %(version)s.")
class GroupNotFound(NotFound): message_format = _("Could not find group: %(group_id)s.")
class TrustUseLimitReached(Forbidden): message_format = _("No remaining uses for trust: %(trust_id)s.")
class TokenNotFound(NotFound): message_format = _("Could not find token: %(token_id)s.")
class MappingNotFound(NotFound): message_format = _("Could not find mapping: %(mapping_id)s.")
class DomainNotFound(NotFound): message_format = _("Could not find domain: %(domain_id)s.")
class UserNotFound(NotFound): message_format = _("Could not find user: %(user_id)s.")
class ServiceNotFound(NotFound): message_format = _("Could not find service: %(service_id)s.")
class ProjectNotFound(NotFound): message_format = _("Could not find project: %(project_id)s.")