Exemplo n.º 1
0
def authbasic(checkpassword, authmethod=""):

    """Filter used to restrict access to resource via HTTP basic auth."""

    # Check if logged-in.
    if cherrypy.session.get("username"):  # @UndefinedVariable
        # page passes credentials; allow to be processed
        return False

    # Proceed with basic authentication.
    request = cherrypy.serving.request
    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        try:
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)
                error_msg = checkpassword(username, password)
                if error_msg:
                    logger.info('basic auth fail for %s: %s' % (username, error_msg))
                else:
                    logger.info('basic auth succeeded for %s' % (username))
                    request.login = username
                    return  # successful authentication
        # split() error, base64.decodestring() error
        except (ValueError, binascii.Error):
            raise cherrypy.HTTPError(400, 'Bad Request')

    # Respond with 401 status and a WWW-Authenticate header
    cherrypy.serving.response.headers['www-authenticate'] = 'Basic realm="rdiffweb"'
    raise cherrypy.HTTPError(401, "You are not authorized to access that resource")
Exemplo n.º 2
0
def authbasic(checkpassword):
    """Filter used to restrict access to resource via HTTP basic auth."""

    # Check if logged-in.
    if cherrypy.session.get("user"):  # @UndefinedVariable
        # page passes credentials; allow to be processed
        return False

    # Proceed with basic authentication.
    request = cherrypy.serving.request
    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        try:
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)
                error_msg = checkpassword(username, password)
                if error_msg:
                    logger.info('basic auth fail for %s: %s', username,
                                error_msg)
                else:
                    logger.info('basic auth succeeded for %s', username)
                    request.login = username
                    return  # successful authentication
        # split() error, base64.decodestring() error
        except (ValueError, binascii.Error):
            raise cherrypy.HTTPError(400, 'Bad Request')

    # Respond with 401 status and a WWW-Authenticate header
    cherrypy.serving.response.headers[
        'www-authenticate'] = 'Basic realm="rdiffweb"'
    raise cherrypy.HTTPError(401,
                             "You are not authorized to access that resource")
Exemplo n.º 3
0
def decode_username_and_password():
    """
    decode the url from the json that was passed to us
    :return: decoded url and password as a tuple
    """
    try:
        # cherrypy.log.error("decoding username and password")
        user_name = base64_decode(cherrypy.request.json["user_name"])
        password = base64_decode(cherrypy.request.json["password"])

        # try and get the redirect path for after successful login
        try:
            location = cherrypy.request.json["location"]
        except Exception as e:
            location = None
            # cherrypy.log.error("no location provided moving on")
    except Exception as e:
        # cherrypy.log.error("username and password could not be decoded")
        slycat.email.send_error(
            "slycat-standard-authentication.py authenticate",
            "cherrypy.HTTPError 400")
        raise cherrypy.HTTPError(400)
    return user_name, password
Exemplo n.º 4
0
def basic_auth(realm, checkpassword, debug=False):
    """A CherryPy tool which hooks at before_handler to perform
    HTTP Basic Access Authentication, as specified in :rfc:`2617`.

    If the request has an 'authorization' header with a 'Basic' scheme, this
    tool attempts to authenticate the credentials supplied in that header.  If
    the request has no 'authorization' header, or if it does but the scheme is
    not 'Basic', or if authentication fails, the tool sends a 401 response with
    a 'WWW-Authenticate' Basic header.

    realm
        A string containing the authentication realm.

    checkpassword
        A callable which checks the authentication credentials.
        Its signature is checkpassword(realm, username, password). where
        username and password are the values obtained from the request's
        'authorization' header.  If authentication succeeds, checkpassword
        returns True, else it returns False.
    
    """

    if '"' in realm:
        raise ValueError('Realm cannot contain the " (quote) character.')
    request = cherrypy.serving.request

    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        try:
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)
                if checkpassword(realm, username, password):
                    if debug:
                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
                    request.login = username
                    return  # successful authentication
        except (ValueError,
                binascii.Error):  # split() error, base64.decodestring() error
            raise cherrypy.HTTPError(400, 'Bad Request')

    # Respond with 401 status and a WWW-Authenticate header
    cherrypy.serving.response.headers[
        'www-authenticate'] = 'Basic realm="%s"' % realm
    raise cherrypy.HTTPError(401,
                             "You are not authorized to access that resource")
