mirror of
https://github.com/morpheus65535/bazarr.git
synced 2025-04-23 14:17:46 -04:00
Fix auth issues by only check auth when requesting webui
This commit is contained in:
parent
53039f855c
commit
a742e3c5e3
11 changed files with 69 additions and 79 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
2
frontend/src/@types/window.d.ts
vendored
2
frontend/src/@types/window.d.ts
vendored
|
@ -6,7 +6,7 @@ declare global {
|
|||
|
||||
export interface BazarrServer {
|
||||
baseUrl: string;
|
||||
apiKey: string;
|
||||
apiKey?: string;
|
||||
canUpdate: boolean;
|
||||
hasUpdate: boolean;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue