|
|
@ -143,13 +143,21 @@ class User(flask_login.UserMixin):
|
|
|
|
def get_id(self):
|
|
|
|
def get_id(self):
|
|
|
|
return str(self.id)
|
|
|
|
return str(self.id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Compare given password against JSON store or Env var
|
|
|
|
def check_password(self, password):
|
|
|
|
def check_password(self, password):
|
|
|
|
|
|
|
|
|
|
|
|
import base64
|
|
|
|
import base64
|
|
|
|
import hashlib
|
|
|
|
import hashlib
|
|
|
|
|
|
|
|
|
|
|
|
# Getting the values back out
|
|
|
|
# Can be stored in env (for deployments) or in the general configs
|
|
|
|
raw_salt_pass = base64.b64decode(datastore.data['settings']['application']['password'])
|
|
|
|
raw_salt_pass = os.getenv("SALTED_PASS", False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not raw_salt_pass:
|
|
|
|
|
|
|
|
raw_salt_pass = datastore.data['settings']['application']['password']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
raw_salt_pass = base64.b64decode(raw_salt_pass)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
salt_from_storage = raw_salt_pass[:32] # 32 is the length of the salt
|
|
|
|
salt_from_storage = raw_salt_pass[:32] # 32 is the length of the salt
|
|
|
|
|
|
|
|
|
|
|
|
# Use the exact same setup you used to generate the key, but this time put in the password to check
|
|
|
|
# Use the exact same setup you used to generate the key, but this time put in the password to check
|
|
|
@ -200,7 +208,7 @@ def changedetection_app(config=None, datastore_o=None):
|
|
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
|
|
def login():
|
|
|
|
def login():
|
|
|
|
|
|
|
|
|
|
|
|
if not datastore.data['settings']['application']['password']:
|
|
|
|
if not datastore.data['settings']['application']['password'] and not os.getenv("SALTED_PASS", False):
|
|
|
|
flash("Login not required, no password enabled.", "notice")
|
|
|
|
flash("Login not required, no password enabled.", "notice")
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
|
|
|
@ -227,8 +235,10 @@ def changedetection_app(config=None, datastore_o=None):
|
|
|
|
|
|
|
|
|
|
|
|
@app.before_request
|
|
|
|
@app.before_request
|
|
|
|
def do_something_whenever_a_request_comes_in():
|
|
|
|
def do_something_whenever_a_request_comes_in():
|
|
|
|
|
|
|
|
|
|
|
|
# Disable password login if there is not one set
|
|
|
|
# Disable password login if there is not one set
|
|
|
|
app.config['LOGIN_DISABLED'] = datastore.data['settings']['application']['password'] == False
|
|
|
|
# (No password in settings or env var)
|
|
|
|
|
|
|
|
app.config['LOGIN_DISABLED'] = datastore.data['settings']['application']['password'] == False and os.getenv("SALTED_PASS", False) == False
|
|
|
|
|
|
|
|
|
|
|
|
# For the RSS path, allow access via a token
|
|
|
|
# For the RSS path, allow access via a token
|
|
|
|
if request.path == '/rss' and request.args.get('token'):
|
|
|
|
if request.path == '/rss' and request.args.get('token'):
|
|
|
@ -571,8 +581,8 @@ def changedetection_app(config=None, datastore_o=None):
|
|
|
|
form.notification_format.data = datastore.data['settings']['application']['notification_format']
|
|
|
|
form.notification_format.data = datastore.data['settings']['application']['notification_format']
|
|
|
|
form.base_url.data = datastore.data['settings']['application']['base_url']
|
|
|
|
form.base_url.data = datastore.data['settings']['application']['base_url']
|
|
|
|
|
|
|
|
|
|
|
|
# Password unset is a GET
|
|
|
|
# Password unset is a GET, but we can lock the session to always need the password
|
|
|
|
if request.values.get('removepassword') == 'yes':
|
|
|
|
if not os.getenv("SALTED_PASS", False) and request.values.get('removepassword') == 'yes':
|
|
|
|
from pathlib import Path
|
|
|
|
from pathlib import Path
|
|
|
|
datastore.data['settings']['application']['password'] = False
|
|
|
|
datastore.data['settings']['application']['password'] = False
|
|
|
|
flash("Password protection removed.", 'notice')
|
|
|
|
flash("Password protection removed.", 'notice')
|
|
|
@ -606,7 +616,7 @@ def changedetection_app(config=None, datastore_o=None):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
flash('No notification URLs set, cannot send test.', 'error')
|
|
|
|
flash('No notification URLs set, cannot send test.', 'error')
|
|
|
|
|
|
|
|
|
|
|
|
if form.password.encrypted_password:
|
|
|
|
if not os.getenv("SALTED_PASS", False) and form.password.encrypted_password:
|
|
|
|
datastore.data['settings']['application']['password'] = form.password.encrypted_password
|
|
|
|
datastore.data['settings']['application']['password'] = form.password.encrypted_password
|
|
|
|
flash("Password protection enabled.", 'notice')
|
|
|
|
flash("Password protection enabled.", 'notice')
|
|
|
|
flask_login.logout_user()
|
|
|
|
flask_login.logout_user()
|
|
|
@ -618,7 +628,10 @@ def changedetection_app(config=None, datastore_o=None):
|
|
|
|
if request.method == 'POST' and not form.validate():
|
|
|
|
if request.method == 'POST' and not form.validate():
|
|
|
|
flash("An error occurred, please see below.", "error")
|
|
|
|
flash("An error occurred, please see below.", "error")
|
|
|
|
|
|
|
|
|
|
|
|
output = render_template("settings.html", form=form, current_base_url = datastore.data['settings']['application']['base_url'])
|
|
|
|
output = render_template("settings.html",
|
|
|
|
|
|
|
|
form=form,
|
|
|
|
|
|
|
|
current_base_url = datastore.data['settings']['application']['base_url'],
|
|
|
|
|
|
|
|
hide_remove_pass=os.getenv("SALTED_PASS", False))
|
|
|
|
|
|
|
|
|
|
|
|
return output
|
|
|
|
return output
|
|
|
|
|
|
|
|
|
|
|
@ -999,6 +1012,8 @@ def notification_runner():
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
print("Watch URL: {} Error {}".format(n_object['watch_url'], e))
|
|
|
|
print("Watch URL: {} Error {}".format(n_object['watch_url'], e))
|
|
|
|
|
|
|
|
datastore.update_watch(uuid=n_object['uuid'], update_obj={'last_error': "Notification error: " + str(e)})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Thread runner to check every minute, look for new watches to feed into the Queue.
|
|
|
|
# Thread runner to check every minute, look for new watches to feed into the Queue.
|
|
|
|
def ticker_thread_check_time_launch_checks():
|
|
|
|
def ticker_thread_check_time_launch_checks():
|
|
|
|