Exemplo n.º 5
0
def basic_auth(realm, checkpassword, debug=False):
    """A CherryPy tool which hooks at before_handler to perform
    HTTP Basic Access Authentication, as specified in :rfc:`2617`.

    If the request has an 'authorization' header with a 'Basic' scheme, this
    tool attempts to authenticate the credentials supplied in that header.  If
    the request has no 'authorization' header, or if it does but the scheme is
    not 'Basic', or if authentication fails, the tool sends a 401 response with
    a 'WWW-Authenticate' Basic header.

    realm
        A string containing the authentication realm.

    checkpassword
        A callable which checks the authentication credentials.
        Its signature is checkpassword(realm, username, password). where
        username and password are the values obtained from the request's
        'authorization' header.  If authentication succeeds, checkpassword
        returns True, else it returns False.

    """

    if '"' in realm:
        raise ValueError('Realm cannot contain the " (quote) character.')
    request = cherrypy.serving.request

    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        try:
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)
                if checkpassword(realm, username, password):
                    if debug:
                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
                    request.login = username
                    return  # successful authentication
        # split() error, base64.decodestring() error
        except (ValueError, binascii.Error):
            raise cherrypy.HTTPError(400, 'Bad Request')

    # Respond with 401 status and a WWW-Authenticate header
    cherrypy.serving.response.headers[
        'www-authenticate'] = 'Basic realm="%s"' % realm
    raise cherrypy.HTTPError(
        401, "You are not authorized to access that resource")
Exemplo n.º 6
0
def basic_auth(realm, checkpassword, debug = False):
    if '"' in realm:
        raise ValueError('Realm cannot contain the " (quote) character.')
    request = cherrypy.serving.request
    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        try:
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)
                if checkpassword(realm, username, password):
                    if debug:
                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
                    request.login = username
                    return
        except (ValueError, binascii.Error):
            raise cherrypy.HTTPError(400, 'Bad Request')

    cherrypy.serving.response.headers['www-authenticate'] = 'Basic realm="%s"' % realm
    raise cherrypy.HTTPError(401, 'You are not authorized to access that resource')
Exemplo n.º 7
0
def basic_auth(realm, checkpassword, debug = False):
    if '"' in realm:
        raise ValueError('Realm cannot contain the " (quote) character.')
    request = cherrypy.serving.request
    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        try:
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)
                if checkpassword(realm, username, password):
                    if debug:
                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
                    request.login = username
                    return
        except (ValueError, binascii.Error):
            raise cherrypy.HTTPError(400, 'Bad Request')

    cherrypy.serving.response.headers['www-authenticate'] = 'Basic realm="%s"' % realm
    raise cherrypy.HTTPError(401, 'You are not authorized to access that resource')
Exemplo n.º 8
0
def _parseBasicAuthorization(auth_params):
    username, password = base64_decode(auth_params).split(':', 1)
    return {'username': username, 'password': password}
Exemplo n.º 9
0
def _parseBasicAuthorization(auth_params):
    username, password = base64_decode(auth_params).split(":", 1)
    return {"username": username, "password": password}
Exemplo n.º 10
0
def _parseBasicAuthorization(auth_params):
    username, password = base64_decode(auth_params).split(':', 1)
    return {'username': username,
     'password': password}
