From 486d2f9481982fef0ff0a30c314f74e9268cc7fd Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Tue, 25 Jul 2023 16:49:04 -0400 Subject: [PATCH] Improved logging of failed attempt to authenticate to UI. Successful basic authentication attempt cannot be logged right now. --- bazarr/api/system/account.py | 22 +++++++++++++++------- bazarr/app/ui.py | 8 +++++--- bazarr/utilities/helper.py | 11 +++++++++-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/bazarr/api/system/account.py b/bazarr/api/system/account.py index 70ad5dbaa..536e14f45 100644 --- a/bazarr/api/system/account.py +++ b/bazarr/api/system/account.py @@ -2,7 +2,7 @@ import gc -from flask import session +from flask import session, request from flask_restx import Resource, Namespace, reqparse from app.config import settings @@ -22,23 +22,31 @@ class SystemAccount(Resource): @api_ns_system_account.doc(parser=post_request_parser) @api_ns_system_account.response(204, 'Success') @api_ns_system_account.response(400, 'Unknown action') - @api_ns_system_account.response(404, 'Unknown authentication type define in config.ini') + @api_ns_system_account.response(403, 'Authentication failed') + @api_ns_system_account.response(406, 'Browser must be closed to invalidate basic authentication') + @api_ns_system_account.response(500, 'Unknown authentication type define in config.ini') def post(self): """Login or logout from Bazarr UI when using form login""" args = self.post_request_parser.parse_args() if settings.auth.type != 'form': - return 'Unknown authentication type define in config.ini', 404 + return 'Unknown authentication type define in config.ini', 500 action = args.get('action') if action == 'login': username = args.get('username') password = args.get('password') - if check_credentials(username, password): + if check_credentials(username, password, request): session['logged_in'] = True return '', 204 + else: + session['logged_in'] = False + return 'Authentication failed', 403 elif action == 'logout': - session.clear() - gc.collect() - return '', 204 + if settings.auth.type == 'basic': + return 'Browser must be closed to invalidate basic authentication', 406 + else: + session.clear() + gc.collect() + return '', 204 return 'Unknown action', 400 diff --git a/bazarr/app/ui.py b/bazarr/app/ui.py index ae8bfba96..77708a8c5 100644 --- a/bazarr/app/ui.py +++ b/bazarr/app/ui.py @@ -49,7 +49,8 @@ def check_login(actual_method): def wrapper(*args, **kwargs): if settings.auth.type == 'basic': auth = request.authorization - if not (auth and check_credentials(request.authorization.username, request.authorization.password)): + if not (auth and + check_credentials(request.authorization.username, request.authorization.password, request)): return ('Unauthorized', 401, { 'WWW-Authenticate': 'Basic realm="Login Required"' }) @@ -65,12 +66,13 @@ def catch_all(path): auth = True if settings.auth.type == 'basic': auth = request.authorization - if not (auth and check_credentials(request.authorization.username, request.authorization.password)): + if not (auth and check_credentials(request.authorization.username, request.authorization.password, request, + log_success=False)): return ('Unauthorized', 401, { 'WWW-Authenticate': 'Basic realm="Login Required"' }) elif settings.auth.type == 'form': - if 'logged_in' not in session: + if 'logged_in' not in session or not session['logged_in']: auth = False try: diff --git a/bazarr/utilities/helper.py b/bazarr/utilities/helper.py index 0a6343393..5169bd259 100644 --- a/bazarr/utilities/helper.py +++ b/bazarr/utilities/helper.py @@ -10,10 +10,17 @@ from bs4 import UnicodeDammit from app.config import settings -def check_credentials(user, pw): +def check_credentials(user, pw, request, log_success=True): + ip_addr = request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr) username = settings.auth.username password = settings.auth.password - return hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username + if hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username: + if log_success: + logging.info(f'Successful authentication from {ip_addr} for user {user}') + return True + else: + logging.info(f'Failed authentication from {ip_addr} for user {user}') + return False def get_subtitle_destination_folder():