aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Inverle <inverle@proton.me> 2025-09-15 22:17:14 +0200
committerGravatar GitHub <noreply@github.com> 2025-09-15 22:17:14 +0200
commitddb51c0e95074c6fbddade547ca267801177bb01 (patch)
treeb036f516b8c437f6cc83a1b4ed721b5cdc79688d
parent38b7daedf70e38e5953e3d4b7bf6c764a1c9c4e0 (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.php9
-rw-r--r--app/views/auth/formLogin.phtml6
-rw-r--r--app/views/auth/reauth.phtml8
-rw-r--r--app/views/auth/register.phtml2
-rw-r--r--app/views/feed/add.phtml2
-rw-r--r--app/views/helpers/feed/update.phtml2
-rw-r--r--app/views/subscription/add.phtml2
-rw-r--r--app/views/user/details.phtml2
-rw-r--r--app/views/user/manage.phtml2
-rw-r--r--app/views/user/profile.phtml29
-rw-r--r--p/scripts/extra.js153
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();