Exemplo n.º 11
0
def basic_auth(realm, checkpassword, debug=False):
    """A CherryPy tool which hooks at before_handler to perform
    HTTP Basic Access Authentication, as specified in :rfc:`2617`.

    If the request has an 'authorization' header with a 'Basic' scheme, this
    tool attempts to authenticate the credentials supplied in that header.  If
    the request has no 'authorization' header, or if it does but the scheme is
    not 'Basic', or if authentication fails, the tool sends a 401 response with
    a 'WWW-Authenticate' Basic header.

    realm
        A string containing the authentication realm.

    checkpassword
        A callable which checks the authentication credentials.
        Its signature is checkpassword(realm, username, password). where
        username and password are the values obtained from the request's
        'authorization' header.  If authentication succeeds, checkpassword
        returns True, else it returns False.

    """

    if '"' in realm:
        raise ValueError('Realm cannot contain the " (quote) character.')
    request = cherrypy.serving.request

    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        # split() error, base64.decodestring() error
        msg = 'Bad Request'
        with cherrypy.HTTPError.handle((ValueError, binascii.Error), 400, msg):
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                username, password = base64_decode(params).split(':', 1)

                # Attempt to decode as utf-8. This will either convert password *and* username or
                # none of them.
                try:
                    username, password = decode_utf8(username), decode_utf8(password)
                except UnicodeDecodeError:
                    pass

                if checkpassword(realm, username, password):
                    if debug:
                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
                    request.login = username
                    return  # successful authentication

    # Respond with 401 status and a WWW-Authenticate header
    cherrypy.serving.response.headers[
        'www-authenticate'] = 'Basic realm="%s"' % realm

    # Attempt to bring the browser to switch charset in conformance with RFC-7617
    #     https://tools.ietf.org/html/rfc7617
    #
    # As of 2018-01-02: Firefox 57.0.2 (32-Bit), curl 7.56.1, Google-Chrome 63.0.3239.108,
    # Internet Explorer 11.786.15063.0 and Edge 40.15063.674.0 will ignore it.
    # cherrypy.serving.response.headers[
    #     'www-authenticate'] = 'Basic realm="%s", charset="UTF-8"' % realm

    raise cherrypy.HTTPError(
        401, 'You are not authorized to access that resource')
