Introduce validation errors, authentication errors, corresponding http codes

This commit is contained in:
Alexandre Aubin 2021-03-12 03:55:52 +01:00
parent f790bde101
commit bc1bdbb247
6 changed files with 36 additions and 24 deletions

View file

@ -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):
"""

View file

@ -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

View file

@ -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"))

View file

@ -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"""

View file

@ -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?

View file

@ -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