From dc1594b04fd5fe44fae390ac1979969f71006d50 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Mon, 21 Mar 2022 22:07:05 +0100 Subject: [PATCH] Use a safer POST for removepassword, adjust tests --- changedetectionio/__init__.py | 7 +- changedetectionio/forms.py | 2 + changedetectionio/poop.txt | 91 +++++++++++++++++++ changedetectionio/templates/settings.html | 13 +-- .../tests/test_access_control.py | 44 ++++++--- 5 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 changedetectionio/poop.txt diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index 3ea9020c..86062587 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -610,16 +610,15 @@ def changedetection_app(config=None, datastore_o=None): form.notification_format.data = datastore.data['settings']['application']['notification_format'] form.base_url.data = datastore.data['settings']['application']['base_url'] - # Password unset is a GET, but we can lock the session to always need the password - if not os.getenv("SALTED_PASS", False) and request.values.get('removepassword') == 'yes': - from pathlib import Path + if request.method == 'POST' and form.data.get('removepassword_button') == True: + # Password unset is a GET, but we can lock the session to a salted env password to always need the password + if not os.getenv("SALTED_PASS", False): datastore.data['settings']['application']['password'] = False flash("Password protection removed.", 'notice') flask_login.logout_user() return redirect(url_for('settings_page')) if request.method == 'POST' and form.validate(): - datastore.data['settings']['application']['notification_urls'] = form.notification_urls.data datastore.data['settings']['requests']['minutes_between_check'] = form.minutes_between_check.data datastore.data['settings']['application']['extract_title_as_title'] = form.extract_title_as_title.data diff --git a/changedetectionio/forms.py b/changedetectionio/forms.py index df5d6336..b3ffc547 100644 --- a/changedetectionio/forms.py +++ b/changedetectionio/forms.py @@ -353,3 +353,5 @@ class globalSettingsForm(commonSettingsForm): global_subtractive_selectors = StringListField('Remove elements', [ValidateCSSJSONXPATHInput(allow_xpath=False, allow_json=False)]) global_ignore_text = StringListField('Ignore Text', [ValidateListRegex()]) ignore_whitespace = BooleanField('Ignore whitespace') + save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"}) + removepassword_button = SubmitField('Remove password', render_kw={"class": "pure-button pure-button-primary"}) \ No newline at end of file diff --git a/changedetectionio/poop.txt b/changedetectionio/poop.txt new file mode 100644 index 00000000..0a6da233 --- /dev/null +++ b/changedetectionio/poop.txt @@ -0,0 +1,91 @@ + + + + + + + Change Detection + + + + + + + + +
+ + +
+ + + +
+
+ +
+ + + + + + + + +
+ + + \ No newline at end of file diff --git a/changedetectionio/templates/settings.html b/changedetectionio/templates/settings.html index 9551695f..db5b9595 100644 --- a/changedetectionio/templates/settings.html +++ b/changedetectionio/templates/settings.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% block content %} -{% from '_helpers.jinja' import render_field %} +{% from '_helpers.jinja' import render_field, render_button %} {% from '_common_fields.jinja' import render_common_settings_form %} @@ -27,8 +27,7 @@
{% if not hide_remove_pass %} {% if current_user.is_authenticated %} - Remove password + {{ render_button(form.removepassword_button) }} {% else %} {{ render_field(form.password) }} Password protection for your changedetection.io application. @@ -114,11 +113,9 @@ nav
- - Back - Delete - History - Snapshot Data + {{ render_button(form.save_button) }} + Back + Delete History Snapshot Data
diff --git a/changedetectionio/tests/test_access_control.py b/changedetectionio/tests/test_access_control.py index 026929ba..80eeb780 100644 --- a/changedetectionio/tests/test_access_control.py +++ b/changedetectionio/tests/test_access_control.py @@ -4,8 +4,8 @@ from flask import url_for def test_check_access_control(app, client): # Still doesnt work, but this is closer. - with app.test_client() as c: - # Check we dont have any password protection enabled yet. + with app.test_client(use_cookies=True) as c: + # Check we don't have any password protection enabled yet. res = c.get(url_for("settings_page")) assert b"Remove password" not in res.data @@ -46,15 +46,20 @@ def test_check_access_control(app, client): assert b"BACKUP" in res.data assert b"IMPORT" in res.data assert b"LOG OUT" in res.data + assert b"minutes_between_check" in res.data + assert b"fetch_backend" in res.data - # Now remove the password so other tests function, @todo this should happen before each test automatically - res = c.get(url_for("settings_page", removepassword="yes"), - follow_redirects=True) - assert b"Password protection removed." in res.data - - res = c.get(url_for("index")) - assert b"LOG OUT" not in res.data - + res = c.post( + url_for("settings_page"), + data={ + "minutes_between_check": 180, + "tag": "", + "headers": "", + "fetch_backend": "html_webdriver", + "removepassword_button": "Remove password" + }, + follow_redirects=True, + ) # There was a bug where saving the settings form would submit a blank password def test_check_access_control_no_blank_password(app, client): @@ -71,8 +76,7 @@ def test_check_access_control_no_blank_password(app, client): data={"password": "", "minutes_between_check": 180, 'fetch_backend': "html_requests"}, - - follow_redirects=True + follow_redirects=True ) assert b"Password protection enabled." not in res.data @@ -91,7 +95,8 @@ def test_check_access_no_remote_access_to_remove_password(app, client): # Enable password check. res = c.post( url_for("settings_page"), - data={"password": "password", "minutes_between_check": 180, + data={"password": "password", + "minutes_between_check": 180, 'fetch_backend': "html_requests"}, follow_redirects=True ) @@ -99,8 +104,17 @@ def test_check_access_no_remote_access_to_remove_password(app, client): assert b"Password protection enabled." in res.data assert b"Login" in res.data - res = c.get(url_for("settings_page", removepassword="yes"), - follow_redirects=True) + res = c.post( + url_for("settings_page"), + data={ + "minutes_between_check": 180, + "tag": "", + "headers": "", + "fetch_backend": "html_webdriver", + "removepassword_button": "Remove password" + }, + follow_redirects=True, + ) assert b"Password protection removed." not in res.data res = c.get(url_for("index"),