Exemplo n.º 12
0
    def authenticate(realm, rules=None):
        # Sanity-check our inputs.
        if '"' in realm:
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate",
                "Realm cannot contain the \" (quote) character.")
            raise ValueError("Realm cannot contain the \" (quote) character.")

        # Require a secure connection.
        if not (cherrypy.request.scheme == "https" or
                cherrypy.request.headers.get("x-forwarded-proto") == "https"):
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate",
                "cherrypy.HTTPError 403 secure connection required.")
            raise cherrypy.HTTPError("403 Secure connection required.")

        # Get the client ip, which might be forwarded by a proxy.
        remote_ip = cherrypy.request.headers.get(
            "x-forwarded-for"
        ) if "x-forwarded-for" in cherrypy.request.headers else cherrypy.request.rem

        # See if the client already has a valid session.
        if "slycatauth" in cherrypy.request.cookie:
            sid = cherrypy.request.cookie["slycatauth"].value
            if sid in authenticate.sessions:
                started = authenticate.sessions[sid]["created"]
                if datetime.datetime.utcnow(
                ) - started > cherrypy.request.app.config["slycat"][
                        "session-timeout"]:
                    del authenticate.sessions[sid]
                else:
                    # Ensure that the user is logged correctly ...
                    cherrypy.request.login = authenticate.sessions[sid][
                        "creator"]
                    return
            else:
                # Expired or forged cookie
                cherrypy.log.error("@%s: expired/unknown session." %
                                   (remote_ip))

        # If the client hasn't authenticated, tell them to do so.
        authorization = cherrypy.request.headers.get("authorization")
        if authorization is None:
            cherrypy.response.headers[
                "www-authenticate"] = "Basic realm=\"%s\"" % realm
            # Not sure if email should be sent for this error: it is generated everytime a new user hit the homepage...
            # slycat.email.send_error("slycat-standard-authentication.py authenticate", "cherrypy.HTTPError 401 authentication required.")
            raise cherrypy.HTTPError(401, "Authentication required.")

        # Parse the client's authentication response.
        try:
            scheme, params = authorization.split(" ", 1)
        except:
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate",
                "cherrypy.HTTPError 400")
            raise cherrypy.HTTPError(400)
        if scheme.lower() != "basic":
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate",
                "cherrypy.HTTPError 400")
            raise cherrypy.HTTPError(400)
        try:
            username, password = base64_decode(params).split(":", 1)
        except:
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate",
                "cherrypy.HTTPError 400")
            raise cherrypy.HTTPError(400)

        cherrypy.log.error("%s@%s: Checking password." % (username, remote_ip))

        if authenticate.password_check is None:
            if "password-check" not in cherrypy.request.app.config[
                    "slycat-web-server"]:
                raise cherrypy.HTTPError("500 No password check configured.")
            plugin = cherrypy.request.app.config["slycat-web-server"][
                "password-check"]["plugin"]
            args = cherrypy.request.app.config["slycat-web-server"][
                "password-check"].get("args", [])
            kwargs = cherrypy.request.app.config["slycat-web-server"][
                "password-check"].get("kwargs", {})
            if plugin not in slycat.web.server.plugin.manager.password_checks.keys(
            ):
                slycat.email.send_error(
                    "slycat-standard-authentication.py authenticate",
                    "cherrypy.HTTPError 500 no password check plugin found.")
                raise cherrypy.HTTPError("500 No password check plugin found.")
            authenticate.password_check = functools.partial(
                slycat.web.server.plugin.manager.password_checks[plugin],
                *args, **kwargs)

        success, groups = authenticate.password_check(realm, username,
                                                      password)
        if success:
            # Apply (optional) authentication rules.
            if rules is not None:
                deny = None
                for operation, category, members in rules:
                    if operation not in ["allow", "deny"]:
                        slycat.email.send_error(
                            "slycat-standard-authentication.py authenticate",
                            "cherrypy.HTTPError 500 unknown operation: %s." %
                            operation)
                        raise cherrypy.HTTPError("500 Unknown operation: %s." %
                                                 operation)
                    if category not in ["users", "groups"]:
                        slycat.email.send_error(
                            "slycat-standard-authentication.py authenticate",
                            "cherrypy.HTTPError 500 unknown category: %s." %
                            category)
                        raise cherrypy.HTTPError("500 Unknown category: %s." %
                                                 category)

                    operation_default = True if operation == "allow" else False
                    operation_deny = False if operation == "allow" else True

                    if deny is None:
                        deny = operation_default
                    if category == "users":
                        if username in members:
                            deny = operation_deny
                    elif category == "groups":
                        for group in groups:
                            if group in members:
                                deny = operation_deny
                                break

                if deny:
                    raise cherrypy.HTTPError(
                        "403 User denied by authentication rules.")

            # Successful authentication, create a session and return.
            cherrypy.log.error("%s@%s: Password check succeeded." %
                               (username, remote_ip))

            sid = uuid.uuid4().hex
            session = {
                "created": datetime.datetime.utcnow(),
                "creator": username
            }
            database = slycat.web.server.database.couchdb.connect()
            database.save({
                "_id": sid,
                "type": "session",
                "created": session["created"].isoformat(),
                "creator": session["creator"]
            })

            authenticate.sessions[sid] = session

            cherrypy.response.cookie["slycatauth"] = sid
            cherrypy.response.cookie["slycatauth"]["path"] = "/"
            cherrypy.response.cookie["slycatauth"]["secure"] = 1
            cherrypy.response.cookie["slycatauth"]["httponly"] = 1
            cherrypy.request.login = username
            return  # successful authentication

        # Authentication failed, tell the client to try again.
        cherrypy.log.error("%s@%s: Password check failed." %
                           (username, remote_ip))
        cherrypy.response.headers[
            "www-authenticate"] = "Basic realm=\"%s\"" % realm
        slycat.email.send_error(
            "slycat-standard-authentication.py authenticate",
            "cherrypy.HTTPError 401 authentication required.")
        raise cherrypy.HTTPError(401, "Authentication required.")
