diff options
| author | 2025-09-15 22:17:14 +0200 | |
|---|---|---|
| committer | 2025-09-15 22:17:14 +0200 | |
| commit | ddb51c0e95074c6fbddade547ca267801177bb01 (patch) | |
| tree | b036f516b8c437f6cc83a1b4ed721b5cdc79688d | |
| parent | 38b7daedf70e38e5953e3d4b7bf6c764a1c9c4e0 (diff) | |
Fix another user self-delete regression (#7877)
Regression from #7763
Earlier regression which was fixed before #7626
In addition:
* get rid of `data-toggle` (refactor)
* show invalid login message if deleting account and entered incorrect password instead of redirect to 403
* remove unused reference to `r` parameter
* `forgetOpenCategories()` on login not on any crypto form
| -rw-r--r-- | app/Controllers/userController.php | 9 | ||||
| -rw-r--r-- | app/views/auth/formLogin.phtml | 6 | ||||
| -rw-r--r-- | app/views/auth/reauth.phtml | 8 | ||||
| -rw-r--r-- | app/views/auth/register.phtml | 2 | ||||
| -rw-r--r-- | app/views/feed/add.phtml | 2 | ||||
| -rw-r--r-- | app/views/helpers/feed/update.phtml | 2 | ||||
| -rw-r--r-- | app/views/subscription/add.phtml | 2 | ||||
| -rw-r--r-- | app/views/user/details.phtml | 2 | ||||
| -rw-r--r-- | app/views/user/manage.phtml | 2 | ||||
| -rw-r--r-- | app/views/user/profile.phtml | 29 | ||||
| -rw-r--r-- | p/scripts/extra.js | 153 |
11 files changed, 105 insertions, 112 deletions
diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 1f4452523..e71c8aaa0 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -635,13 +635,16 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { $username, FreshRSS_Context::userConf()->passwordHash, $nonce, $challenge ); + if (!$ok) { + Minz_Request::bad(_t('feedback.auth.login.invalid'), ['c' => 'user', 'a' => 'profile']); + return; + } } elseif (self::reauthRedirect()) { return; } - if ($ok) { - $ok &= self::deleteUser($username); - } + $ok &= self::deleteUser($username); + if ($ok && $self_deletion) { FreshRSS_Auth::removeAccess(); $redirect_url = ['c' => 'index', 'a' => 'index']; diff --git a/app/views/auth/formLogin.phtml b/app/views/auth/formLogin.phtml index d72d0b1b3..23fceeb88 100644 --- a/app/views/auth/formLogin.phtml +++ b/app/views/auth/formLogin.phtml @@ -11,7 +11,7 @@ </div> <?php } ?> - <form id="crypto-form" method="post" action="<?= _url('auth', 'login') ?>"> + <form class="crypto-form" method="post" action="<?= _url('auth', 'login') ?>"> <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <input type="hidden" name="original_request" value="<?= Minz_Url::serialize(Minz_Request::originalRequest())?>" /> @@ -24,8 +24,8 @@ <div class="form-group"> <label for="passwordPlain"><?= _t('gen.auth.password') ?></label> <div class="stick"> - <input type="password" id="passwordPlain" required="required" /> - <button type="button" class="btn toggle-password" data-toggle="passwordPlain"><?= _i('key') ?></button> + <input type="password" id="passwordPlain" class="passwordPlain" required="required" /> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <input type="hidden" id="challenge" name="challenge" /> <noscript><strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> diff --git a/app/views/auth/reauth.phtml b/app/views/auth/reauth.phtml index d5b75decd..b48f89e5f 100644 --- a/app/views/auth/reauth.phtml +++ b/app/views/auth/reauth.phtml @@ -6,14 +6,14 @@ <main class="prompt"> <h1><?= _t('gen.auth.reauth.header') ?></h1> - <form id="crypto-form" method="post"> + <form class="crypto-form" method="post"> <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <input type="hidden" id="username" value="<?= Minz_User::name() ?>" /> <div class="form-group"> <label for="passwordPlain"><?= _t('gen.auth.password') ?></label> <div class="stick"> - <input type="password" id="passwordPlain" required="required" /> - <button type="button" class="btn toggle-password" data-toggle="passwordPlain"><?= _i('key') ?></button> + <input type="password" id="passwordPlain" class="passwordPlain" required="required" /> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <input type="hidden" id="challenge" name="challenge" /> <noscript><strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> @@ -23,7 +23,7 @@ ?> <p class="help"><?= _i('help') ?> <?= _t('gen.auth.reauth.tip', intval($reauth_time / 60)) ?></p> <div class="form-group form-group-actions"> - <button id="loginButton" type="submit" class="btn btn-important" disabled="disabled"> + <button type="submit" class="btn btn-important" disabled="disabled"> <?= _t('gen.auth.login') ?> </button> </div> diff --git a/app/views/auth/register.phtml b/app/views/auth/register.phtml index daaf08b8c..5787b36fc 100644 --- a/app/views/auth/register.phtml +++ b/app/views/auth/register.phtml @@ -50,7 +50,7 @@ <label for="new_user_passwordPlain"><?= _t('gen.auth.password') ?></label> <div class="stick"> <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" required="required" autocomplete="new-password" pattern=".{7,}" /> - <button type="button" class="btn toggle-password" data-toggle="new_user_passwordPlain"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> <p class="help"><?= _i('help') ?> <?= _t('gen.auth.password.format') ?></p> diff --git a/app/views/feed/add.phtml b/app/views/feed/add.phtml index ceec0d7b6..a30ce19a1 100644 --- a/app/views/feed/add.phtml +++ b/app/views/feed/add.phtml @@ -83,7 +83,7 @@ <div class="group-controls"> <div class="stick"> <input type="password" name="http_pass" id="http_pass" value="<?= $auth['password'] ?>" autocomplete="new-password" /> - <button type="button" class="btn toggle-password" data-toggle="http_pass"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> </div> </div> diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index c363532f5..e95400953 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -231,7 +231,7 @@ <div class="stick w50"> <input type="password" name="http_pass_feed<?= $this->feed->id() ?>" id="http_pass_feed<?= $this->feed->id() ?>" value="<?= $auth['password'] ?>" autocomplete="new-password" /> - <button type="button" class="btn toggle-password" data-toggle="http_pass_feed<?= $this->feed->id() ?>"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> </div> </div> diff --git a/app/views/subscription/add.phtml b/app/views/subscription/add.phtml index 3e5d6d08b..a822b4440 100644 --- a/app/views/subscription/add.phtml +++ b/app/views/subscription/add.phtml @@ -277,7 +277,7 @@ <div class="group-controls"> <div class="stick"> <input id="http_pass" name="http_pass" type="password" value="" autocomplete="new-password" /> - <button type="button" class="btn toggle-password" data-toggle="http_pass"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> </div> </div> diff --git a/app/views/user/details.phtml b/app/views/user/details.phtml index 20ab4d804..648bbfd27 100644 --- a/app/views/user/details.phtml +++ b/app/views/user/details.phtml @@ -61,7 +61,7 @@ <div class="stick"> <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?= cryptAvailable() && Minz_User::name() !== $this->username ? '' : 'disabled="disabled" ' ?>/> - <button type="button" class="btn toggle-password" data-toggle="newPasswordPlain"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <p class="help"><?= _i('help'); ?> <?= _t('admin.user.password_format') ?></p> </div> diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml index d416d0c0c..2c2354b3a 100644 --- a/app/views/user/manage.phtml +++ b/app/views/user/manage.phtml @@ -71,7 +71,7 @@ <div class="group-controls"> <div class="stick"> <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" autocomplete="new-password" pattern=".{7,}" /> - <button type="button" class="btn toggle-password" data-toggle="new_user_passwordPlain"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <p class="help"><?= _i('help') ?> <?= _t('admin.user.password_format') ?></p> <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml index d49147c59..f55eead4d 100644 --- a/app/views/user/profile.phtml +++ b/app/views/user/profile.phtml @@ -7,7 +7,7 @@ ?> <main class="post"> - <form id="crypto-form" method="post" action="<?= _url('user', 'profile') ?>" data-auto-leave-validation="1"> + <form class="crypto-form" method="post" action="<?= _url('user', 'profile') ?>" data-auto-leave-validation="1"> <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <h1><?= _t('conf.profile') ?></h1> @@ -58,12 +58,12 @@ <details class="form-advanced" data-challenge-if-not-empty="1"<?= $open ? ' open="open"' : ''?>> <summary class="form-advanced-title"><?= _t('conf.profile.change_password') ?></summary> <div class="form-group"> - <label class="group-name" for="passwordPlain"><?= _t('conf.profile.current_password') ?></label> + <label class="group-name" for="currentPasswordPlain"><?= _t('conf.profile.current_password') ?></label> <div class="group-controls"> <input type="hidden" id="username" value="<?= Minz_User::name() ?? '' ?>" /> <div class="stick"> - <input type="password" id="passwordPlain" /> - <button type="button" class="btn toggle-password" data-toggle="passwordPlain"><img class="icon" src="../themes/icons/key.svg" loading="lazy" alt="ð"></button> + <input type="password" id="currentPasswordPlain" class="passwordPlain" /> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <noscript> @@ -77,10 +77,10 @@ <div class="group-controls"> <div class="stick"> <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="new-password" pattern=".{7,}" /> - <button type="button" class="btn toggle-password" data-toggle="newPasswordPlain"><img class="icon" src="../themes/icons/key.svg" loading="lazy" alt="ð"></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <p class="help"> - <img class="icon" src="../themes/icons/help.svg" loading="lazy" alt="â¹ïž"> <?= _t('conf.profile.password_format') ?> + <?= _i('help') ?> <?= _t('conf.profile.password_format') ?> </p> </div> </div> @@ -89,7 +89,7 @@ <div class="group-controls"> <div class="stick"> <input type="password" id="confirmPasswordPlain" name="confirmPasswordPlain" autocomplete="new-password" pattern=".{7,}" /> - <button type="button" class="btn toggle-password" data-toggle="confirmPasswordPlain"><img class="icon" src="../themes/icons/key.svg" loading="lazy" alt="ð"></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> </div> </div> @@ -120,7 +120,7 @@ placeholder="<?= _t('conf.profile.api.api_not_set') ?>" <?php } ?> pattern=".{7,}" <?= cryptAvailable() ? '' : 'disabled="disabled" ' ?>/> - <button type="button" class="btn toggle-password" data-toggle="apiPasswordPlain"><?= _i('key') ?></button> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <p class="help"><?= _i('help') ?> <?= _t('conf.profile.api.check_link', Minz_Url::display('/api/', 'html', true)) ?></p> <p class="help"><?= _i('help') ?> <?= _t('conf.profile.api.documentation_link') ?></p> @@ -146,7 +146,7 @@ <?php if (!FreshRSS_Auth::hasAccess('admin')) { ?> <h2><?= _t('conf.profile.delete') ?></h2> - <form id="crypto-form" method="post" action="<?= _url('user', 'delete') ?>"> + <form class="crypto-form" method="post" action="<?= _url('user', 'delete') ?>"> <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('conf.profile.delete.warn') ?></p> @@ -155,8 +155,8 @@ <label class="group-name" for="passwordPlain"><?= _t('gen.auth.password') ?></label> <div class="group-controls"> <div class="stick"> - <input type="password" id="passwordPlain" required="required" /> - <button type="button" class="btn toggle-password" data-toggle="passwordPlain"><?= _i('key') ?></button> + <input type="password" id="passwordPlain" class="passwordPlain" required="required" /> + <button type="button" class="btn toggle-password"><?= _i('key') ?></button> </div> <input type="hidden" id="challenge" name="challenge" /><br /> <noscript><strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> @@ -165,13 +165,6 @@ <div class="form-group form-actions"> <div class="group-controls"> - <?php - $redirect_url = urlencode(Minz_Url::display( - ['c' => 'user', 'a' => 'profile'], - 'php', true - )); - ?> - <input type="hidden" name="r" value="<?= $redirect_url ?>" /> <input type="hidden" name="username" id="username" value="<?= Minz_User::name() ?>" /> <button type="submit" class="btn btn-attention confirm"><?= _t('gen.action.remove') ?></button> </div> diff --git a/p/scripts/extra.js b/p/scripts/extra.js index 8d5b960c8..48f50325b 100644 --- a/p/scripts/extra.js +++ b/p/scripts/extra.js @@ -16,114 +16,107 @@ function forgetOpenCategories() { localStorage.removeItem('FreshRSS_open_categories'); } -function init_crypto_form() { - /* globals bcrypt */ - const crypto_form = document.getElementById('crypto-form'); - if (!crypto_form) { - return; - } - +function init_crypto_forms() { if (!(window.bcrypt)) { if (window.console) { console.log('FreshRSS waiting for bcrypt.jsâŠ'); } - setTimeout(init_crypto_form, 100); + setTimeout(init_crypto_forms, 100); return; } - forgetOpenCategories(); - - const submit_button = crypto_form.querySelector('[type="submit"]'); - if (submit_button) { - submit_button.disabled = false; - } + /* globals bcrypt */ + const crypto_forms = document.querySelectorAll('.crypto-form'); + crypto_forms.forEach(crypto_form => { + const submit_button = crypto_form.querySelector('[type="submit"]'); + if (submit_button) { + submit_button.disabled = false; + } - crypto_form.onsubmit = function (e) { - let challenge = crypto_form.querySelector('#challenge'); - if (!challenge) { - crypto_form.querySelectorAll('[data-challenge-if-not-empty] input[type="password"]').forEach(el => { - if (el.value !== '' && !challenge) { - crypto_form.insertAdjacentHTML('beforeend', '<input type="hidden" id="challenge" name="challenge" />'); - challenge = crypto_form.querySelector('#challenge'); - } - }); + crypto_form.onsubmit = function (e) { + let challenge = crypto_form.querySelector('#challenge'); if (!challenge) { - return true; + crypto_form.querySelectorAll('[data-challenge-if-not-empty] input[type="password"]').forEach(el => { + if (el.value !== '' && !challenge) { + crypto_form.insertAdjacentHTML('beforeend', '<input type="hidden" id="challenge" name="challenge" />'); + challenge = crypto_form.querySelector('#challenge'); + } + }); + if (!challenge) { + return true; + } } - } - e.preventDefault(); + e.preventDefault(); - if (!submit_button) { - return false; - } - submit_button.disabled = true; + if (!submit_button) { + return false; + } + submit_button.disabled = true; - const req = new XMLHttpRequest(); - req.open('GET', './?c=javascript&a=nonce&user=' + document.getElementById('username').value, true); + const req = new XMLHttpRequest(); + req.open('GET', './?c=javascript&a=nonce&user=' + crypto_form.querySelector('#username').value, true); - req.onerror = function () { - openNotification('Communication error!', 'bad'); - submit_button.disabled = false; - }; + req.onerror = function () { + openNotification('Communication error!', 'bad'); + submit_button.disabled = false; + }; - req.onload = function () { - if (req.status == 200) { - const json = xmlHttpRequestJson(req); - if (!json.salt1 || !json.nonce) { - openNotification('Invalid user!', 'bad'); - } else { - try { - const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'); - const s = bcrypt.hashSync(document.getElementById('passwordPlain').value, json.salt1); - const c = bcrypt.hashSync(json.nonce + s, strong ? bcrypt.genSaltSync(4) : poormanSalt()); - challenge.value = c; - if (!s || !c) { - openNotification('Crypto error!', 'bad'); - } else { - crypto_form.removeEventListener('submit', crypto_form.onsubmit); - crypto_form.submit(); + req.onload = function () { + if (req.status == 200) { + const json = xmlHttpRequestJson(req); + if (!json.salt1 || !json.nonce) { + openNotification('Invalid user!', 'bad'); + } else { + try { + const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'); + const s = bcrypt.hashSync(crypto_form.querySelector('.passwordPlain').value, json.salt1); + const c = bcrypt.hashSync(json.nonce + s, strong ? bcrypt.genSaltSync(4) : poormanSalt()); + challenge.value = c; + if (!s || !c) { + openNotification('Crypto error!', 'bad'); + } else { + crypto_form.removeEventListener('submit', crypto_form.onsubmit); + crypto_form.submit(); + } + } catch (ex) { + openNotification('Crypto exception! ' + ex, 'bad'); } - } catch (ex) { - openNotification('Crypto exception! ' + ex, 'bad'); } + } else { + req.onerror(); } - } else { - req.onerror(); - } - submit_button.disabled = false; - }; + submit_button.disabled = false; + }; - req.send(); - }; + req.send(); + }; + }); } // </crypto form (Web login)> // <show password> -let timeoutHide; - -function showPW_this() { - const id_passwordField = this.getAttribute('data-toggle'); - if (this.classList.contains('active')) { - hidePW(id_passwordField); +function togglePW(btn) { + if (btn.classList.contains('active')) { + hidePW(btn); } else { - showPW(id_passwordField); + showPW(btn); } return false; } -function showPW(id_passwordField) { - const passwordField = document.getElementById(id_passwordField); +function showPW(btn) { + const passwordField = btn.previousElementSibling; passwordField.setAttribute('type', 'text'); - passwordField.nextElementSibling.classList.add('active'); - clearTimeout(timeoutHide); - timeoutHide = setTimeout(function () { hidePW(id_passwordField); }, 5000); + btn.classList.add('active'); + clearTimeout(btn.timeoutHide); + btn.timeoutHide = setTimeout(function () { hidePW(btn); }, 5000); return false; } -function hidePW(id_passwordField) { - clearTimeout(timeoutHide); - const passwordField = document.getElementById(id_passwordField); +function hidePW(btn) { + clearTimeout(btn.timeoutHide); + const passwordField = btn.previousElementSibling; passwordField.setAttribute('type', 'password'); passwordField.nextElementSibling.classList.remove('active'); return false; @@ -131,7 +124,7 @@ function hidePW(id_passwordField) { function init_password_observers(parent) { parent.querySelectorAll('.toggle-password').forEach(function (btn) { - btn.addEventListener('click', showPW_this); + btn.onclick = () => togglePW(btn); }); } // </show password> @@ -500,8 +493,12 @@ function init_extra_afterDOM() { setTimeout(init_extra_afterDOM, 50); return; } + const loginButton = document.querySelector('#loginButton'); + if (loginButton) { + loginButton.addEventListener('click', forgetOpenCategories); + } if (!['normal', 'global', 'reader'].includes(context.current_view)) { - init_crypto_form(); + init_crypto_forms(); init_password_observers(document.body); init_select_observers(); init_configuration_alert(); |
