diff --git a/MANIFEST.in b/MANIFEST.in index f7b15f44..d55ab541 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include hw_diag/templates/template.html +include hw_diag/templates/password_reset.html include hw_diag/templates/password_change_form.html include hw_diag/templates/login_form.html include hw_diag/templates/diagnostics_page_light_miner.html diff --git a/hw_diag/templates/login_form.html b/hw_diag/templates/login_form.html index 6fd365db..8bdb09fe 100644 --- a/hw_diag/templates/login_form.html +++ b/hw_diag/templates/login_form.html @@ -40,12 +40,13 @@

{% endif %}
- +

Hint: The default password is the ethernet MAC address of your device without any colons which may be found on your device's identification sticker.

+

Forgotten Password?

diff --git a/hw_diag/templates/password_change_form.html b/hw_diag/templates/password_change_form.html index d429e69f..be844b1a 100644 --- a/hw_diag/templates/password_change_form.html +++ b/hw_diag/templates/password_change_form.html @@ -18,11 +18,11 @@

Change Password

- +
- +
- +
diff --git a/hw_diag/templates/password_reset.html b/hw_diag/templates/password_reset.html index ba6a5346..bb3ec05b 100644 --- a/hw_diag/templates/password_reset.html +++ b/hw_diag/templates/password_reset.html @@ -34,6 +34,7 @@

+

Press and hold your miner's button for at-least 5 seconds to reset device's password. You must complete this within 60 seconds, if you require more time please refresh the page. @@ -43,14 +44,30 @@

+

Cancel | Refresh

diff --git a/hw_diag/utilities/auth.py b/hw_diag/utilities/auth.py index 443481e0..0b9bc78e 100644 --- a/hw_diag/utilities/auth.py +++ b/hw_diag/utilities/auth.py @@ -145,22 +145,62 @@ def update_password_reset_expiry(): g.db.commit() -def check_password_reset_expiry(): +def set_last_password_reset(): now = datetime.datetime.utcnow() - expiry = now + datetime.timedelta(minutes=1) - try: reset_expiry_row = g.db.query(AuthKeyValue). \ - filter(AuthKeyValue.key == 'password_reset_expiry'). \ + filter(AuthKeyValue.key == 'password_last_reset'). \ one() - reset_expiry_row.value = expiry.isoformat() + reset_expiry_row.value = now.isoformat() g.db.commit() except NoResultFound: reset_expiry_row = AuthKeyValue( - key='password_reset_expiry', - value=expiry.isoformat() + key='password_last_reset', + value=now.isoformat() ) g.db.add(reset_expiry_row) g.db.commit() - return expiry + +def password_updated_in_last_minute(): + now = datetime.datetime.utcnow() + one_min_ago = now - datetime.timedelta(minutes=1) + try: + last_reset_row = g.db.query(AuthKeyValue). \ + filter(AuthKeyValue.key == 'password_last_reset'). \ + one() + last_reset = datetime.datetime.fromisoformat(last_reset_row.value) + + if last_reset > one_min_ago: + valid = True + else: + valid = False + except Exception: + valid = False + + return valid + + +def perform_password_reset(): + now = datetime.datetime.utcnow() + + try: + reset_expiry_row = g.db.query(AuthKeyValue). \ + filter(AuthKeyValue.key == 'password_reset_expiry'). \ + one() + expiry = datetime.datetime.fromisoformat(reset_expiry_row.value) + + if expiry > now: + diagnostics = read_diagnostics_file() + eth_mac = diagnostics.get('E0') + default_password = eth_mac.replace(':', '') + write_password(default_password) + set_last_password_reset() + valid = True + else: + valid = False + + except Exception: + valid = False + + return valid diff --git a/hw_diag/views/auth.py b/hw_diag/views/auth.py index 762007ea..5c62e287 100644 --- a/hw_diag/views/auth.py +++ b/hw_diag/views/auth.py @@ -7,6 +7,7 @@ from flask import request from flask import redirect from flask import session +from flask import jsonify from hm_pyhelper.logger import get_logger from hw_diag.utilities.diagnostics import read_diagnostics_file @@ -17,6 +18,8 @@ from hw_diag.utilities.auth import count_recent_auth_failures from hw_diag.utilities.auth import add_login_failure from hw_diag.utilities.auth import update_password_reset_expiry +from hw_diag.utilities.auth import perform_password_reset +from hw_diag.utilities.auth import password_updated_in_last_minute logging.basicConfig(level=os.environ.get("LOGLEVEL", "DEBUG")) @@ -136,6 +139,9 @@ def handle_login(): @AUTH.route('/reset_password') def display_password_reset_page(): + if session.get('logged_in'): + return redirect('/') + diagnostics = read_diagnostics_file() display_lte = should_display_lte(diagnostics) now = datetime.datetime.utcnow() @@ -148,3 +154,17 @@ def display_password_reset_page(): display_lte=display_lte, now=now ) + + +@AUTH.route('/password_reset', methods=['POST']) +def handle_reset_password(): + # Check this originates from the docker private subnet, only + # internal containers should be privileged to reset the password. + password_reset = perform_password_reset() + return jsonify({'password_updated': password_reset}) + + +@AUTH.route('/password_reset', methods=['GET']) +def validate_password_reset(): + result = password_updated_in_last_minute() + return jsonify({'password_updated': result})