Exemplo n.º 13
0
    def authenticate(realm, rules=None):
        # Sanity-check our inputs.
        if '"' in realm:
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate", 'Realm cannot contain the " (quote) character.'
            )
            raise ValueError('Realm cannot contain the " (quote) character.')

        # Require a secure connection.
        if not (cherrypy.request.scheme == "https" or cherrypy.request.headers.get("x-forwarded-proto") == "https"):
            slycat.email.send_error(
                "slycat-standard-authentication.py authenticate", "cherrypy.HTTPError 403 secure connection required."
            )
            raise cherrypy.HTTPError("403 Secure connection required.")

        # Get the client ip, which might be forwarded by a proxy.
        remote_ip = (
            cherrypy.request.headers.get("x-forwarded-for")
            if "x-forwarded-for" in cherrypy.request.headers
            else cherrypy.request.rem
        )

        # See if the client already has a valid session.
        if "slycatauth" in cherrypy.request.cookie:
            sid = cherrypy.request.cookie["slycatauth"].value
            if sid in authenticate.sessions:
                started = authenticate.sessions[sid]["created"]
                if datetime.datetime.utcnow() - started > cherrypy.request.app.config["slycat"]["session-timeout"]:
                    del authenticate.sessions[sid]
                else:
                    # Ensure that the user is logged correctly ...
                    cherrypy.request.login = authenticate.sessions[sid]["creator"]
                    return
            else:
                # Expired or forged cookie
                cherrypy.log.error("@%s: expired/unknown session." % (remote_ip))

        # If the client hasn't authenticated, tell them to do so.
        authorization = cherrypy.request.headers.get("authorization")
        if authorization is None:
            cherrypy.response.headers["www-authenticate"] = 'Basic realm="%s"' % realm
            raise cherrypy.HTTPError(401, "Authentication required.")

        # Parse the client's authentication response.
        try:
            scheme, params = authorization.split(" ", 1)
        except:
            slycat.email.send_error("slycat-standard-authentication.py authenticate", "cherrypy.HTTPError 400")
            raise cherrypy.HTTPError(400)
        if scheme.lower() != "basic":
            slycat.email.send_error("slycat-standard-authentication.py authenticate", "cherrypy.HTTPError 400")
            raise cherrypy.HTTPError(400)
        try:
            username, password = base64_decode(params).split(":", 1)
        except:
            slycat.email.send_error("slycat-standard-authentication.py authenticate", "cherrypy.HTTPError 400")
            raise cherrypy.HTTPError(400)

        cherrypy.log.error("%s@%s: Checking password." % (username, remote_ip))

        if authenticate.password_check is None:
            if "password-check" not in cherrypy.request.app.config["slycat-web-server"]:
                raise cherrypy.HTTPError("500 No password check configured.")
            plugin = cherrypy.request.app.config["slycat-web-server"]["password-check"]["plugin"]
            args = cherrypy.request.app.config["slycat-web-server"]["password-check"].get("args", [])
            kwargs = cherrypy.request.app.config["slycat-web-server"]["password-check"].get("kwargs", {})
            if plugin not in slycat.web.server.plugin.manager.password_checks.keys():
                slycat.email.send_error(
                    "slycat-standard-authentication.py authenticate",
                    "cherrypy.HTTPError 500 no password check plugin found.",
                )
                raise cherrypy.HTTPError("500 No password check plugin found.")
            authenticate.password_check = functools.partial(
                slycat.web.server.plugin.manager.password_checks[plugin], *args, **kwargs
            )

        success, groups = authenticate.password_check(realm, username, password)
        if success:
            # Apply (optional) authentication rules.
            if rules is not None:
                deny = None
                for operation, category, members in rules:
                    if operation not in ["allow", "deny"]:
                        slycat.email.send_error(
                            "slycat-standard-authentication.py authenticate",
                            "cherrypy.HTTPError 500 unknown operation: %s." % operation,
                        )
                        raise cherrypy.HTTPError("500 Unknown operation: %s." % operation)
                    if category not in ["users", "groups"]:
                        slycat.email.send_error(
                            "slycat-standard-authentication.py authenticate",
                            "cherrypy.HTTPError 500 unknown category: %s." % category,
                        )
                        raise cherrypy.HTTPError("500 Unknown category: %s." % category)

                    operation_default = True if operation == "allow" else False
                    operation_deny = False if operation == "allow" else True

                    if deny is None:
                        deny = operation_default
                    if category == "users":
                        if username in members:
                            deny = operation_deny
                    elif category == "groups":
                        for group in groups:
                            if group in members:
                                deny = operation_deny
                                break

                if deny:
                    raise cherrypy.HTTPError("403 User denied by authentication rules.")

            # Successful authentication, create a session and return.
            cherrypy.log.error("%s@%s: Password check succeeded." % (username, remote_ip))

            sid = uuid.uuid4().hex
            session = {"created": datetime.datetime.utcnow(), "creator": username}
            database = slycat.web.server.database.couchdb.connect()
            database.save(
                {
                    "_id": sid,
                    "type": "session",
                    "created": session["created"].isoformat(),
                    "creator": session["creator"],
                }
            )

            authenticate.sessions[sid] = session

            cherrypy.response.cookie["slycatauth"] = sid
            cherrypy.response.cookie["slycatauth"]["path"] = "/"
            cherrypy.response.cookie["slycatauth"]["secure"] = 1
            cherrypy.response.cookie["slycatauth"]["httponly"] = 1
            cherrypy.request.login = username
            return  # successful authentication

        # Authentication failed, tell the client to try again.
        cherrypy.log.error("%s@%s: Password check failed." % (username, remote_ip))
        cherrypy.response.headers["www-authenticate"] = 'Basic realm="%s"' % realm
        slycat.email.send_error(
            "slycat-standard-authentication.py authenticate",
            "cherrypy.HTTPError 401 authentication failed for user %s." % username,
        )
        raise cherrypy.HTTPError(401, "Authentication required.")
