diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index d4a5f079..64d00bd7 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -14,7 +14,7 @@ from importlib import import_module from moulinette import m18n, msignals from moulinette.cache import open_cachefile from moulinette.globals import init_moulinette_env -from moulinette.core import MoulinetteError, MoulinetteLock +from moulinette.core import MoulinetteError, MoulinetteLock, MoulinetteAuthenticationError, MoulinetteValidationError from moulinette.interfaces import BaseActionsMapParser, GLOBAL_SECTION, TO_RETURN_PROP from moulinette.utils.log import start_action_logging @@ -207,7 +207,7 @@ class PatternParameter(_ExtraParameter): if msg == message: msg = m18n.g(message) - raise MoulinetteError("invalid_argument", argument=arg_name, error=msg) + raise MoulinetteValidationError("invalid_argument", argument=arg_name, error=msg) return arg_value @staticmethod @@ -238,7 +238,7 @@ class RequiredParameter(_ExtraParameter): def __call__(self, required, arg_name, arg_value): if required and (arg_value is None or arg_value == ""): logger.warning("argument '%s' is required", arg_name) - raise MoulinetteError("argument_required", argument=arg_name) + raise MoulinetteValidationError("argument_required", argument=arg_name) return arg_value @staticmethod @@ -497,7 +497,7 @@ class ActionsMap(object): auth = msignals.authenticate(authenticator) if not auth.is_authenticated: - raise MoulinetteError("authentication_required_long") + raise MoulinetteAuthenticationError("authentication_required_long") def process(self, args, timeout=None, **kwargs): """ diff --git a/moulinette/authenticators/__init__.py b/moulinette/authenticators/__init__.py index 0170d345..e004db0a 100644 --- a/moulinette/authenticators/__init__.py +++ b/moulinette/authenticators/__init__.py @@ -6,7 +6,7 @@ import hashlib import hmac from moulinette.cache import open_cachefile, get_cachedir, cachefile_exists -from moulinette.core import MoulinetteError +from moulinette.core import MoulinetteError, MoulinetteAuthenticationError logger = logging.getLogger("moulinette.authenticator") @@ -105,7 +105,7 @@ class BaseAuthenticator(object): self.vendor, e, ) - raise MoulinetteError("unable_authenticate") + raise MoulinetteAuthenticationError("unable_authenticate") self.is_authenticated = True @@ -139,7 +139,7 @@ class BaseAuthenticator(object): self.vendor, e, ) - raise MoulinetteError("unable_authenticate") + raise MoulinetteAuthenticationError("unable_authenticate") else: self.is_authenticated = True @@ -147,7 +147,7 @@ class BaseAuthenticator(object): # No credentials given, can't authenticate # else: - raise MoulinetteError("unable_authenticate") + raise MoulinetteAuthenticationError("unable_authenticate") return self @@ -175,7 +175,7 @@ class BaseAuthenticator(object): def _authenticate_session(self, session_id, session_token): """Checks session and token against the stored session token""" if not self._session_exists(session_id): - raise MoulinetteError("session_expired") + raise MoulinetteAuthenticationError("session_expired") try: # FIXME : shouldn't we also add a check that this session file # is not too old ? e.g. not older than 24 hours ? idk... @@ -184,7 +184,7 @@ class BaseAuthenticator(object): stored_hash = f.read() except IOError as e: logger.debug("unable to retrieve session", exc_info=1) - raise MoulinetteError("unable_retrieve_session", exception=e) + raise MoulinetteAuthenticationError("unable_retrieve_session", exception=e) else: # # session_id (or just id) : This is unique id for the current session from the user. Not too important @@ -206,7 +206,7 @@ class BaseAuthenticator(object): hash_ = hashlib.sha256(to_hash).hexdigest() if not hmac.compare_digest(hash_, stored_hash): - raise MoulinetteError("invalid_token") + raise MoulinetteAuthenticationError("invalid_token") else: return diff --git a/moulinette/authenticators/ldap.py b/moulinette/authenticators/ldap.py index 13c635a3..93cf0717 100644 --- a/moulinette/authenticators/ldap.py +++ b/moulinette/authenticators/ldap.py @@ -10,7 +10,7 @@ import time import ldap.modlist as modlist from moulinette import m18n -from moulinette.core import MoulinetteError, MoulinetteLdapIsDownError +from moulinette.core import MoulinetteError, MoulinetteAuthenticationError, MoulinetteLdapIsDownError from moulinette.authenticators import BaseAuthenticator logger = logging.getLogger("moulinette.authenticator.ldap") @@ -86,7 +86,7 @@ class Authenticator(BaseAuthenticator): try: con = _reconnect() except ldap.INVALID_CREDENTIALS: - raise MoulinetteError("invalid_password") + raise MoulinetteAuthenticationError("invalid_password") except ldap.SERVER_DOWN: # ldap is down, attempt to restart it before really failing logger.warning(m18n.g("ldap_server_is_down_restart_it")) diff --git a/moulinette/core.py b/moulinette/core.py index c41b7d21..800b8540 100644 --- a/moulinette/core.py +++ b/moulinette/core.py @@ -382,6 +382,8 @@ class MoulinetteSignals(object): class MoulinetteError(Exception): + http_code = 500 + """Moulinette base exception""" def __init__(self, key, raw_msg=False, *args, **kwargs): @@ -396,6 +398,16 @@ class MoulinetteError(Exception): return self.strerror +class MoulinetteValidationError(MoulinetteError): + + http_code = 400 + + +class MoulinetteAuthenticationError(MoulinetteError): + + http_code = 401 + + class MoulinetteLdapIsDownError(MoulinetteError): """Used when ldap is down""" diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 33734f14..0b333e90 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -15,7 +15,7 @@ from bottle import abort from moulinette import msignals, m18n, env from moulinette.actionsmap import ActionsMap -from moulinette.core import MoulinetteError +from moulinette.core import MoulinetteError, MoulinetteValidationError from moulinette.interfaces import ( BaseActionsMapParser, BaseInterface, @@ -546,17 +546,17 @@ class _ActionsMapPlugin(object): # HTTP Responses ------------------------------------------------------- -def moulinette_error_to_http_response(self): +def moulinette_error_to_http_response(error): content = error.content() if isinstance(content, dict): return HTTPResponse( json_encode(content), - 400, + error.http_code, headers={"Content-type": "application/json"}, ) else: - return HTTPResponse(content, 400) + return HTTPResponse(content, error.http_code) def format_for_response(content): @@ -673,7 +673,7 @@ class ActionsMapParser(BaseActionsMapParser): e, ) logger.error(error_message) - raise MoulinetteError(error_message, raw_msg=True) + raise MoulinetteValidationError(error_message, raw_msg=True) if self.get_conf(tid, "authenticate"): authenticator = self.get_conf(tid, "authenticator") @@ -702,7 +702,7 @@ class ActionsMapParser(BaseActionsMapParser): except KeyError as e: error_message = "no argument parser found for route '%s': %s" % (route, e) logger.error(error_message) - raise MoulinetteError(error_message, raw_msg=True) + raise MoulinetteValidationError(error_message, raw_msg=True) ret = argparse.Namespace() # TODO: Catch errors? diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index 19505365..f21cb499 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -13,7 +13,7 @@ import argcomplete from moulinette import msignals, m18n from moulinette.actionsmap import ActionsMap -from moulinette.core import MoulinetteError +from moulinette.core import MoulinetteError, MoulinetteValidationError from moulinette.interfaces import ( BaseActionsMapParser, BaseInterface, @@ -411,7 +411,7 @@ class ActionsMapParser(BaseActionsMapParser): e, ) logger.exception(error_message) - raise MoulinetteError(error_message, raw_msg=True) + raise MoulinetteValidationError(error_message, raw_msg=True) tid = getattr(ret, "_tid", None) if self.get_conf(tid, "authenticate"): @@ -439,7 +439,7 @@ class ActionsMapParser(BaseActionsMapParser): e, ) logger.exception(error_message) - raise MoulinetteError(error_message, raw_msg=True) + raise MoulinetteValidationError(error_message, raw_msg=True) else: self.prepare_action_namespace(getattr(ret, "_tid", None), ret) self._parser.dequeue_callbacks(ret) @@ -490,7 +490,7 @@ class Interface(BaseInterface): """ if output_as and output_as not in ["json", "plain", "none"]: - raise MoulinetteError("invalid_usage") + raise MoulinetteValidationError("invalid_usage") # auto-complete argcomplete.autocomplete(self.actionsmap.parser._parser) @@ -555,7 +555,7 @@ class Interface(BaseInterface): if confirm: m = message[0].lower() + message[1:] if prompt(m18n.g("confirm", prompt=m)) != value: - raise MoulinetteError("values_mismatch") + raise MoulinetteValidationError("values_mismatch") return value