Fix auth issues by only check auth when requesting webui

This commit is contained in:
LASER-Yi 2021-04-21 22:29:25 +08:00
parent 53039f855c
commit a742e3c5e3
11 changed files with 69 additions and 79 deletions

View File

@ -31,7 +31,7 @@ from list_subtitles import store_subtitles, store_subtitles_movie, series_scan_s
list_missing_subtitles, list_missing_subtitles_movies
from utils import history_log, history_log_movie, blacklist_log, blacklist_delete, blacklist_delete_all, \
blacklist_log_movie, blacklist_delete_movie, blacklist_delete_all_movie, get_sonarr_version, get_radarr_version, \
delete_subtitles, subtitles_apply_mods, translate_subtitles_file
delete_subtitles, subtitles_apply_mods, translate_subtitles_file, check_credentials
from get_providers import get_providers, get_providers_auth, list_throttled_providers, reset_throttled_providers, \
get_throttled_providers, set_throttled_providers
from event_handler import event_stream
@ -52,27 +52,9 @@ api = Api(api_bp)
None_Keys = ['null', 'undefined', '']
def check_credentials(user, pw):
username = settings.auth.username
password = settings.auth.password
if hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username:
return True
return False
def authenticate(actual_method):
@wraps(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)):
return ('Unauthorized', 401, {
'WWW-Authenticate': 'Basic realm="Login Required"'
})
elif settings.auth.type == 'form':
if 'logged_in' not in session:
return abort(401, message="Unauthorized")
apikey_settings = settings.auth.apikey
apikey_get = request.args.get('apikey')
apikey_post = request.form.get('apikey')
@ -314,12 +296,9 @@ class SystemAccount(Resource):
session['logged_in'] = True
return '', 204
elif action == 'logout':
if settings.auth.type == 'basic':
return abort(401)
elif settings.auth.type == 'form':
session.clear()
gc.collect()
return '', 204
session.clear()
gc.collect()
return '', 204
return '', 401

View File

@ -39,6 +39,7 @@ from get_movies import *
from check_update import apply_update, check_if_new_update, check_releases
from server import app, webserver
from functools import wraps
from utils import check_credentials
# Install downloaded update
if bazarr_version != '':
@ -57,44 +58,68 @@ login_auth = settings.auth.type
update_notifier()
def check_login(actual_method):
@wraps(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)):
return ('Unauthorized', 401, {
'WWW-Authenticate': 'Basic realm="Login Required"'
})
elif settings.auth.type == 'form':
if 'logged_in' not in session:
return abort(401, message="Unauthorized")
actual_method(*args, **kwargs)
@app.errorhandler(404)
def page_not_found(e):
return redirect(base_url, code=302)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
return render_template("index.html")
auth = True
if settings.auth.type == 'basic':
auth = request.authorization
if not (auth and check_credentials(request.authorization.username, request.authorization.password)):
return ('Unauthorized', 401, {
'WWW-Authenticate': 'Basic realm="Login Required"'
})
elif settings.auth.type == 'form':
if 'logged_in' not in session:
auth = False
@app.context_processor
def template_variable_processor():
updated = False
try:
updated = database.execute("SELECT updated FROM system", only_one=True)['updated']
except:
pass
updated = False
inject = dict()
inject["apiKey"] = settings.auth.apikey
inject["baseUrl"] = base_url
inject["canUpdate"] = not args.no_update
inject["hasUpdate"] = updated != '0'
if auth:
inject["apiKey"] = settings.auth.apikey
template_url = base_url
if not template_url.endswith("/"):
template_url += "/"
return dict(BAZARR_SERVER_INJECT=inject, baseUrl=template_url)
return render_template("index.html", BAZARR_SERVER_INJECT=inject, baseUrl=template_url)
@check_login
@app.route('/bazarr.log')
def download_log():
return send_file(os.path.join(args.config_dir, 'log', 'bazarr.log'), cache_timeout=0, as_attachment=True)
@check_login
@app.route('/images/series/<path:url>', methods=['GET'])
def series_images(url):
url = url.strip("/")
@ -109,6 +134,7 @@ def series_images(url):
return Response(stream_with_context(req.iter_content(2048)), content_type=req.headers['content-type'])
@check_login
@app.route('/images/movies/<path:url>', methods=['GET'])
def movies_images(url):
apikey = settings.radarr.apikey
@ -135,6 +161,7 @@ def configured():
database.execute("UPDATE system SET configured = 1")
@check_login
@app.route('/test', methods=['GET'])
@app.route('/test/<protocol>/<path:url>', methods=['GET'])
def proxy(protocol, url):

View File

@ -398,3 +398,8 @@ def translate_subtitles_file(video_path, source_srt_file, to_lang, forced, hi):
subs.save(dest_srt_file)
return dest_srt_file
def check_credentials(user, pw):
username = settings.auth.username
password = settings.auth.password
return hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username

View File