Exemplo n.º 14
0
def _parseBasicAuthorization (auth_params):
    username, password = base64_decode(auth_params).split(":", 1)
    return {"username": username, "password": password}
def basic_auth(realm, checkpassword, debug=False, accept_charset='utf-8'):
    """A CherryPy tool which hooks at before_handler to perform
    HTTP Basic Access Authentication, as specified in :rfc:`2617`
    and :rfc:`7617`.

    If the request has an 'authorization' header with a 'Basic' scheme, this
    tool attempts to authenticate the credentials supplied in that header.  If
    the request has no 'authorization' header, or if it does but the scheme is
    not 'Basic', or if authentication fails, the tool sends a 401 response with
    a 'WWW-Authenticate' Basic header.

    realm
        A string containing the authentication realm.

    checkpassword
        A callable which checks the authentication credentials.
        Its signature is checkpassword(realm, username, password). where
        username and password are the values obtained from the request's
        'authorization' header.  If authentication succeeds, checkpassword
        returns True, else it returns False.

    """

    fallback_charset = 'ISO-8859-1'

    if '"' in realm:
        raise ValueError('Realm cannot contain the " (quote) character.')
    request = cherrypy.serving.request

    auth_header = request.headers.get('authorization')
    if auth_header is not None:
        # split() error, base64.decodestring() error
        msg = 'Bad Request'
        with cherrypy.HTTPError.handle((ValueError, binascii.Error), 400, msg):
            scheme, params = auth_header.split(' ', 1)
            if scheme.lower() == 'basic':
                decoded_params = base64_decode(params)
                decoded_params = ntob(decoded_params)

                last_err = None
                for charset in (accept_charset, fallback_charset):
                    try:
                        decoded_params = tonative(decoded_params, charset)
                        break
                    except ValueError as ve:
                        last_err = ve
                else:
                    raise last_err

                decoded_params = ntou(decoded_params)
                decoded_params = unicodedata.normalize('NFC', decoded_params)
                decoded_params = tonative(decoded_params)
                username, password = decoded_params.split(':', 1)
                if checkpassword(realm, username, password):
                    if debug:
                        cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC')
                    request.login = username
                    return  # successful authentication

    charset = accept_charset.upper()
    charset_declaration = ((', charset="%s"' %
                            charset) if charset != fallback_charset else '')
    # Respond with 401 status and a WWW-Authenticate header
    cherrypy.serving.response.headers['www-authenticate'] = (
        'Basic realm="%s"%s' % (realm, charset_declaration))
    raise cherrypy.HTTPError(401,
                             'You are not authorized to access that resource')