mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
fix(auth): render password reset links outside login error text (#8289)
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
This commit is contained in:
1 parent
54251fd2fc
commit
bcb4aaf4ae
6 files changed
+50
-6
No files matched your search
@@ -49,6 +49,7 @@ Cacti CHANGELOG
|
||||
-issue#8120: Do not replicate repository and tooling metadata files (.gitignore, .htaccess.dist, .mdl_style.rb, .mdlrc) to remote data collectors during file replication
|
||||
-issue#8116: Restore legacy unique_keys[] support in db_table_create() and db_update_table() so plugins that declare UNIQUE indexes with that definition style keep them across a schema refresh
|
||||
-issue#8105: Add a Force Uninstall action to remove an orphaned plugin whose directory is missing
|
||||
-issue#8073: Render a separate password reset link on local login errors while keeping error text escaped
|
||||
-issue#7969: Restore excluded integration and mutation regression coverage
|
||||
-issue#7969: Align Rocky Linux compatibility checks with the supported PHP runtime
|
||||
-issue#8063: Support searching by graph ID on the Graphs list/preview and Aggregate Graphs pages
|
||||
|
||||
+1
-1
@@ -434,4 +434,4 @@ html_auth_footer('login', $error_message, "
|
||||
|
||||
});
|
||||
</script>
|
||||
");
|
||||
", $error && $frv_realm <= 1);
|
||||
@@ -161,7 +161,7 @@ final class LocalAuthLoginProvider implements CredentialLoginProviderInterface {
|
||||
|
||||
if (!$error) {
|
||||
$error = true;
|
||||
$error_msg = __('Access Denied! Login Failed.') . ' <a href="auth_resetpassword.php">' . __('Reset password') . '</a>';
|
||||
$error_msg = __('Access Denied! Login Failed.');
|
||||
|
||||
cacti_log(sprintf('LOGIN FAILED: Local Login Failed for user %s from IP Address %s', $username, get_client_addr()), false, 'AUTH');
|
||||
}
|
||||
|
||||
+8
-4
@@ -3638,13 +3638,14 @@ function html_auth_header(string $section, string $browser_title, string $legend
|
||||
* including any error messages, version information, and additional HTML content.
|
||||
* It also triggers a plugin hook and includes the global session file.
|
||||
*
|
||||
* @param string $section The section identifier used for the plugin hook.
|
||||
* @param string $error Optional. The error message to display. Default is an empty string.
|
||||
* @param string $html Optional. Additional HTML content to include. Default is an empty string.
|
||||
* @param string $section The section identifier used for the plugin hook.
|
||||
* @param string $error Optional. The error message to display. Default is an empty string.
|
||||
* @param string $html Optional. Additional HTML content to include. Default is an empty string.
|
||||
* @param bool $password_reset Show the local password-reset link separately from the error text.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function html_auth_footer(string $section, string $error = '', string $html = '') : void {
|
||||
function html_auth_footer(string $section, string $error = '', string $html = '', bool $password_reset = false) : void {
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
@@ -3653,6 +3654,9 @@ function html_auth_footer(string $section, string $error = '', string $html = ''
|
||||
<hr />
|
||||
<div class='cactiAuthErrors'>
|
||||
<?php print htmle($error); ?>
|
||||
<?php if ($password_reset) { ?>
|
||||
<a href='<?php print html_escape_attr(cacti_url('auth_resetpassword.php')); ?>'><?php print __esc('Reset password'); ?></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class='versionInfo'>
|
||||
<?php print __('Version %s | %s', CACTI_VERSION_BRIEF, COPYRIGHT_YEARS_SHORT); ?>
|
||||
|
||||
@@ -25459,3 +25459,11 @@ msgstr ""
|
||||
#: layouts.php
|
||||
msgid "Click 'Continue' to make the following Layouts Local to you."
|
||||
msgstr ""
|
||||
|
||||
#: lib/html.php:3658
|
||||
msgid "Reset password"
|
||||
msgstr ""
|
||||
|
||||
#: lib/Auth/LocalAuthLoginProvider.php:164
|
||||
msgid "Access Denied! Login Failed."
|
||||
msgstr ""
|
||||
@@ -15,6 +15,37 @@
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
for (const scenario of [
|
||||
{ name: 'wrong password', username: 'admin', password: 'wrong-fixture-password' },
|
||||
{ name: 'unknown user', username: 'missingfixtureuser', password: 'wrong-fixture-password' },
|
||||
{ name: 'empty password', username: 'admin', password: '' },
|
||||
]) {
|
||||
test(`local login ${scenario.name} renders a working reset link`, async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page.locator('.cactiAuthErrors a')).toHaveCount(0);
|
||||
if (await page.locator('#realm').count()) {
|
||||
await page.locator('#realm').selectOption({ label: 'Local' });
|
||||
}
|
||||
await page.locator('#login_username').fill(scenario.username);
|
||||
await page.locator('#login_password').fill(scenario.password);
|
||||
if (!scenario.password) {
|
||||
await page.locator('#login_password').evaluate((input: HTMLInputElement) => { input.required = false; });
|
||||
}
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('form#auth input[type="submit"], form#auth button[type="submit"]').click(),
|
||||
]);
|
||||
const errors = page.locator('.cactiAuthErrors');
|
||||
await expect(errors).not.toContainText('<a');
|
||||
const reset = errors.getByRole('link', { name: 'Reset password', exact: true });
|
||||
await expect(reset).toHaveCount(1);
|
||||
await expect(reset).toHaveAttribute('href', /auth_resetpassword\.php$/);
|
||||
await reset.click();
|
||||
await expect(page).toHaveURL(/auth_resetpassword\.php$/);
|
||||
await expect(page.locator('form#auth')).toBeVisible();
|
||||
});
|
||||
}
|
||||
|
||||
test('login page renders form controls without browser errors', async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
|
||||
|
||||
Reference in new issue
Block a user