diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-12-11 11:51:09 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-12-17 20:08:01 +0000 |
commit | 30287fb9c01be104a84a48cc0d42fe4f99f12684 (patch) | |
tree | 536acaa91a09b3dba60c9aa84a63db08dadeaab4 /server | |
parent | 5997c86172976ca177723ca2807375ce6b80b7eb (diff) | |
download | sonarqube-30287fb9c01be104a84a48cc0d42fe4f99f12684.tar.gz sonarqube-30287fb9c01be104a84a48cc0d42fe4f99f12684.zip |
SONAR-8714 Make authorization errors more noticeable
Diffstat (limited to 'server')
6 files changed, 105 insertions, 46 deletions
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Login.css b/server/sonar-web/src/main/js/apps/sessions/components/Login.css index 7ee05ac9186..32cdebc5b6d 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/Login.css +++ b/server/sonar-web/src/main/js/apps/sessions/components/Login.css @@ -19,12 +19,12 @@ */ .login-page { padding-top: 10vh; + max-width: 300px; + margin: 0 auto; } .login-title { - margin-bottom: 40px; line-height: 1.5; font-size: 24px; font-weight: 300; - text-align: center; } diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx index 0b3d32cbd30..25d4dbd4ff4 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx @@ -18,30 +18,57 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { connect } from 'react-redux'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer'; +import { Store } from '../../../store/rootReducer'; import './Login.css'; import LoginForm from './LoginForm'; import OAuthProviders from './OAuthProviders'; -interface Props { +export interface LoginProps { + authorizationError?: boolean; + authenticationError?: boolean; identityProviders: T.IdentityProvider[]; onSubmit: (login: string, password: string) => Promise<void>; returnTo: string; } -export default function Login({ identityProviders, onSubmit, returnTo }: Props) { +export function Login(props: LoginProps) { + const { authorizationError, authenticationError, identityProviders, returnTo } = props; + const displayError = authorizationError || authenticationError; + return ( <div className="login-page" id="login_form"> - <h1 className="login-title text-center">{translate('login.login_to_sonarqube')}</h1> + <h1 className="login-title text-center huge-spacer-bottom"> + {translate('login.login_to_sonarqube')} + </h1> - <GlobalMessagesContainer /> + {displayError && ( + <Alert className="huge-spacer-bottom" display="block" variant="error"> + {translate('login.unauthorized_access_alert')} + </Alert> + )} {identityProviders.length > 0 && ( <OAuthProviders identityProviders={identityProviders} returnTo={returnTo} /> )} - <LoginForm collapsed={identityProviders.length > 0} onSubmit={onSubmit} returnTo={returnTo} /> + <LoginForm + collapsed={identityProviders.length > 0} + onSubmit={props.onSubmit} + returnTo={returnTo} + /> + + <GlobalMessagesContainer /> </div> ); } + +const mapStateToProps = (state: Store) => ({ + authorizationError: state.appState.authorizationError, + authenticationError: state.appState.authenticationError +}); + +export default connect(mapStateToProps)(Login); diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx index 556d930ede1..bdd88e5e5c0 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx @@ -19,23 +19,30 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import Login from '../Login'; +import { Login, LoginProps } from '../Login'; -const identityProvider = { - backgroundColor: '#000', - iconPath: '/some/path', - key: 'foo', - name: 'foo' -}; - -it('logs in with form alone', () => { - const wrapper = shallow(<Login identityProviders={[]} onSubmit={jest.fn()} returnTo="" />); - expect(wrapper).toMatchSnapshot(); +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('with identity providers'); + expect(shallowRender({ identityProviders: [] })).toMatchSnapshot( + 'without any identity providers' + ); + expect(shallowRender({ authorizationError: true })).toMatchSnapshot('with authorization error'); }); -it('logs in with identity provider', () => { - const wrapper = shallow( - <Login identityProviders={[identityProvider]} onSubmit={jest.fn()} returnTo="" /> +function shallowRender(props: Partial<LoginProps> = {}) { + return shallow<LoginProps>( + <Login + identityProviders={[ + { + backgroundColor: '#000', + iconPath: '/some/path', + key: 'foo', + name: 'foo' + } + ]} + onSubmit={jest.fn()} + returnTo="" + {...props} + /> ); - expect(wrapper).toMatchSnapshot(); -}); +} diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap index 9319b5a2187..d09b2373fa9 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap @@ -1,35 +1,54 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`logs in with form alone 1`] = ` +exports[`should render correctly: with authorization error 1`] = ` <div className="login-page" id="login_form" > <h1 - className="login-title text-center" + className="login-title text-center huge-spacer-bottom" > login.login_to_sonarqube </h1> - <Connect(GlobalMessages) /> + <Alert + className="huge-spacer-bottom" + display="block" + variant="error" + > + login.unauthorized_access_alert + </Alert> + <OAuthProviders + identityProviders={ + Array [ + Object { + "backgroundColor": "#000", + "iconPath": "/some/path", + "key": "foo", + "name": "foo", + }, + ] + } + returnTo="" + /> <LoginForm - collapsed={false} + collapsed={true} onSubmit={[MockFunction]} returnTo="" /> + <Connect(GlobalMessages) /> </div> `; -exports[`logs in with identity provider 1`] = ` +exports[`should render correctly: with identity providers 1`] = ` <div className="login-page" id="login_form" > <h1 - className="login-title text-center" + className="login-title text-center huge-spacer-bottom" > login.login_to_sonarqube </h1> - <Connect(GlobalMessages) /> <OAuthProviders identityProviders={ Array [ @@ -48,5 +67,25 @@ exports[`logs in with identity provider 1`] = ` onSubmit={[MockFunction]} returnTo="" /> + <Connect(GlobalMessages) /> +</div> +`; + +exports[`should render correctly: without any identity providers 1`] = ` +<div + className="login-page" + id="login_form" +> + <h1 + className="login-title text-center huge-spacer-bottom" + > + login.login_to_sonarqube + </h1> + <LoginForm + collapsed={false} + onSubmit={[MockFunction]} + returnTo="" + /> + <Connect(GlobalMessages) /> </div> `; diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginContainer-test.tsx.snap index dd0cc38ae08..cbd6e21d99a 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginContainer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginContainer-test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly 1`] = ` -<Login +<Connect(Login) identityProviders={ Array [ Object { diff --git a/server/sonar-web/src/main/js/store/globalMessages.ts b/server/sonar-web/src/main/js/store/globalMessages.ts index 87cccf03f55..57394c8d366 100644 --- a/server/sonar-web/src/main/js/store/globalMessages.ts +++ b/server/sonar-web/src/main/js/store/globalMessages.ts @@ -19,7 +19,6 @@ */ import { uniqueId } from 'lodash'; import { Dispatch } from 'redux'; -import { requireAuthorization } from './appState'; import { ActionType } from './utils/actions'; enum MessageLevel { @@ -48,8 +47,7 @@ export function closeAllGlobalMessages() { type Action = | ActionType<typeof addGlobalMessageActionCreator, 'ADD_GLOBAL_MESSAGE'> | ActionType<typeof closeGlobalMessage, 'CLOSE_GLOBAL_MESSAGE'> - | ActionType<typeof closeAllGlobalMessages, 'CLOSE_ALL_GLOBAL_MESSAGES'> - | ActionType<typeof requireAuthorization, 'REQUIRE_AUTHORIZATION'>; + | ActionType<typeof closeAllGlobalMessages, 'CLOSE_ALL_GLOBAL_MESSAGES'>; function addGlobalMessage(message: string, level: MessageLevel) { return (dispatch: Dispatch) => { @@ -74,18 +72,6 @@ export default function(state: State = [], action: Action): State { case 'ADD_GLOBAL_MESSAGE': return [{ id: action.id, message: action.message, level: action.level }]; - case 'REQUIRE_AUTHORIZATION': - // FIXME l10n - return [ - { - id: uniqueId('global-message-'), - message: - 'You are not authorized to access this page. ' + - 'Please log in with more privileges and try again.', - level: MessageLevel.Error - } - ]; - case 'CLOSE_GLOBAL_MESSAGE': return state.filter(message => message.id !== action.id); |