diff --git a/command.py b/command.py
index 4d29eba..dcf4da3 100644
--- a/command.py
+++ b/command.py
@@ -1183,9 +1183,19 @@ def cmd_web_access(bot, user, text, command, parameter):
import secrets
import datetime
import json
- token = secrets.token_urlsafe(5)
- var.db.set("user", user, json.dumps({'token': token, 'datetime': str(datetime.datetime.now()), 'IP':''}))
- bot.send_msg(constants.strings('webpage_token',token=token), text)
+
+ user_info = var.db.get("user", user, fallback=None)
+ if user_info is not None:
+ user_dict = json.loads(user_info)
+ token = user_dict['token']
+ else:
+ token = secrets.token_urlsafe(5)
+ var.db.set("web_token", token, user)
+
+ var.db.set("user", user, json.dumps({'token': token, 'datetime': str(datetime.datetime.now()), 'IP': ''}))
+
+ access_address = var.config.get("webinterface", "access_address")
+ bot.send_msg(constants.strings('webpage_token', address=access_address, token=token), text)
# Just for debug use
def cmd_real_time_rms(bot, user, text, command, parameter):
diff --git a/configuration.default.ini b/configuration.default.ini
index 24ff1e5..38ab075 100644
--- a/configuration.default.ini
+++ b/configuration.default.ini
@@ -96,14 +96,13 @@ listening_addr = 127.0.0.1
listening_port = 8181
web_logfile =
-# Set this option to True to enable password protection for the web interface
-require_auth = False
-user =
-password =
+auth_method = password
+user = botamusique
+password = mumble
+
+access_address = http://127.0.0.1:8181
-# Set this option to match mumble user with token on flask and add a password to encrypt/sign cookies
flask_secret = ChangeThisPassword
-match_mumble_user = False
[debug]
# Set ffmpeg to True if you want to display DEBUG level log of ffmpeg.
@@ -259,7 +258,7 @@ cleared_tags = Removed all tags from {song}.
cleared_tags_from_all = Removed all tags from songs on the playlist.
shortlist_instruction = Use !sl {indexes} to play the item you want.
auto_paused = Use !play to resume music!
-webpage_token= Your token to access the Bot webpage is {token}, short URL
+webpage_token= Your own address to access the web interface is {address}/?token={token}
help =
Commands
Control
diff --git a/configuration.example.ini b/configuration.example.ini
index 72e21f9..59462cc 100644
--- a/configuration.example.ini
+++ b/configuration.example.ini
@@ -108,33 +108,35 @@ port = 64738
# - "pause",
# - "pause_resume" (pause and resume once somebody re-enters the channel)
# - "stop" (also clears playlist)
-# - "nothing" or leave empty (do nothing)
-#when_nobody_in_channel = nothing
+# - leave empty (do nothing)
+#when_nobody_in_channel =
# [webinterface] stores settings related to the web interface.
[webinterface]
# 'enable': Set 'enabled' to True if you'd like to use the web interface to manage
# your playlist, upload files, etc.
# The web interface is disable by default for security and performance reason.
+# 'access_address': Used when user are questing the address to access the web interface.
#enabled = False
#listening_addr = 127.0.0.1
#listening_port = 8181
#is_web_proxified = True
+#access_address = http://127.0.0.1:8181
# 'web_logfile': write access logs of the web server into this file.
#web_logfile =
-# 'required_auth': Set this to True to enable password protection for the web interface.
-#require_auth = False
-#user =
-#password =
+# 'auth_method': Method used to authenticate users accessing the web interface.
+# Options are 'password', 'token', 'none'
+#auth_method = password
-# Set this option to match mumble user with user on the webinterface
-# It's working with an unique token an user can ask to the bot with token and to add music to the bot.
-# It's also allow users to know who have add a music from the webinterface
-# match_mumble_user = True
+# 'user', 'password': If auth_method set to 'password', you need to set the username and
+# password.
+#user = botamusique
+#password = mumble
-# To use token (need session) flask need a password to encrypt/sign cookies used. !! YOU NEED TO CHANGE IT IF PREVIOUS OPTION IS TRUE!!
+# 'flask_secret': To use token, flask need a password to encrypt/sign cookies.
+# !! YOU NEED TO CHANGE IT IF auth_method IS 'token'!!
# flask_secret = ChangeThisPassword
# [debug] stores some debug settings.
diff --git a/interface.py b/interface.py
index 99b4bda..4af9f9d 100644
--- a/interface.py
+++ b/interface.py
@@ -94,53 +94,47 @@ def authenticate():
def requires_auth(f):
- @wraps(f)
- def decorated(*args, **kwargs):
- global log
- auth = request.authorization
- if var.config.getboolean("webinterface", "require_auth") and (
- not auth or not check_auth(auth.username, auth.password)):
- if auth:
- log.info("web: Failed login attempt, user: %s" % auth.username)
- return authenticate()
- return f(*args, **kwargs)
-
- return decorated
-
-
-def set_cookie_token(f):
- @wraps(f)
- def decorated(*args, **kwargs):
- global log
- if var.config.getboolean("webinterface", "match_mumble_user"):
- users = var.db.items('user')
- if users:
- for user in users:
- tp = json.loads(user[1])
- log.info(tp)
- if tp['token'] == request.args.get('token'):
- t_user = user[0]
- log.info(f"web: token validated for the user: {t_user}")
- session['user']=t_user
- return f(*args, **kwargs)
- log.info("web: Bad token used")
- abort(403)
-
- return f(*args, **kwargs)
- return decorated
-
-
-def requires_token(f):
@wraps(f)
def decorated(*args, **kwargs):
global log, user
- if var.config.getboolean("webinterface", "match_mumble_user"):
- if 'user' in session:
- log.debug(f"Request done by {session['user']}")
- user = session['user']
- else:
- abort(403)
+
+ auth_method = var.config.get("webinterface", "auth_method")
+
+ if auth_method == 'password':
+ auth = request.authorization
+ if var.config.getboolean("webinterface", "require_auth") and (
+ not auth or not check_auth(auth.username, auth.password)):
+ if auth:
+ log.warning(f"web: failed login attempt, user: {auth.username}, from ip {request.remote_addr}.")
+ return authenticate()
+ if auth_method == 'token':
+ if 'token' in session and 'token' not in request.args:
+ token = session['token']
+ token_user = var.db.get("web_token", token, fallback=None)
+ if token_user is not None:
+ user = token_user
+ log.debug(f"web: token validated for the user: {token_user}, from ip {request.remote_addr}.")
+ return f(*args, **kwargs)
+ elif 'token' in request.args:
+ token = request.args.get('token')
+ token_user = var.db.get("web_token", token, fallback=None)
+ if token_user is not None:
+ user = token_user
+
+ user_info = var.db.get("user", user, fallback=None)
+ user_dict = json.loads(user_info)
+ user_dict['IP'] = request.remote_addr
+ var.db.set("user", user, json.dumps(user_dict))
+
+ log.info(f"web: new user access, token validated for the user: {token_user}, from ip {request.remote_addr}.")
+ session['token'] = token
+ return f(*args, **kwargs)
+
+ log.info(f"web: bad token from ip {request.remote_addr}.")
+ abort(403)
+
return f(*args, **kwargs)
+
return decorated
@@ -190,7 +184,6 @@ def get_all_dirs():
@web.route("/", methods=['GET'])
@requires_auth
-@set_cookie_token
def index():
while var.cache.dir_lock.locked():
time.sleep(0.1)
@@ -205,7 +198,6 @@ def index():
@web.route("/playlist", methods=['GET'])
@requires_auth
-@requires_token
def playlist():
if len(var.playlist) == 0:
return ('', 204)
@@ -301,7 +293,6 @@ def status():
@web.route("/post", methods=['POST'])
@requires_auth
-@requires_token
def post():
global log
@@ -520,7 +511,6 @@ def build_library_query_condition(form):
@web.route("/library", methods=['POST'])
@requires_auth
-@requires_token
def library():
global log
ITEM_PER_PAGE = 10
@@ -622,8 +612,7 @@ def library():
@web.route('/upload', methods=["POST"])
-#@requires_auth missing here ?
-@requires_token
+@requires_auth
def upload():
global log
@@ -672,6 +661,7 @@ def upload():
@web.route('/download', methods=["GET"])
+@requires_auth
def download():
global log
diff --git a/static/js/custom.js b/static/js/custom.js
index 1064aab..8b5eec2 100644
--- a/static/js/custom.js
+++ b/static/js/custom.js
@@ -928,6 +928,7 @@ function uploadNextFile(){
form.append('targetdir', uploadTargetDir.value);
req.open('POST', 'upload');
+ req.withCredentials = true;
req.send(form);
file_progress_item.progress.classList.add("progress-bar-striped");