@ -1,7 +1,6 @@
import { createAction } from "redux-actions";
import { BadgesApi } from "../../apis";
import {
SITE_AUTH_SUCCESS,
SITE_BADGE_UPDATE,
SITE_INITIALIZED,
SITE_INITIALIZE_FAILED,
@ -28,8 +27,6 @@ const siteInitialized = createAction(SITE_INITIALIZED);
export const siteRedirectToAuth = createAction(SITE_NEED_AUTH);
export const siteAuthSuccess = createAction(SITE_AUTH_SUCCESS);
export const badgeUpdateAll = createAsyncAction(SITE_BADGE_UPDATE, () =>
BadgesApi.all()
);

View File

@ -31,7 +31,6 @@ export const MOVIES_UPDATE_RANGE = "MOVIES_UPDATE_RANGE";
export const MOVIES_UPDATE_BLACKLIST = "UPDATE_MOVIES_BLACKLIST";
// Site Action
export const SITE_AUTH_SUCCESS = "SITE_AUTH_SUCCESS";
export const SITE_NEED_AUTH = "SITE_NEED_AUTH";
export const SITE_INITIALIZED = "SITE_SYSTEM_INITIALIZED";
export const SITE_INITIALIZE_FAILED = "SITE_INITIALIZE_FAILED";

View File

@ -1,7 +1,7 @@
import { Action, handleActions } from "redux-actions";
import { storage } from "../../@storage/local";
import apis from "../../apis";
import {
SITE_AUTH_SUCCESS,
SITE_BADGE_UPDATE,
SITE_INITIALIZED,
SITE_INITIALIZE_FAILED,
@ -23,14 +23,15 @@ function updateLocalStorage(): Partial<ReduxStore.Site> {
const reducer = handleActions<ReduxStore.Site, any>(
{
[SITE_NEED_AUTH]: (state) => ({
...state,
auth: false,
}),
[SITE_AUTH_SUCCESS]: (state) => ({
...state,
auth: true,
}),
[SITE_NEED_AUTH]: (state) => {
if (process.env.NODE_ENV !== "development") {
apis.danger_resetApi("NEED_AUTH");
}
return {
...state,
auth: false,
};
},
[SITE_INITIALIZED]: (state) => ({
...state,
initialized: true,

View File

@ -6,7 +6,7 @@ declare global {
export interface BazarrServer {
baseUrl: string;
apiKey: string;
apiKey?: string;
canUpdate: boolean;
hasUpdate: boolean;
}

View File

@ -58,7 +58,7 @@ const Header: FunctionComponent<Props> = () => {
const [settings] = useSystemSettings();
const canLogout = (settings.data?.auth.type ?? "none") !== "none";
const canLogout = (settings.data?.auth.type ?? "none") === "form";
const toggleSidebar = useContext(SidebarToggleContext);

View File

@ -9,8 +9,7 @@ import {
Spinner,
} from "react-bootstrap";
import { Redirect } from "react-router-dom";
import { siteAuthSuccess } from "../@redux/actions";
import { useReduxAction, useReduxStore } from "../@redux/hooks/base";
import { useReduxStore } from "../@redux/hooks/base";
import logo from "../@static/logo128.png";
import { SystemApi } from "../apis";
import "./style.scss";
@ -29,7 +28,7 @@ const AuthPage: FunctionComponent<Props> = () => {
setTimeout(() => setError(""), 2000);
}, []);
const onSuccess = useReduxAction(siteAuthSuccess);
const onSuccess = useCallback(() => window.location.reload(), []);
const authState = useReduxStore((s) => s.site.auth);

View File

@ -17,13 +17,13 @@ class Api {
}
}
initialize(url: string, apikey: string) {
initialize(url: string, apikey?: string) {
this.axios = Axios.create({
baseURL: url,
});
this.axios.defaults.headers.post["Content-Type"] = "application/json";
this.axios.defaults.headers.common["X-API-KEY"] = apikey;
this.axios.defaults.headers.common["X-API-KEY"] = apikey ?? "AUTH_NEEDED";
this.source = Axios.CancelToken.source();
@ -56,6 +56,10 @@ class Api {
);
}
danger_resetApi(apikey: string) {
this.axios.defaults.headers.common["X-API-KEY"] = apikey;
}
onOnline() {
const offline = reduxStore.getState().site.offline;
if (offline) {

View File

@ -1,4 +1,4 @@
import Axios, { AxiosInstance } from "axios";
import apis from ".";
type UrlTestResponse =
| {
@ -11,35 +11,14 @@ type UrlTestResponse =
};
class RequestUtils {
private axios!: AxiosInstance;
constructor() {
if (process.env.NODE_ENV === "development") {
this.recreateAxios("/", process.env.REACT_APP_APIKEY!);
} else {
const baseUrl =
window.Bazarr.baseUrl === "/" ? "/" : `${window.Bazarr.baseUrl}/`;
this.recreateAxios(baseUrl, window.Bazarr.apiKey);
}
}
private recreateAxios(url: string, apikey: string) {
this.axios = Axios.create({
baseURL: url,
});
this.axios.defaults.headers.post["Content-Type"] = "application/json";
this.axios.defaults.headers.common["x-api-key"] = apikey;
}
urlTest(
protocol: string,
url: string,
params?: any
): Promise<UrlTestResponse> {
return new Promise<UrlTestResponse>((resolve, reject) => {
this.axios
.get(`test/${protocol}/${url}api/system/status`, { params })
apis.axios
.get(`../test/${protocol}/${url}api/system/status`, { params })
.then((result) => resolve(result.data))
.catch(reject);
});