Login: Show oauth error messages inline (#72255)

Squashed commit of the following:

commit b82ed875b88d12348cd4043e2add493e8fb6edc6
Author: Roxana Turc <anamaria-roxana.turc@grafana.com>
Date:   Tue Jul 25 10:42:31 2023 +0100

    user essentials mob! 🔱

    lastFile:public/app/core/components/Login/LoginPage.test.tsx

commit 923cbcf190d2def57b59f8b231f14322efdf9718
Author: joshhunt <josh@trtr.co>
Date:   Tue Jul 25 10:17:51 2023 +0100

    user essentials mob! 🔱

Co-authored-by: joshhunt <josh@trtr.co>
Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
Co-authored-by: L-M-K-B <48948963+L-M-K-B@users.noreply.github.com>
This commit is contained in:
RoxanaAnamariaTurc 2023-07-25 10:57:45 +01:00 committed by GitHub
parent e9ba6922c0
commit edb7d0e0d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 32 additions and 14 deletions

View File

@ -79,7 +79,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
disableUserSignUp = false;
loginHint = '';
passwordHint = '';
loginError = undefined;
loginError: string | undefined = undefined;
viewersCanEdit = false;
editorsCanAdmin = false;
disableSanitizeHtml = false;

View File

@ -1,8 +1,6 @@
import React, { PureComponent } from 'react';
import { AppEvents } from '@grafana/data';
import { FetchError, getBackendSrv, isFetchError } from '@grafana/runtime';
import appEvents from 'app/core/app_events';
import config from 'app/core/config';
import { t } from 'app/core/internationalization';
@ -53,11 +51,8 @@ export class LoginCtrl extends PureComponent<Props, State> {
isLoggingIn: false,
isChangingPassword: false,
showDefaultPasswordWarning: false,
loginErrorMessage: config.loginError,
};
if (config.loginError) {
appEvents.emit(AppEvents.alertWarning, ['Login Failed', config.loginError]);
}
}
changePassword = (password: string) => {

View File

@ -105,6 +105,16 @@ describe('Login Page', () => {
expect(screen.getByRole('link', { name: 'Sign in with Okta Test' })).toBeInTheDocument();
});
it('shows oauth errors', async () => {
runtimeMock.config.loginError = 'Oh no there was an error :(';
render(<LoginPage />);
const alert = await screen.findByRole('alert', { name: 'Login failed' });
expect(alert).toBeInTheDocument();
expect(alert).toHaveTextContent('Oh no there was an error :(');
});
it('shows an error with incorrect password', async () => {
postMock.mockRejectedValueOnce({
data: {
@ -122,7 +132,9 @@ describe('Login Page', () => {
await userEvent.type(screen.getByLabelText('Password input field'), 'test');
await userEvent.click(screen.getByRole('button', { name: 'Login button' }));
expect(await screen.findByRole('alert', { name: 'Invalid username or password' })).toBeInTheDocument();
const alert = await screen.findByRole('alert', { name: 'Login failed' });
expect(alert).toBeInTheDocument();
expect(alert).toHaveTextContent('Invalid username or password');
});
it('shows a different error with failed login attempts', async () => {
@ -142,10 +154,10 @@ describe('Login Page', () => {
await userEvent.type(screen.getByLabelText('Password input field'), 'test');
await userEvent.click(screen.getByRole('button', { name: 'Login button' }));
expect(
await screen.findByRole('alert', {
name: 'You have exceeded the number of login attempts for this user. Please try again later.',
})
).toBeInTheDocument();
const alert = await screen.findByRole('alert', { name: 'Login failed' });
expect(alert).toBeInTheDocument();
expect(alert).toHaveTextContent(
'You have exceeded the number of login attempts for this user. Please try again later.'
);
});
});

View File

@ -6,6 +6,7 @@ import React from 'react';
import { Alert, HorizontalGroup, LinkButton } from '@grafana/ui';
import { Branding } from 'app/core/components/Branding/Branding';
import config from 'app/core/config';
import { t } from 'app/core/internationalization';
import { ChangePassword } from '../ForgottenPassword/ChangePassword';
@ -44,7 +45,11 @@ export const LoginPage = () => {
<LoginLayout isChangingPassword={isChangingPassword}>
{!isChangingPassword && (
<InnerBox>
{loginErrorMessage && <Alert className={alertStyles} severity="error" title={loginErrorMessage} />}
{loginErrorMessage && (
<Alert className={alertStyles} severity="error" title={t('login.error.title', 'Login failed')}>
{loginErrorMessage}
</Alert>
)}
{!disableLoginForm && (
<LoginForm onSubmit={login} loginHint={loginHint} passwordHint={passwordHint} isLoggingIn={isLoggingIn}>

View File

@ -254,6 +254,7 @@
"error": {
"blocked": "",
"invalid-user-or-password": "",
"title": "",
"unknown": ""
}
},

View File

@ -254,6 +254,7 @@
"error": {
"blocked": "You have exceeded the number of login attempts for this user. Please try again later.",
"invalid-user-or-password": "Invalid username or password",
"title": "Login failed",
"unknown": "Unknown error occurred"
}
},

View File

@ -259,6 +259,7 @@
"error": {
"blocked": "",
"invalid-user-or-password": "",
"title": "",
"unknown": ""
}
},

View File

@ -259,6 +259,7 @@
"error": {
"blocked": "",
"invalid-user-or-password": "",
"title": "",
"unknown": ""
}
},

View File

@ -254,6 +254,7 @@
"error": {
"blocked": "Ÿőū ĥävę ęχčęęđęđ ŧĥę ʼnūmþęř őƒ ľőģįʼn äŧŧęmpŧş ƒőř ŧĥįş ūşęř. Pľęäşę ŧřy äģäįʼn ľäŧęř.",
"invalid-user-or-password": "Ĩʼnväľįđ ūşęřʼnämę őř päşşŵőřđ",
"title": "Ŀőģįʼn ƒäįľęđ",
"unknown": "Ůʼnĸʼnőŵʼn ęřřőř őččūřřęđ"
}
},

View File

@ -249,6 +249,7 @@
"error": {
"blocked": "",
"invalid-user-or-password": "",
"title": "",
"unknown": ""
}
},