"Fossies" - the Fresh Open Source Software Archive

Member "keystone-18.0.0/keystone/exception.py" (14 Oct 2020, 26088 Bytes) of package /linux/misc/openstack/keystone-18.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 "exception.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 17.0.0_vs_18.0.0.

    1 # Copyright 2012 OpenStack Foundation
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 # not use this file except in compliance with the License. You may obtain
    5 # a copy of the License at
    6 #
    7 #      http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations
   13 # under the License.
   14 
   15 import http.client
   16 from oslo_log import log
   17 from oslo_utils import encodeutils
   18 
   19 import keystone.conf
   20 from keystone.i18n import _
   21 
   22 
   23 CONF = keystone.conf.CONF
   24 LOG = log.getLogger(__name__)
   25 
   26 KEYSTONE_API_EXCEPTIONS = set([])
   27 
   28 # Tests use this to make exception message format errors fatal
   29 _FATAL_EXCEPTION_FORMAT_ERRORS = False
   30 
   31 
   32 def _format_with_unicode_kwargs(msg_format, kwargs):
   33     try:
   34         return msg_format % kwargs
   35     except UnicodeDecodeError:
   36         try:
   37             kwargs = {k: encodeutils.safe_decode(v)
   38                       for k, v in kwargs.items()}
   39         except UnicodeDecodeError:
   40             # NOTE(jamielennox): This is the complete failure case
   41             # at least by showing the template we have some idea
   42             # of where the error is coming from
   43             return msg_format
   44 
   45         return msg_format % kwargs
   46 
   47 
   48 class _KeystoneExceptionMeta(type):
   49     """Automatically Register the Exceptions in 'KEYSTONE_API_EXCEPTIONS' list.
   50 
   51     The `KEYSTONE_API_EXCEPTIONS` list is utilized by flask to register a
   52     handler to emit sane details when the exception occurs.
   53     """
   54 
   55     def __new__(mcs, name, bases, class_dict):
   56         """Create a new instance and register with KEYSTONE_API_EXCEPTIONS."""
   57         cls = type.__new__(mcs, name, bases, class_dict)
   58         KEYSTONE_API_EXCEPTIONS.add(cls)
   59         return cls
   60 
   61 
   62 class Error(Exception, metaclass=_KeystoneExceptionMeta):
   63     """Base error class.
   64 
   65     Child classes should define an HTTP status code, title, and a
   66     message_format.
   67 
   68     """
   69 
   70     code = None
   71     title = None
   72     message_format = None
   73 
   74     def __init__(self, message=None, **kwargs):
   75         try:
   76             message = self._build_message(message, **kwargs)
   77         except KeyError:
   78             # if you see this warning in your logs, please raise a bug report
   79             if _FATAL_EXCEPTION_FORMAT_ERRORS:
   80                 raise
   81             else:
   82                 LOG.warning('missing exception kwargs (programmer error)')
   83                 message = self.message_format
   84 
   85         super(Error, self).__init__(message)
   86 
   87     def _build_message(self, message, **kwargs):
   88         """Build and returns an exception message.
   89 
   90         :raises KeyError: given insufficient kwargs
   91 
   92         """
   93         if message:
   94             return message
   95         return _format_with_unicode_kwargs(self.message_format, kwargs)
   96 
   97 
   98 class ValidationError(Error):
   99     message_format = _("Expecting to find %(attribute)s in %(target)s."
  100                        " The server could not comply with the request"
  101                        " since it is either malformed or otherwise"
  102                        " incorrect. The client is assumed to be in error.")
  103     code = int(http.client.BAD_REQUEST)
  104     title = http.client.responses[http.client.BAD_REQUEST]
  105 
  106 
  107 class URLValidationError(ValidationError):
  108     message_format = _("Cannot create an endpoint with an invalid URL:"
  109                        " %(url)s.")
  110 
  111 
  112 class PasswordValidationError(ValidationError):
  113     message_format = _("Password validation error: %(detail)s.")
  114 
  115 
  116 class PasswordRequirementsValidationError(PasswordValidationError):
  117     message_format = _("The password does not match the requirements:"
  118                        " %(detail)s.")
  119 
  120 
  121 class PasswordHistoryValidationError(PasswordValidationError):
  122     message_format = _("The new password cannot be identical to a "
  123                        "previous password. The total number which "
  124                        "includes the new password must be unique is "
  125                        "%(unique_count)s.")
  126 
  127 
  128 class PasswordAgeValidationError(PasswordValidationError):
  129     message_format = _("You cannot change your password at this time due "
  130                        "to the minimum password age. Once you change your "
  131                        "password, it must be used for %(min_age_days)d day(s) "
  132                        "before it can be changed. Please try again in "
  133                        "%(days_left)d day(s) or contact your administrator to "
  134                        "reset your password.")
  135 
  136 
  137 class PasswordSelfServiceDisabled(PasswordValidationError):
  138     message_format = _("You cannot change your password at this time due "
  139                        "to password policy disallowing password changes. "
  140                        "Please contact your administrator to reset your "
  141                        "password.")
  142 
  143 
  144 class SchemaValidationError(ValidationError):
  145     # NOTE(lbragstad): For whole OpenStack message consistency, this error
  146     # message has been written in a format consistent with WSME.
  147     message_format = _("%(detail)s")
  148 
  149 
  150 class ValidationTimeStampError(Error):
  151     message_format = _("Timestamp not in expected format."
  152                        " The server could not comply with the request"
  153                        " since it is either malformed or otherwise"
  154                        " incorrect. The client is assumed to be in error.")
  155     code = int(http.client.BAD_REQUEST)
  156     title = http.client.responses[http.client.BAD_REQUEST]
  157 
  158 
  159 class InvalidOperatorError(ValidationError):
  160     message_format = _("The given operator %(_op)s is not valid."
  161                        " It must be one of the following:"
  162                        " 'eq', 'neq', 'lt', 'lte', 'gt', or 'gte'.")
  163 
  164 
  165 class ValidationExpirationError(Error):
  166     message_format = _("The 'expires_at' must not be before now."
  167                        " The server could not comply with the request"
  168                        " since it is either malformed or otherwise"
  169                        " incorrect. The client is assumed to be in error.")
  170     code = int(http.client.BAD_REQUEST)
  171     title = http.client.responses[http.client.BAD_REQUEST]
  172 
  173 
  174 class StringLengthExceeded(ValidationError):
  175     message_format = _("String length exceeded. The length of"
  176                        " string '%(string)s' exceeds the limit"
  177                        " of column %(type)s(CHAR(%(length)d)).")
  178 
  179 
  180 class AmbiguityError(ValidationError):
  181     message_format = _("There are multiple %(resource)s entities named"
  182                        " '%(name)s'. Please use ID instead of names to"
  183                        " resolve the ambiguity.")
  184 
  185 
  186 class ApplicationCredentialValidationError(ValidationError):
  187     message_format = _("Invalid application credential: %(detail)s")
  188 
  189 
  190 class CircularRegionHierarchyError(Error):
  191     message_format = _("The specified parent region %(parent_region_id)s "
  192                        "would create a circular region hierarchy.")
  193     code = int(http.client.BAD_REQUEST)
  194     title = http.client.responses[http.client.BAD_REQUEST]
  195 
  196 
  197 class ForbiddenNotSecurity(Error):
  198     """When you want to return a 403 Forbidden response but not security.
  199 
  200     Use this for errors where the message is always safe to present to the user
  201     and won't give away extra information.
  202 
  203     """
  204 
  205     code = int(http.client.FORBIDDEN)
  206     title = http.client.responses[http.client.FORBIDDEN]
  207 
  208 
  209 class PasswordVerificationError(ForbiddenNotSecurity):
  210     message_format = _("The password length must be less than or equal "
  211                        "to %(size)i. The server could not comply with the "
  212                        "request because the password is invalid.")
  213 
  214 
  215 class RegionDeletionError(ForbiddenNotSecurity):
  216     message_format = _("Unable to delete region %(region_id)s because it or "
  217                        "its child regions have associated endpoints.")
  218 
  219 
  220 class ApplicationCredentialLimitExceeded(ForbiddenNotSecurity):
  221     message_format = _("Unable to create additional application credentials, "
  222                        "maximum of %(limit)d already exceeded for user.")
  223 
  224 
  225 class CredentialLimitExceeded(ForbiddenNotSecurity):
  226     message_format = _("Unable to create additional credentials, maximum "
  227                        "of %(limit)d already exceeded for user.")
  228 
  229 
  230 class SecurityError(Error):
  231     """Security error exception.
  232 
  233     Avoids exposing details of security errors, unless in insecure_debug mode.
  234 
  235     """
  236 
  237     amendment = _('(Disable insecure_debug mode to suppress these details.)')
  238 
  239     def __deepcopy__(self):
  240         """Override the default deepcopy.
  241 
  242         Keystone :class:`keystone.exception.Error` accepts an optional message
  243         that will be used when rendering the exception object as a string. If
  244         not provided the object's message_format attribute is used instead.
  245         :class:`keystone.exception.SecurityError` is a little different in
  246         that it only uses the message provided to the initializer when
  247         keystone is in `insecure_debug` mode. Instead it will use its
  248         `message_format`. This is to ensure that sensitive details are not
  249         leaked back to the caller in a production deployment.
  250 
  251         This dual mode for string rendering causes some odd behaviour when
  252         combined with oslo_i18n translation. Any object used as a value for
  253         formatting a translated string is deep copied.
  254 
  255         The copy causes an issue. The deep copy process actually creates a new
  256         exception instance with the rendered string. Then when that new
  257         instance is rendered as a string to use for substitution a warning is
  258         logged. This is because the code tries to use the `message_format` in
  259         secure mode, but the required kwargs are not in the deep copy.
  260 
  261         The end result is not an error because when the KeyError is caught the
  262         instance's ``message`` is used instead and this has the properly
  263         translated message. The only indication that something is wonky is a
  264         message in the warning log.
  265         """
  266         return self
  267 
  268     def _build_message(self, message, **kwargs):
  269         """Only returns detailed messages in insecure_debug mode."""
  270         if message and CONF.insecure_debug:
  271             if isinstance(message, str):
  272                 # Only do replacement if message is string. The message is
  273                 # sometimes a different exception or bytes, which would raise
  274                 # TypeError.
  275                 message = _format_with_unicode_kwargs(message, kwargs)
  276             return _('%(message)s %(amendment)s') % {
  277                 'message': message,
  278                 'amendment': self.amendment}
  279 
  280         return _format_with_unicode_kwargs(self.message_format, kwargs)
  281 
  282 
  283 class Unauthorized(SecurityError):
  284     message_format = _("The request you have made requires authentication.")
  285     code = int(http.client.UNAUTHORIZED)
  286     title = http.client.responses[http.client.UNAUTHORIZED]
  287 
  288 
  289 class InsufficientAuthMethods(Error):
  290     # NOTE(adriant): This is an internal only error that is built into
  291     # an auth receipt response.
  292     message_format = _("Insufficient auth methods received for %(user_id)s. "
  293                        "Auth Methods Provided: %(methods)s.")
  294     code = 401
  295     title = 'Unauthorized'
  296 
  297     def __init__(self, message=None, user_id=None, methods=None):
  298         methods_str = '[%s]' % ','.join(methods)
  299         super(InsufficientAuthMethods, self).__init__(
  300             message, user_id=user_id, methods=methods_str)
  301 
  302         self.user_id = user_id
  303         self.methods = methods
  304 
  305 
  306 class ReceiptNotFound(Unauthorized):
  307     message_format = _("Could not find auth receipt: %(receipt_id)s.")
  308 
  309 
  310 class PasswordExpired(Unauthorized):
  311     message_format = _("The password is expired and needs to be changed for "
  312                        "user: %(user_id)s.")
  313 
  314 
  315 class AuthPluginException(Unauthorized):
  316     message_format = _("Authentication plugin error.")
  317 
  318     def __init__(self, *args, **kwargs):
  319         super(AuthPluginException, self).__init__(*args, **kwargs)
  320         self.authentication = {}
  321 
  322 
  323 class UserDisabled(Unauthorized):
  324     message_format = _("The account is disabled for user: %(user_id)s.")
  325 
  326 
  327 class AccountLocked(Unauthorized):
  328     message_format = _("The account is locked for user: %(user_id)s.")
  329 
  330 
  331 class AuthMethodNotSupported(AuthPluginException):
  332     message_format = _("Attempted to authenticate with an unsupported method.")
  333 
  334     def __init__(self, *args, **kwargs):
  335         super(AuthMethodNotSupported, self).__init__(*args, **kwargs)
  336         self.authentication = {'methods': CONF.auth.methods}
  337 
  338 
  339 class ApplicationCredentialAuthError(AuthPluginException):
  340     message_format = _(
  341         "Error authenticating with application credential: %(detail)s")
  342 
  343 
  344 class AdditionalAuthRequired(AuthPluginException):
  345     message_format = _("Additional authentications steps required.")
  346 
  347     def __init__(self, auth_response=None, **kwargs):
  348         super(AdditionalAuthRequired, self).__init__(message=None, **kwargs)
  349         self.authentication = auth_response
  350 
  351 
  352 class Forbidden(SecurityError):
  353     message_format = _("You are not authorized to perform the"
  354                        " requested action.")
  355     code = int(http.client.FORBIDDEN)
  356     title = http.client.responses[http.client.FORBIDDEN]
  357 
  358 
  359 class ForbiddenAction(Forbidden):
  360     message_format = _("You are not authorized to perform the"
  361                        " requested action: %(action)s.")
  362 
  363 
  364 class CrossBackendNotAllowed(Forbidden):
  365     message_format = _("Group membership across backend boundaries is not "
  366                        "allowed. Group in question is %(group_id)s, "
  367                        "user is %(user_id)s.")
  368 
  369 
  370 class InvalidPolicyAssociation(Forbidden):
  371     message_format = _("Invalid mix of entities for policy association: "
  372                        "only Endpoint, Service, or Region+Service allowed. "
  373                        "Request was - Endpoint: %(endpoint_id)s, "
  374                        "Service: %(service_id)s, Region: %(region_id)s.")
  375 
  376 
  377 class InvalidDomainConfig(Forbidden):
  378     message_format = _("Invalid domain specific configuration: %(reason)s.")
  379 
  380 
  381 class InvalidLimit(Forbidden):
  382     message_format = _("Invalid resource limit: %(reason)s.")
  383 
  384 
  385 class LimitTreeExceedError(Exception):
  386     def __init__(self, project_id, max_limit_depth):
  387         super(LimitTreeExceedError, self).__init__(_(
  388             "Keystone cannot start due to project hierarchical depth in the "
  389             "current deployment (project_ids: %(project_id)s) exceeds the "
  390             "enforcement model's maximum limit of %(max_limit_depth)s. Please "
  391             "use a different enforcement model to correct the issue."
  392         ) % {'project_id': project_id, 'max_limit_depth': max_limit_depth})
  393 
  394 
  395 class NotFound(Error):
  396     message_format = _("Could not find: %(target)s.")
  397     code = int(http.client.NOT_FOUND)
  398     title = http.client.responses[http.client.NOT_FOUND]
  399 
  400 
  401 class EndpointNotFound(NotFound):
  402     message_format = _("Could not find endpoint: %(endpoint_id)s.")
  403 
  404 
  405 class PolicyNotFound(NotFound):
  406     message_format = _("Could not find policy: %(policy_id)s.")
  407 
  408 
  409 class PolicyAssociationNotFound(NotFound):
  410     message_format = _("Could not find policy association.")
  411 
  412 
  413 class RoleNotFound(NotFound):
  414     message_format = _("Could not find role: %(role_id)s.")
  415 
  416 
  417 class ImpliedRoleNotFound(NotFound):
  418     message_format = _("%(prior_role_id)s does not imply %(implied_role_id)s.")
  419 
  420 
  421 class InvalidImpliedRole(Forbidden):
  422     message_format = _("%(role_id)s cannot be an implied roles.")
  423 
  424 
  425 class DomainSpecificRoleMismatch(Forbidden):
  426     message_format = _("Project %(project_id)s must be in the same domain "
  427                        "as the role %(role_id)s being assigned.")
  428 
  429 
  430 class DomainSpecificRoleNotWithinIdPDomain(Forbidden):
  431     message_format = _("role: %(role_name)s must be within the same domain as "
  432                        "the identity provider: %(identity_provider)s.")
  433 
  434 
  435 class DomainIdInvalid(ValidationError):
  436     message_format = _("Domain ID does not conform to required UUID format.")
  437 
  438 
  439 class RoleAssignmentNotFound(NotFound):
  440     message_format = _("Could not find role assignment with role: "
  441                        "%(role_id)s, user or group: %(actor_id)s, "
  442                        "project, domain, or system: %(target_id)s.")
  443 
  444 
  445 class RegionNotFound(NotFound):
  446     message_format = _("Could not find region: %(region_id)s.")
  447 
  448 
  449 class ServiceNotFound(NotFound):
  450     message_format = _("Could not find service: %(service_id)s.")
  451 
  452 
  453 class DomainNotFound(NotFound):
  454     message_format = _("Could not find domain: %(domain_id)s.")
  455 
  456 
  457 class ProjectNotFound(NotFound):
  458     message_format = _("Could not find project: %(project_id)s.")
  459 
  460 
  461 class ProjectTagNotFound(NotFound):
  462     message_format = _("Could not find project tag: %(project_tag)s.")
  463 
  464 
  465 class TokenNotFound(NotFound):
  466     message_format = _("Could not find token: %(token_id)s.")
  467 
  468 
  469 class UserNotFound(NotFound):
  470     message_format = _("Could not find user: %(user_id)s.")
  471 
  472 
  473 class GroupNotFound(NotFound):
  474     message_format = _("Could not find group: %(group_id)s.")
  475 
  476 
  477 class MappingNotFound(NotFound):
  478     message_format = _("Could not find mapping: %(mapping_id)s.")
  479 
  480 
  481 class TrustNotFound(NotFound):
  482     message_format = _("Could not find trust: %(trust_id)s.")
  483 
  484 
  485 class TrustUseLimitReached(Forbidden):
  486     message_format = _("No remaining uses for trust: %(trust_id)s.")
  487 
  488 
  489 class CredentialNotFound(NotFound):
  490     message_format = _("Could not find credential: %(credential_id)s.")
  491 
  492 
  493 class VersionNotFound(NotFound):
  494     message_format = _("Could not find version: %(version)s.")
  495 
  496 
  497 class EndpointGroupNotFound(NotFound):
  498     message_format = _("Could not find Endpoint Group: %(endpoint_group_id)s.")
  499 
  500 
  501 class IdentityProviderNotFound(NotFound):
  502     message_format = _("Could not find Identity Provider: %(idp_id)s.")
  503 
  504 
  505 class ServiceProviderNotFound(NotFound):
  506     message_format = _("Could not find Service Provider: %(sp_id)s.")
  507 
  508 
  509 class FederatedProtocolNotFound(NotFound):
  510     message_format = _("Could not find federated protocol %(protocol_id)s for"
  511                        " Identity Provider: %(idp_id)s.")
  512 
  513 
  514 class PublicIDNotFound(NotFound):
  515     # This is used internally and mapped to either User/GroupNotFound or,
  516     # Assertion before the exception leaves Keystone.
  517     message_format = "%(id)s"
  518 
  519 
  520 class RegisteredLimitNotFound(NotFound):
  521     message_format = _("Could not find registered limit for %(id)s.")
  522 
  523 
  524 class LimitNotFound(NotFound):
  525     message_format = _("Could not find limit for %(id)s.")
  526 
  527 
  528 class NoLimitReference(Forbidden):
  529     message_format = _("Unable to create a limit that has no corresponding "
  530                        "registered limit.")
  531 
  532 
  533 class RegisteredLimitError(ForbiddenNotSecurity):
  534     message_format = _("Unable to update or delete registered limit %(id)s "
  535                        "because there are project limits associated with it.")
  536 
  537 
  538 class DomainConfigNotFound(NotFound):
  539     message_format = _('Could not find %(group_or_option)s in domain '
  540                        'configuration for domain %(domain_id)s.')
  541 
  542 
  543 class ConfigRegistrationNotFound(Exception):
  544     # This is used internally between the domain config backend and the
  545     # manager, so should not escape to the client.  If it did, it is a coding
  546     # error on our part, and would end up, appropriately, as a 500 error.
  547     pass
  548 
  549 
  550 class ApplicationCredentialNotFound(NotFound):
  551     message_format = _("Could not find Application Credential: "
  552                        "%(application_credential_id)s.")
  553 
  554 
  555 class AccessRuleNotFound(NotFound):
  556     message_format = _("Could not find Access Rule: %(access_rule_id)s.")
  557 
  558 
  559 class Conflict(Error):
  560     message_format = _("Conflict occurred attempting to store %(type)s -"
  561                        " %(details)s.")
  562     code = int(http.client.CONFLICT)
  563     title = http.client.responses[http.client.CONFLICT]
  564 
  565 
  566 class UnexpectedError(SecurityError):
  567     """Avoids exposing details of failures, unless in insecure_debug mode."""
  568 
  569     message_format = _("An unexpected error prevented the server "
  570                        "from fulfilling your request.")
  571 
  572     debug_message_format = _("An unexpected error prevented the server "
  573                              "from fulfilling your request: %(exception)s.")
  574 
  575     def _build_message(self, message, **kwargs):
  576 
  577         # Ensure that exception has a value to be extra defensive for
  578         # substitutions and make sure the exception doesn't raise an
  579         # exception.
  580         kwargs.setdefault('exception', '')
  581 
  582         return super(UnexpectedError, self)._build_message(
  583             message or self.debug_message_format, **kwargs)
  584 
  585     code = int(http.client.INTERNAL_SERVER_ERROR)
  586     title = http.client.responses[http.client.INTERNAL_SERVER_ERROR]
  587 
  588 
  589 class TrustConsumeMaximumAttempt(UnexpectedError):
  590     debug_message_format = _("Unable to consume trust %(trust_id)s. Unable to "
  591                              "acquire lock.")
  592 
  593 
  594 class MalformedEndpoint(UnexpectedError):
  595     debug_message_format = _("Malformed endpoint URL (%(endpoint)s),"
  596                              " see ERROR log for details.")
  597 
  598 
  599 class MappedGroupNotFound(UnexpectedError):
  600     debug_message_format = _("Group %(group_id)s returned by mapping "
  601                              "%(mapping_id)s was not found in the backend.")
  602 
  603 
  604 class MetadataFileError(UnexpectedError):
  605     debug_message_format = _("Error while reading metadata file: %(reason)s.")
  606 
  607 
  608 class DirectMappingError(UnexpectedError):
  609     debug_message_format = _("Local section in mapping %(mapping_id)s refers "
  610                              "to a remote match that doesn't exist "
  611                              "(e.g. {0} in a local section).")
  612 
  613 
  614 class AssignmentTypeCalculationError(UnexpectedError):
  615     debug_message_format = _(
  616         'Unexpected combination of grant attributes - '
  617         'User: %(user_id)s, Group: %(group_id)s, Project: %(project_id)s, '
  618         'Domain: %(domain_id)s.')
  619 
  620 
  621 class NotImplemented(Error):
  622     message_format = _("The action you have requested has not"
  623                        " been implemented.")
  624     code = int(http.client.NOT_IMPLEMENTED)
  625     title = http.client.responses[http.client.NOT_IMPLEMENTED]
  626 
  627 
  628 class Gone(Error):
  629     message_format = _("The service you have requested is no"
  630                        " longer available on this server.")
  631     code = int(http.client.GONE)
  632     title = http.client.responses[http.client.GONE]
  633 
  634 
  635 class ConfigFileNotFound(UnexpectedError):
  636     debug_message_format = _("The Keystone configuration file %(config_file)s "
  637                              "could not be found.")
  638 
  639 
  640 class KeysNotFound(UnexpectedError):
  641     debug_message_format = _('No encryption keys found; run keystone-manage '
  642                              'fernet_setup to bootstrap one.')
  643 
  644 
  645 class MultipleSQLDriversInConfig(UnexpectedError):
  646     debug_message_format = _('The Keystone domain-specific configuration has '
  647                              'specified more than one SQL driver (only one is '
  648                              'permitted): %(source)s.')
  649 
  650 
  651 class MigrationNotProvided(Exception):
  652     def __init__(self, mod_name, path):
  653         super(MigrationNotProvided, self).__init__(_(
  654             "%(mod_name)s doesn't provide database migrations. The migration"
  655             " repository path at %(path)s doesn't exist or isn't a directory."
  656         ) % {'mod_name': mod_name, 'path': path})
  657 
  658 
  659 class UnsupportedTokenVersionException(UnexpectedError):
  660     debug_message_format = _('Token version is unrecognizable or '
  661                              'unsupported.')
  662 
  663 
  664 class SAMLSigningError(UnexpectedError):
  665     debug_message_format = _('Unable to sign SAML assertion. It is likely '
  666                              'that this server does not have xmlsec1 '
  667                              'installed or this is the result of '
  668                              'misconfiguration. Reason %(reason)s.')
  669 
  670 
  671 class OAuthHeadersMissingError(UnexpectedError):
  672     debug_message_format = _('No Authorization headers found, cannot proceed '
  673                              'with OAuth related calls. If running under '
  674                              'HTTPd or Apache, ensure WSGIPassAuthorization '
  675                              'is set to On.')
  676 
  677 
  678 class TokenlessAuthConfigError(ValidationError):
  679     message_format = _('Could not determine Identity Provider ID. The '
  680                        'configuration option %(issuer_attribute)s '
  681                        'was not found in the request environment.')
  682 
  683 
  684 class CredentialEncryptionError(Exception):
  685     message_format = _("An unexpected error prevented the server "
  686                        "from accessing encrypted credentials.")
  687 
  688 
  689 class LDAPServerConnectionError(UnexpectedError):
  690     debug_message_format = _('Unable to establish a connection to '
  691                              'LDAP Server (%(url)s).')
  692 
  693 
  694 class LDAPInvalidCredentialsError(UnexpectedError):
  695     message_format = _('Unable to authenticate against Identity backend - '
  696                        'Invalid username or password')
  697 
  698 
  699 class LDAPSizeLimitExceeded(UnexpectedError):
  700     message_format = _('Number of User/Group entities returned by LDAP '
  701                        'exceeded size limit. Contact your LDAP '
  702                        'administrator.')
  703 
  704 
  705 class CacheDeserializationError(Exception):
  706 
  707     def __init__(self, obj, data):
  708         super(CacheDeserializationError, self).__init__(
  709             _('Failed to deserialize %(obj)s. Data is %(data)s') % {
  710                 'obj': obj, 'data': data
  711             }
  712         )
  713 
  714 
  715 class ResourceUpdateForbidden(ForbiddenNotSecurity):
  716     message_format = _('Unable to update immutable %(type)s resource: '
  717                        '`%(resource_id)s. Set resource option "immutable" '
  718                        'to false first.')
  719 
  720 
  721 class ResourceDeleteForbidden(ForbiddenNotSecurity):
  722     message_format = _('Unable to delete immutable %(type)s resource: '
  723                        '`%(resource_id)s. Set resource option "immutable" '
  724                        'to false first.')