mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-14 18:23:52 -04:00
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
This commit is contained in:
@@ -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'];
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (ex) {
|
||||
openNotification('Crypto exception! ' + ex, 'bad');
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
req.onerror();
|
||||
}
|
||||
submit_button.disabled = false;
|
||||
};
|
||||
|
||||
req.send();
|
||||
};
|
||||
e.preventDefault();
|
||||
|
||||
if (!submit_button) {
|
||||
return false;
|
||||
}
|
||||
submit_button.disabled = 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.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');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
req.onerror();
|
||||
}
|
||||
submit_button.disabled = false;
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user