From 4ebbffb8989f990ff7c3dd0d737140d7c3a031d6 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Fri, 22 May 2026 17:18:06 -0700 Subject: [PATCH] Fix admin PIN after profile switches Reset profile PIN dialog controls each time it opens so stale profile-specific event listeners cannot submit an admin PIN against a previously selected profile. Keep failed PIN attempts retryable and restrict launch-lock verification to the admin profile PIN only, so non-admin profile PINs cannot mark the admin lock as verified. --- web_server.py | 15 ++++++++++----- webui/static/init.js | 21 ++++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/web_server.py b/web_server.py index 3966f93c..deea944a 100644 --- a/web_server.py +++ b/web_server.py @@ -24315,9 +24315,10 @@ def select_profile(): return jsonify({'success': False, 'error': 'Invalid PIN'}), 401 session['profile_id'] = profile_id - # If PIN was just validated, also mark launch PIN as verified - # so the subsequent page reload doesn't ask again - if pin: + # If the admin PIN was just validated, also mark launch PIN as + # verified so the subsequent page reload doesn't ask again. A + # non-admin profile PIN must not unlock the admin launch lock. + if pin and profile_id == 1: session['launch_pin_verified'] = True return jsonify({'success': True, 'profile': profile}) except Exception as e: @@ -24402,12 +24403,16 @@ def reset_pin_via_credential(): # Credential verified — clear PIN for the requested profile (default: admin) database = get_database() - target_profile = data.get('profile_id', 1) + try: + target_profile = int(data.get('profile_id', 1)) + except (TypeError, ValueError): + target_profile = 1 database.update_profile(target_profile, pin_hash=None) # If clearing admin PIN, also disable launch lock if target_profile == 1: config_manager.set('security.require_pin_on_launch', False) - session['launch_pin_verified'] = True + if target_profile == 1: + session['launch_pin_verified'] = True return jsonify({'success': True, 'message': 'PIN cleared. You can set a new PIN in Settings.'}) except Exception as e: diff --git a/webui/static/init.js b/webui/static/init.js index 3a0cf496..0465f70d 100644 --- a/webui/static/init.js +++ b/webui/static/init.js @@ -707,8 +707,19 @@ function showPinDialog(profile) { const dialog = document.getElementById('profile-pin-dialog'); const avatar = document.getElementById('profile-pin-avatar'); const nameEl = document.getElementById('profile-pin-name'); - const input = document.getElementById('profile-pin-input'); const errorEl = document.getElementById('profile-pin-error'); + const oldInput = document.getElementById('profile-pin-input'); + const oldSubmit = document.getElementById('profile-pin-submit'); + const oldCancel = document.getElementById('profile-pin-cancel'); + + // Replace controls on every open so stale listeners from a previous + // profile cannot submit the new PIN against the old profile id. + const input = oldInput.cloneNode(true); + const submit = oldSubmit.cloneNode(true); + const cancel = oldCancel.cloneNode(true); + oldInput.parentNode.replaceChild(input, oldInput); + oldSubmit.parentNode.replaceChild(submit, oldSubmit); + oldCancel.parentNode.replaceChild(cancel, oldCancel); renderProfileAvatar(avatar, profile); nameEl.textContent = profile.name; @@ -718,13 +729,12 @@ function showPinDialog(profile) { dialog.style.display = 'flex'; setTimeout(() => input.focus(), 100); - const submit = document.getElementById('profile-pin-submit'); - const cancel = document.getElementById('profile-pin-cancel'); - const wasSwitching = !!currentProfile; const handleSubmit = async () => { const pin = input.value; if (!pin) return; + submit.disabled = true; + submit.textContent = 'Verifying...'; try { const res = await fetch('/api/profiles/select', { method: 'POST', @@ -753,7 +763,8 @@ function showPinDialog(profile) { errorEl.textContent = 'Connection error'; errorEl.style.display = ''; } - cleanup(); + submit.disabled = false; + submit.textContent = 'Submit'; }; const handleCancel = () => {