.navbar-context-branches .popup {
min-width: 430px;
+ max-width: 650px;
}
.navbar-context-meta-branch-menu-title {
}
.navbar-context-meta-branch-menu-item-name {
- flex: 0 1 300px; /* Workaround for SONAR-10971 */
+ flex: 0 1 550px; /* Workaround for SONAR-10971 */
min-width: 0;
}
color: rgba(68, 68, 68, 0.3);
}
+.horizontal-pipe-separator {
+ display: flex;
+ align-items: center;
+ margin-top: calc(4 * var(--gridSize));
+ margin-bottom: calc(4 * var(--gridSize));
+}
+
+.horizontal-pipe-separator > .horizontal-separator {
+ margin: 0 4px;
+}
+
+.horizontal-separator {
+ min-width: 16px;
+ height: 1px;
+ flex-grow: 1;
+ background-color: var(--barBorderColor);
+}
+
.vertical-separator {
width: 1px;
min-height: 16px;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+.sonarcloud-login-alert {
+ margin: 10vh auto 5vh auto;
+ width: 256px;
+}
+
.sonarcloud-login-page {
margin-top: 15vh;
width: 216px;
padding: calc(4 * var(--gridSize)) 20px;
}
+.sonarcloud-login-alert ~ .sonarcloud-login-page {
+ margin-top: 0;
+}
+
.sonarcloud-login-page-large {
width: 300px;
}
.sonarcloud-oauth-providers.oauth-providers .oauth-providers-help {
right: -22px;
}
+
+.sonarcloud-login-cancel {
+ text-align: center;
+}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { connect } from 'react-redux';
import * as classNames from 'classnames';
import LoginForm from './LoginForm';
import OAuthProviders from './OAuthProviders';
import { getBaseUrl } from '../../../helpers/urls';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { Alert } from '../../../components/ui/Alert';
import './LoginSonarCloud.css';
+import { Store } from '../../../store/rootReducer';
interface Props {
identityProviders: T.IdentityProvider[];
onSubmit: (login: string, password: string) => Promise<void>;
returnTo: string;
showForm?: boolean;
+ authorizationError?: boolean;
+ authenticationError?: boolean;
}
-export default function LoginSonarCloud({
+function formatLabel(name: string) {
+ return translateWithParameters('login.with_x', name);
+}
+
+export function LoginSonarCloud({
+ showForm,
identityProviders,
- onSubmit,
returnTo,
- showForm
+ onSubmit,
+ authorizationError,
+ authenticationError
}: Props) {
const displayForm = showForm || identityProviders.length <= 0;
+ const displayErrorAction = authorizationError || authenticationError;
return (
- <div
- className={classNames('sonarcloud-login-page boxed-group boxed-group-inner', {
- 'sonarcloud-login-page-large': displayForm
- })}
- id="login_form">
- <div className="text-center">
- <img
- alt="SonarCloud logo"
- height={36}
- src={`${getBaseUrl()}/images/sonarcloud-square-logo.svg`}
- width={36}
- />
- <h1 className="sonarcloud-login-title">
- {translate('login.login_or_signup_to_sonarcloud')}
- </h1>
- </div>
-
- {displayForm ? (
- <LoginForm onSubmit={onSubmit} returnTo={returnTo} />
- ) : (
- <OAuthProviders
- className="sonarcloud-oauth-providers"
- formatLabel={formatLabel}
- identityProviders={identityProviders}
- returnTo={returnTo}
- />
+ <>
+ {displayErrorAction && (
+ <Alert className="sonarcloud-login-alert" display="block" variant="warning">
+ {translate('login.unauthorized_access_alert')}
+ </Alert>
)}
- </div>
+ <div
+ className={classNames('sonarcloud-login-page boxed-group boxed-group-inner', {
+ 'sonarcloud-login-page-large': displayForm
+ })}
+ id="login_form">
+ <div className="text-center">
+ <img
+ alt="SonarCloud logo"
+ height={36}
+ src={`${getBaseUrl()}/images/sonarcloud-square-logo.svg`}
+ width={36}
+ />
+ <h1 className="sonarcloud-login-title">
+ {translate('login.login_or_signup_to_sonarcloud')}
+ </h1>
+ </div>
+
+ {displayForm ? (
+ <LoginForm onSubmit={onSubmit} returnTo={returnTo} />
+ ) : (
+ <OAuthProviders
+ className="sonarcloud-oauth-providers"
+ formatLabel={formatLabel}
+ identityProviders={identityProviders}
+ returnTo={returnTo}
+ />
+ )}
+
+ {displayErrorAction && (
+ <div className="sonarcloud-login-cancel">
+ <div className="horizontal-pipe-separator">
+ <div className="horizontal-separator" />
+ <span className="note">{translate('or')}</span>
+ <div className="horizontal-separator" />
+ </div>
+ <a href={`${getBaseUrl()}/`}>{translate('go_back_to_homepage')}</a>
+ </div>
+ )}
+ </div>
+ </>
);
}
-function formatLabel(name: string) {
- return translateWithParameters('login.with_x', name);
-}
+const mapStateToProps = (state: Store) => ({
+ authorizationError: state.appState.authorizationError,
+ authenticationError: state.appState.authenticationError
+});
+
+export default connect(mapStateToProps)(LoginSonarCloud);
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import LoginSonarCloud from '../LoginSonarCloud';
+import { LoginSonarCloud } from '../LoginSonarCloud';
const identityProvider = {
backgroundColor: '#000',
shallow(<LoginSonarCloud identityProviders={[]} onSubmit={jest.fn()} returnTo="" />)
).toMatchSnapshot();
});
+
+it("shows an warning message if there's an authorization error", () => {
+ const wrapper = shallow(
+ <LoginSonarCloud
+ authorizationError={true}
+ identityProviders={[identityProvider]}
+ onSubmit={jest.fn()}
+ returnTo=""
+ />
+ );
+ expect(wrapper).toMatchSnapshot();
+});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`logs in with identity provider 1`] = `
-<div
- className="sonarcloud-login-page boxed-group boxed-group-inner"
- id="login_form"
->
+<Fragment>
<div
- className="text-center"
+ className="sonarcloud-login-page boxed-group boxed-group-inner"
+ id="login_form"
>
- <img
- alt="SonarCloud logo"
- height={36}
- src="/images/sonarcloud-square-logo.svg"
- width={36}
- />
- <h1
- className="sonarcloud-login-title"
+ <div
+ className="text-center"
>
- login.login_or_signup_to_sonarcloud
- </h1>
+ <img
+ alt="SonarCloud logo"
+ height={36}
+ src="/images/sonarcloud-square-logo.svg"
+ width={36}
+ />
+ <h1
+ className="sonarcloud-login-title"
+ >
+ login.login_or_signup_to_sonarcloud
+ </h1>
+ </div>
+ <OAuthProviders
+ className="sonarcloud-oauth-providers"
+ formatLabel={[Function]}
+ identityProviders={
+ Array [
+ Object {
+ "backgroundColor": "#000",
+ "iconPath": "/some/path",
+ "key": "foo",
+ "name": "foo",
+ },
+ ]
+ }
+ returnTo=""
+ />
</div>
- <OAuthProviders
- className="sonarcloud-oauth-providers"
- formatLabel={[Function]}
- identityProviders={
- Array [
- Object {
- "backgroundColor": "#000",
- "iconPath": "/some/path",
- "key": "foo",
- "name": "foo",
- },
- ]
- }
- returnTo=""
- />
-</div>
+</Fragment>
`;
exports[`logs in with simple form 1`] = `
-<div
- className="sonarcloud-login-page boxed-group boxed-group-inner sonarcloud-login-page-large"
- id="login_form"
->
+<Fragment>
<div
- className="text-center"
+ className="sonarcloud-login-page boxed-group boxed-group-inner sonarcloud-login-page-large"
+ id="login_form"
>
- <img
- alt="SonarCloud logo"
- height={36}
- src="/images/sonarcloud-square-logo.svg"
- width={36}
- />
- <h1
- className="sonarcloud-login-title"
+ <div
+ className="text-center"
>
- login.login_or_signup_to_sonarcloud
- </h1>
+ <img
+ alt="SonarCloud logo"
+ height={36}
+ src="/images/sonarcloud-square-logo.svg"
+ width={36}
+ />
+ <h1
+ className="sonarcloud-login-title"
+ >
+ login.login_or_signup_to_sonarcloud
+ </h1>
+ </div>
+ <LoginForm
+ onSubmit={[MockFunction]}
+ returnTo=""
+ />
</div>
- <LoginForm
- onSubmit={[MockFunction]}
- returnTo=""
- />
-</div>
+</Fragment>
`;
exports[`logs in with simple form 2`] = `
-<div
- className="sonarcloud-login-page boxed-group boxed-group-inner sonarcloud-login-page-large"
- id="login_form"
->
+<Fragment>
<div
- className="text-center"
+ className="sonarcloud-login-page boxed-group boxed-group-inner sonarcloud-login-page-large"
+ id="login_form"
>
- <img
- alt="SonarCloud logo"
- height={36}
- src="/images/sonarcloud-square-logo.svg"
- width={36}
+ <div
+ className="text-center"
+ >
+ <img
+ alt="SonarCloud logo"
+ height={36}
+ src="/images/sonarcloud-square-logo.svg"
+ width={36}
+ />
+ <h1
+ className="sonarcloud-login-title"
+ >
+ login.login_or_signup_to_sonarcloud
+ </h1>
+ </div>
+ <LoginForm
+ onSubmit={[MockFunction]}
+ returnTo=""
+ />
+ </div>
+</Fragment>
+`;
+
+exports[`shows an warning message if there's an authorization error 1`] = `
+<Fragment>
+ <Alert
+ className="sonarcloud-login-alert"
+ display="block"
+ variant="warning"
+ >
+ login.unauthorized_access_alert
+ </Alert>
+ <div
+ className="sonarcloud-login-page boxed-group boxed-group-inner"
+ id="login_form"
+ >
+ <div
+ className="text-center"
+ >
+ <img
+ alt="SonarCloud logo"
+ height={36}
+ src="/images/sonarcloud-square-logo.svg"
+ width={36}
+ />
+ <h1
+ className="sonarcloud-login-title"
+ >
+ login.login_or_signup_to_sonarcloud
+ </h1>
+ </div>
+ <OAuthProviders
+ className="sonarcloud-oauth-providers"
+ formatLabel={[Function]}
+ identityProviders={
+ Array [
+ Object {
+ "backgroundColor": "#000",
+ "iconPath": "/some/path",
+ "key": "foo",
+ "name": "foo",
+ },
+ ]
+ }
+ returnTo=""
/>
- <h1
- className="sonarcloud-login-title"
+ <div
+ className="sonarcloud-login-cancel"
>
- login.login_or_signup_to_sonarcloud
- </h1>
+ <div
+ className="horizontal-pipe-separator"
+ >
+ <div
+ className="horizontal-separator"
+ />
+ <span
+ className="note"
+ >
+ or
+ </span>
+ <div
+ className="horizontal-separator"
+ />
+ </div>
+ <a
+ href="/"
+ >
+ go_back_to_homepage
+ </a>
+ </div>
</div>
- <LoginForm
- onSubmit={[MockFunction]}
- returnTo=""
- />
-</div>
+</Fragment>
`;
<form id="generate-secret-key-form" onSubmit={this.handleSubmit}>
<p className="spacer-bottom">
<FormattedMessage
- defaultMessage={translate('encryptionFormattedMessage.secret_key_description')}
+ defaultMessage={translate('encryption.secret_key_description')}
id="encryption.secret_key_description"
values={{
moreInformationLink: (
login.login_or_signup_to_sonarcloud=Log in or Sign up to SonarCloud
login.login_with_x=Log in with {0}
login.more_options=More options
+login.unauthorized_access_alert=You are not authorized to access this page. Please log in with more privileges and try again.
login.with_x=With {0}
unauthorized.message=You're not authorized to access this page. Please contact the administrator.
encryption.secret_key_description=Secret key is required to be able to encrypt properties. {moreInformationLink}
encryption.secret_key=Secret Key
encryption.how_to_use=How To Use
-encryption.how_to_use.content=<ul><li>Store the secret key in the file <code>~/.sonar/sonar-secret.txt</code> of the server. This file can be relocated by defining the property <code>sonar.secretKeyPath</code> in <code>conf/sonar.properties</code></li><li>Restrict access to this file by making it readable and by owner only</li><li>Restart the server if the property <code>sonar.secretKeyPath</code> has been set or changed.</li><li>Copy this file on all the machines that execute code inspection. Define the property <code>sonar.secretKeyPath</code> on those machines if the path is not <code>~/.sonar/sonar-secret.txt</code>.</li><li>For each property that you want to encrypt, generate the encrypted value and replace the original value wherever it is stored (configuration files, command lines).</li></ul>
+encryption.how_to_use.content=<ul><li>Store the secret key in the file <code>~/.sonar/sonar-secret.txt</code> of the server. This file can be relocated by defining the property <code>sonar.secretKeyPath</code> in <code>conf/sonar.properties</code></li><li>Restrict access to this file by making it readable and by owner only</li><li>Restart the server if the property <code>sonar.secretKeyPath</code> has been set or changed.</li><li>For each property that you want to encrypt, generate the encrypted value and replace the original value wherever it is stored (configuration files, command lines).</li></ul>
#------------------------------------------------------------------------------