"Fossies" - the Fresh Open Source Software Archive

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


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

    1 # 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 SecurityError(Error):
  226     """Security error exception.
  227 
  228     Avoids exposing details of security errors, unless in insecure_debug mode.
  229 
  230     """
  231 
  232     amendment = _('(Disable insecure_debug mode to suppress these details.)')
  233 
  234     def __deepcopy__(self):
  235         """Override the default deepcopy.
  236 
  237         Keystone :class:`keystone.exception.Error` accepts an optional message
  238         that will be used when rendering the exception object as a string. If
  239         not provided the object's message_format attribute is used instead.
  240         :class:`keystone.exception.SecurityError` is a little different in
  241         that it only uses the message provided to the initializer when
  242         keystone is in `insecure_debug` mode. Instead it will use its
  243         `message_format`. This is to ensure that sensitive details are not
  244         leaked back to the caller in a production deployment.
  245 
  246         This dual mode for string rendering causes some odd behaviour when
  247         combined with oslo_i18n translation. Any object used as a value for
  248         formatting a translated string is deep copied.
  249 
  250         The copy causes an issue. The deep copy process actually creates a new
  251         exception instance with the rendered string. Then when that new
  252         instance is rendered as a string to use for substitution a warning is
  253         logged. This is because the code tries to use the `message_format` in
  254         secure mode, but the required kwargs are not in the deep copy.
  255 
  256         The end result is not an error because when the KeyError is caught the
  257         instance's ``message`` is used instead and this has the properly
  258         translated message. The only indication that something is wonky is a
  259         message in the warning log.
  260         """
  261         return self
  262 
  263     def _build_message(self, message, **kwargs):
  264         """Only returns detailed messages in insecure_debug mode."""
  265         if message and CONF.insecure_debug:
  266             if isinstance(message, str):
  267                 # Only do replacement if message is string. The message is
  268                 # sometimes a different exception or bytes, which would raise
  269                 # TypeError.
  270                 message = _format_with_unicode_kwargs(message, kwargs)
  271             return _('%(message)s %(amendment)s') % {
  272                 'message': message,
  273                 'amendment': self.amendment}
  274 
  275         return _format_with_unicode_kwargs(self.message_format, kwargs)
  276 
  277 
  278 class Unauthorized(SecurityError):
  279     message_format = _("The request you have made requires authentication.")
  280     code = int(http.client.UNAUTHORIZED)
  281     title = http.client.responses[http.client.UNAUTHORIZED]
  282 
  283 
  284 class InsufficientAuthMethods(Error):
  285     # NOTE(adriant): This is an internal only error that is built into
  286     # an auth receipt response.
  287     message_format = _("Insufficient auth methods received for %(user_id)s. "
  288                        "Auth Methods Provided: %(methods)s.")
  289     code = 401
  290     title = 'Unauthorized'
  291 
  292     def __init__(self, message=None, user_id=None, methods=None):
  293         methods_str = '[%s]' % ','.join(methods)
  294         super(InsufficientAuthMethods, self).__init__(
  295             message, user_id=user_id, methods=methods_str)
  296 
  297         self.user_id = user_id
  298         self.methods = methods
  299 
  300 
  301 class ReceiptNotFound(Unauthorized):
  302     message_format = _("Could not find auth receipt: %(receipt_id)s.")
  303 
  304 
  305 class PasswordExpired(Unauthorized):
  306     message_format = _("The password is expired and needs to be changed for "
  307                        "user: %(user_id)s.")
  308 
  309 
  310 class AuthPluginException(Unauthorized):
  311     message_format = _("Authentication plugin error.")
  312 
  313     def __init__(self, *args, **kwargs):
  314         super(AuthPluginException, self).__init__(*args, **kwargs)
  315         self.authentication = {}
  316 
  317 
  318 class UserDisabled(Unauthorized):
  319     message_format = _("The account is disabled for user: %(user_id)s.")
  320 
  321 
  322 class AccountLocked(Unauthorized):
  323     message_format = _("The account is locked for user: %(user_id)s.")
  324 
  325 
  326 class AuthMethodNotSupported(AuthPluginException):
  327     message_format = _("Attempted to authenticate with an unsupported method.")
  328 
  329     def __init__(self, *args, **kwargs):
  330         super(AuthMethodNotSupported, self).__init__(*args, **kwargs)
  331         self.authentication = {'methods': CONF.auth.methods}
  332 
  333 
  334 class ApplicationCredentialAuthError(AuthPluginException):
  335     message_format = _(
  336         "Error authenticating with application credential: %(detail)s")
  337 
  338 
  339 class AdditionalAuthRequired(AuthPluginException):
  340     message_format = _("Additional authentications steps required.")
  341 
  342     def __init__(self, auth_response=None, **kwargs):
  343         super(AdditionalAuthRequired, self).__init__(message=None, **kwargs)
  344         self.authentication = auth_response
  345 
  346 
  347 class Forbidden(SecurityError):
  348     message_format = _("You are not authorized to perform the"
  349                        " requested action.")
  350     code = int(http.client.FORBIDDEN)
  351     title = http.client.responses[http.client.FORBIDDEN]
  352 
  353 
  354 class ForbiddenAction(Forbidden):
  355     message_format = _("You are not authorized to perform the"
  356                        " requested action: %(action)s.")
  357 
  358 
  359 class CrossBackendNotAllowed(Forbidden):
  360     message_format = _("Group membership across backend boundaries is not "
  361                        "allowed. Group in question is %(group_id)s, "
  362                        "user is %(user_id)s.")
  363 
  364 
  365 class InvalidPolicyAssociation(Forbidden):
  366     message_format = _("Invalid mix of entities for policy association: "
  367                        "only Endpoint, Service, or Region+Service allowed. "
  368                        "Request was - Endpoint: %(endpoint_id)s, "
  369                        "Service: %(service_id)s, Region: %(region_id)s.")
  370 
  371 
  372 class InvalidDomainConfig(Forbidden):
  373     message_format = _("Invalid domain specific configuration: %(reason)s.")
  374 
  375 
  376 class InvalidLimit(Forbidden):
  377     message_format = _("Invalid resource limit: %(reason)s.")
  378 
  379 
  380 class LimitTreeExceedError(Exception):
  381     def __init__(self, project_id, max_limit_depth):
  382         super(LimitTreeExceedError, self).__init__(_(
  383             "Keystone cannot start due to project hierarchical depth in the "
  384             "current deployment (project_ids: %(project_id)s) exceeds the "
  385             "enforcement model's maximum limit of %(max_limit_depth)s. Please "
  386             "use a different enforcement model to correct the issue."
  387         ) % {'project_id': project_id, 'max_limit_depth': max_limit_depth})
  388 
  389 
  390 class NotFound(Error):
  391     message_format = _("Could not find: %(target)s.")
  392     code = int(http.client.NOT_FOUND)
  393     title = http.client.responses[http.client.NOT_FOUND]
  394 
  395 
  396 class EndpointNotFound(NotFound):
  397     message_format = _("Could not find endpoint: %(endpoint_id)s.")
  398 
  399 
  400 class PolicyNotFound(NotFound):
  401     message_format = _("Could not find policy: %(policy_id)s.")
  402 
  403 
  404 class PolicyAssociationNotFound(NotFound):
  405     message_format = _("Could not find policy association.")
  406 
  407 
  408 class RoleNotFound(NotFound):
  409     message_format = _("Could not find role: %(role_id)s.")
  410 
  411 
  412 class ImpliedRoleNotFound(NotFound):
  413     message_format = _("%(prior_role_id)s does not imply %(implied_role_id)s.")
  414 
  415 
  416 class InvalidImpliedRole(Forbidden):
  417     message_format = _("%(role_id)s cannot be an implied roles.")
  418 
  419 
  420 class DomainSpecificRoleMismatch(Forbidden):
  421     message_format = _("Project %(project_id)s must be in the same domain "
  422                        "as the role %(role_id)s being assigned.")
  423 
  424 
  425 class DomainSpecificRoleNotWithinIdPDomain(Forbidden):
  426     message_format = _("role: %(role_name)s must be within the same domain as "
  427                        "the identity provider: %(identity_provider)s.")
  428 
  429 
  430 class DomainIdInvalid(ValidationError):
  431     message_format = _("Domain ID does not conform to required UUID format.")
  432 
  433 
  434 class RoleAssignmentNotFound(NotFound):
  435     message_format = _("Could not find role assignment with role: "
  436                        "%(role_id)s, user or group: %(actor_id)s, "
  437                        "project, domain, or system: %(target_id)s.")
  438 
  439 
  440 class RegionNotFound(NotFound):
  441     message_format = _("Could not find region: %(region_id)s.")
  442 
  443 
  444 class ServiceNotFound(NotFound):
  445     message_format = _("Could not find service: %(service_id)s.")
  446 
  447 
  448 class DomainNotFound(NotFound):
  449     message_format = _("Could not find domain: %(domain_id)s.")
  450 
  451 
  452 class ProjectNotFound(NotFound):
  453     message_format = _("Could not find project: %(project_id)s.")
  454 
  455 
  456 class ProjectTagNotFound(NotFound):
  457     message_format = _("Could not find project tag: %(project_tag)s.")
  458 
  459 
  460 class TokenNotFound(NotFound):
  461     message_format = _("Could not find token: %(token_id)s.")
  462 
  463 
  464 class UserNotFound(NotFound):
  465     message_format = _("Could not find user: %(user_id)s.")
  466 
  467 
  468 class GroupNotFound(NotFound):
  469     message_format = _("Could not find group: %(group_id)s.")
  470 
  471 
  472 class MappingNotFound(NotFound):
  473     message_format = _("Could not find mapping: %(mapping_id)s.")
  474 
  475 
  476 class TrustNotFound(NotFound):
  477     message_format = _("Could not find trust: %(trust_id)s.")
  478 
  479 
  480 class TrustUseLimitReached(Forbidden):
  481     message_format = _("No remaining uses for trust: %(trust_id)s.")
  482 
  483 
  484 class CredentialNotFound(NotFound):
  485     message_format = _("Could not find credential: %(credential_id)s.")
  486 
  487 
  488 class VersionNotFound(NotFound):
  489     message_format = _("Could not find version: %(version)s.")
  490 
  491 
  492 class EndpointGroupNotFound(NotFound):
  493     message_format = _("Could not find Endpoint Group: %(endpoint_group_id)s.")
  494 
  495 
  496 class IdentityProviderNotFound(NotFound):
  497     message_format = _("Could not find Identity Provider: %(idp_id)s.")
  498 
  499 
  500 class ServiceProviderNotFound(NotFound):
  501     message_format = _("Could not find Service Provider: %(sp_id)s.")
  502 
  503 
  504 class FederatedProtocolNotFound(NotFound):
  505     message_format = _("Could not find federated protocol %(protocol_id)s for"
  506                        " Identity Provider: %(idp_id)s.")
  507 
  508 
  509 class PublicIDNotFound(NotFound):
  510     # This is used internally and mapped to either User/GroupNotFound or,
  511     # Assertion before the exception leaves Keystone.
  512     message_format = "%(id)s"
  513 
  514 
  515 class RegisteredLimitNotFound(NotFound):
  516     message_format = _("Could not find registered limit for %(id)s.")
  517 
  518 
  519 class LimitNotFound(NotFound):
  520     message_format = _("Could not find limit for %(id)s.")
  521 
  522 
  523 class NoLimitReference(Forbidden):
  524     message_format = _("Unable to create a limit that has no corresponding "
  525                        "registered limit.")
  526 
  527 
  528 class RegisteredLimitError(ForbiddenNotSecurity):
  529     message_format = _("Unable to update or delete registered limit %(id)s "
  530                        "because there are project limits associated with it.")
  531 
  532 
  533 class DomainConfigNotFound(NotFound):
  534     message_format = _('Could not find %(group_or_option)s in domain '
  535                        'configuration for domain %(domain_id)s.')
  536 
  537 
  538 class ConfigRegistrationNotFound(Exception):
  539     # This is used internally between the domain config backend and the
  540     # manager, so should not escape to the client.  If it did, it is a coding
  541     # error on our part, and would end up, appropriately, as a 500 error.
  542     pass
  543 
  544 
  545 class ApplicationCredentialNotFound(NotFound):
  546     message_format = _("Could not find Application Credential: "
  547                        "%(application_credential_id)s.")
  548 
  549 
  550 class AccessRuleNotFound(NotFound):
  551     message_format = _("Could not find Access Rule: %(access_rule_id)s.")
  552 
  553 
  554 class Conflict(Error):
  555     message_format = _("Conflict occurred attempting to store %(type)s -"
  556                        " %(details)s.")
  557     code = int(http.client.CONFLICT)
  558     title = http.client.responses[http.client.CONFLICT]
  559 
  560 
  561 class UnexpectedError(SecurityError):
  562     """Avoids exposing details of failures, unless in insecure_debug mode."""
  563 
  564     message_format = _("An unexpected error prevented the server "
  565                        "from fulfilling your request.")
  566 
  567     debug_message_format = _("An unexpected error prevented the server "
  568                              "from fulfilling your request: %(exception)s.")
  569 
  570     def _build_message(self, message, **kwargs):
  571 
  572         # Ensure that exception has a value to be extra defensive for
  573         # substitutions and make sure the exception doesn't raise an
  574         # exception.
  575         kwargs.setdefault('exception', '')
  576 
  577         return super(UnexpectedError, self)._build_message(
  578             message or self.debug_message_format, **kwargs)
  579 
  580     code = int(http.client.INTERNAL_SERVER_ERROR)
  581     title = http.client.responses[http.client.INTERNAL_SERVER_ERROR]
  582 
  583 
  584 class TrustConsumeMaximumAttempt(UnexpectedError):
  585     debug_message_format = _("Unable to consume trust %(trust_id)s. Unable to "
  586                              "acquire lock.")
  587 
  588 
  589 class MalformedEndpoint(UnexpectedError):
  590     debug_message_format = _("Malformed endpoint URL (%(endpoint)s),"
  591                              " see ERROR log for details.")
  592 
  593 
  594 class MappedGroupNotFound(UnexpectedError):
  595     debug_message_format = _("Group %(group_id)s returned by mapping "
  596                              "%(mapping_id)s was not found in the backend.")
  597 
  598 
  599 class MetadataFileError(UnexpectedError):
  600     debug_message_format = _("Error while reading metadata file: %(reason)s.")
  601 
  602 
  603 class DirectMappingError(UnexpectedError):
  604     debug_message_format = _("Local section in mapping %(mapping_id)s refers "
  605                              "to a remote match that doesn't exist "
  606                              "(e.g. {0} in a local section).")
  607 
  608 
  609 class AssignmentTypeCalculationError(UnexpectedError):
  610     debug_message_format = _(
  611         'Unexpected combination of grant attributes - '
  612         'User: %(user_id)s, Group: %(group_id)s, Project: %(project_id)s, '
  613         'Domain: %(domain_id)s.')
  614 
  615 
  616 class NotImplemented(Error):
  617     message_format = _("The action you have requested has not"
  618                        " been implemented.")
  619     code = int(http.client.NOT_IMPLEMENTED)
  620     title = http.client.responses[http.client.NOT_IMPLEMENTED]
  621 
  622 
  623 class Gone(Error):
  624     message_format = _("The service you have requested is no"
  625                        " longer available on this server.")
  626     code = int(http.client.GONE)
  627     title = http.client.responses[http.client.GONE]
  628 
  629 
  630 class ConfigFileNotFound(UnexpectedError):
  631     debug_message_format = _("The Keystone configuration file %(config_file)s "
  632                              "could not be found.")
  633 
  634 
  635 class KeysNotFound(UnexpectedError):
  636     debug_message_format = _('No encryption keys found; run keystone-manage '
  637                              'fernet_setup to bootstrap one.')
  638 
  639 
  640 class MultipleSQLDriversInConfig(UnexpectedError):
  641     debug_message_format = _('The Keystone domain-specific configuration has '
  642                              'specified more than one SQL driver (only one is '
  643                              'permitted): %(source)s.')
  644 
  645 
  646 class MigrationNotProvided(Exception):
  647     def __init__(self, mod_name, path):
  648         super(MigrationNotProvided, self).__init__(_(
  649             "%(mod_name)s doesn't provide database migrations. The migration"
  650             " repository path at %(path)s doesn't exist or isn't a directory."
  651         ) % {'mod_name': mod_name, 'path': path})
  652 
  653 
  654 class UnsupportedTokenVersionException(UnexpectedError):
  655     debug_message_format = _('Token version is unrecognizable or '
  656                              'unsupported.')
  657 
  658 
  659 class SAMLSigningError(UnexpectedError):
  660     debug_message_format = _('Unable to sign SAML assertion. It is likely '
  661                              'that this server does not have xmlsec1 '
  662                              'installed or this is the result of '
  663                              'misconfiguration. Reason %(reason)s.')
  664 
  665 
  666 class OAuthHeadersMissingError(UnexpectedError):
  667     debug_message_format = _('No Authorization headers found, cannot proceed '
  668                              'with OAuth related calls. If running under '
  669                              'HTTPd or Apache, ensure WSGIPassAuthorization '
  670                              'is set to On.')
  671 
  672 
  673 class TokenlessAuthConfigError(ValidationError):
  674     message_format = _('Could not determine Identity Provider ID. The '
  675                        'configuration option %(issuer_attribute)s '
  676                        'was not found in the request environment.')
  677 
  678 
  679 class CredentialEncryptionError(Exception):
  680     message_format = _("An unexpected error prevented the server "
  681                        "from accessing encrypted credentials.")
  682 
  683 
  684 class LDAPServerConnectionError(UnexpectedError):
  685     debug_message_format = _('Unable to establish a connection to '
  686                              'LDAP Server (%(url)s).')
  687 
  688 
  689 class LDAPInvalidCredentialsError(UnexpectedError):
  690     message_format = _('Unable to authenticate against Identity backend - '
  691                        'Invalid username or password')
  692 
  693 
  694 class LDAPSizeLimitExceeded(UnexpectedError):
  695     message_format = _('Number of User/Group entities returned by LDAP '
  696                        'exceeded size limit. Contact your LDAP '
  697                        'administrator.')
  698 
  699 
  700 class CacheDeserializationError(Exception):
  701 
  702     def __init__(self, obj, data):
  703         super(CacheDeserializationError, self).__init__(
  704             _('Failed to deserialize %(obj)s. Data is %(data)s') % {
  705                 'obj': obj, 'data': data
  706             }
  707         )
  708 
  709 
  710 class ResourceUpdateForbidden(ForbiddenNotSecurity):
  711     message_format = _('Unable to update immutable %(type)s resource: '
  712                        '`%(resource_id)s. Set resource option "immutable" '
  713                        'to false first.')
  714 
  715 
  716 class ResourceDeleteForbidden(ForbiddenNotSecurity):
  717     message_format = _('Unable to delete immutable %(type)s resource: '
  718                        '`%(resource_id)s. Set resource option "immutable" '
  719                        'to false first.')