path="account/reset_password"
component={lazyLoadComponent(() => import('../components/ResetPassword'))}
/>
+ <Route
+ // We don't want this route to have any menu. This is why we define it here
+ // rather than under the admin routes.
+ path="admin/change_admin_password"
+ component={lazyLoadComponent(() =>
+ import('../../apps/change-admin-password/ChangeAdminPasswordApp')
+ )}
+ />
<Route
path="not_found"
component={lazyLoadComponent(() => import('../components/NotFound'))}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { changePassword } from '../../api/users';
+import { Location, withRouter } from '../../components/hoc/withRouter';
+import { getAppState, Store } from '../../store/rootReducer';
+import ChangeAdminPasswordAppRenderer from './ChangeAdminPasswordAppRenderer';
+import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from './constants';
+
+interface Props {
+ canAdmin?: boolean;
+ instanceUsesDefaultAdminCredentials?: boolean;
+ location: Location;
+}
+
+interface State {
+ passwordValue: string;
+ confirmPasswordValue: string;
+ canSubmit?: boolean;
+ submitting: boolean;
+ success: boolean;
+}
+
+export class ChangeAdminPasswordApp extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ constructor(props: Props) {
+ super(props);
+
+ this.state = {
+ passwordValue: '',
+ confirmPasswordValue: '',
+ submitting: false,
+ success: !props.instanceUsesDefaultAdminCredentials
+ };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handlePasswordChange = (passwordValue: string) => {
+ this.setState({ passwordValue }, this.checkCanSubmit);
+ };
+
+ handleConfirmPasswordChange = (confirmPasswordValue: string) => {
+ this.setState({ confirmPasswordValue }, this.checkCanSubmit);
+ };
+
+ handleSubmit = async () => {
+ const { canSubmit, passwordValue } = this.state;
+ if (canSubmit) {
+ this.setState({ submitting: true });
+ const success = await changePassword({
+ login: DEFAULT_ADMIN_LOGIN,
+ password: passwordValue
+ }).then(
+ () => true,
+ () => false
+ );
+ if (this.mounted) {
+ this.setState({ submitting: false, success });
+ }
+ }
+ };
+
+ checkCanSubmit = () => {
+ this.setState(({ passwordValue, confirmPasswordValue }) => ({
+ canSubmit: passwordValue === confirmPasswordValue && passwordValue !== DEFAULT_ADMIN_PASSWORD
+ }));
+ };
+
+ render() {
+ const { canAdmin, location } = this.props;
+ const { canSubmit, confirmPasswordValue, passwordValue, submitting, success } = this.state;
+ return (
+ <ChangeAdminPasswordAppRenderer
+ canAdmin={canAdmin}
+ passwordValue={passwordValue}
+ confirmPasswordValue={confirmPasswordValue}
+ canSubmit={canSubmit}
+ onPasswordChange={this.handlePasswordChange}
+ onConfirmPasswordChange={this.handleConfirmPasswordChange}
+ onSubmit={this.handleSubmit}
+ submitting={submitting}
+ success={success}
+ location={location}
+ />
+ );
+ }
+}
+
+export const mapStateToProps = (state: Store) => {
+ const { canAdmin, instanceUsesDefaultAdminCredentials } = getAppState(state);
+ return { canAdmin, instanceUsesDefaultAdminCredentials };
+};
+
+export default connect(mapStateToProps)(withRouter(ChangeAdminPasswordApp));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
+import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getReturnUrl } from 'sonar-ui-common/helpers/urls';
+import GlobalMessagesContainer from '../../app/components/GlobalMessagesContainer';
+import { Location } from '../../components/hoc/withRouter';
+import Unauthorized from '../sessions/components/Unauthorized';
+import { DEFAULT_ADMIN_PASSWORD } from './constants';
+
+export interface ChangeAdminPasswordAppRendererProps {
+ passwordValue: string;
+ confirmPasswordValue: string;
+ onConfirmPasswordChange: (password: string) => void;
+ onPasswordChange: (password: string) => void;
+ onSubmit: () => void;
+ canAdmin?: boolean;
+ canSubmit?: boolean;
+ submitting: boolean;
+ success: boolean;
+ location: Location;
+}
+
+export default function ChangeAdminPasswordAppRenderer(props: ChangeAdminPasswordAppRendererProps) {
+ const {
+ canAdmin,
+ canSubmit,
+ confirmPasswordValue,
+ passwordValue,
+ location,
+ submitting,
+ success
+ } = props;
+
+ if (!canAdmin) {
+ return <Unauthorized />;
+ }
+
+ return (
+ <div className="page-wrapper-simple">
+ <div className="page-simple">
+ {success ? (
+ <Alert variant="success">
+ <p className="spacer-bottom">{translate('users.change_admin_password.form.success')}</p>
+ {/* We must not use Link here, because we need a refresh of the /api/navigation/global call. */}
+ <a href={getReturnUrl(location)}>
+ {translate('users.change_admin_password.form.continue_to_app')}
+ </a>
+ </Alert>
+ ) : (
+ <>
+ <GlobalMessagesContainer />
+
+ <h1 className="text-center bg-danger big padded">
+ {translate('users.change_admin_password.instance_is_at_risk')}
+ </h1>
+ <p className="text-center huge huge-spacer-top">
+ {translate('users.change_admin_password.header')}
+ </p>
+ <p className="text-center huge-spacer-top huge-spacer-bottom">
+ {translate('users.change_admin_password.description')}
+ </p>
+
+ <form
+ className="text-center"
+ onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
+ e.preventDefault();
+ props.onSubmit();
+ }}>
+ <h2 className="big-spacer-bottom big">
+ {translate('users.change_admin_password.form.header')}
+ </h2>
+
+ <MandatoryFieldsExplanation className="form-field" />
+
+ <div className="form-field">
+ <label htmlFor="user-password">
+ {translate('users.change_admin_password.form.password')}
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="user-password"
+ name="password"
+ onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
+ props.onPasswordChange(e.currentTarget.value);
+ }}
+ required={true}
+ type="password"
+ value={passwordValue}
+ />
+ </div>
+
+ <div className="form-field">
+ <label htmlFor="confirm-user-password">
+ {translate('users.change_admin_password.form.confirm')}
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="confirm-user-password"
+ name="confirm-password"
+ onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
+ props.onConfirmPasswordChange(e.currentTarget.value);
+ }}
+ required={true}
+ type="password"
+ value={confirmPasswordValue}
+ />
+
+ {confirmPasswordValue === passwordValue &&
+ passwordValue === DEFAULT_ADMIN_PASSWORD && (
+ <Alert className="spacer-top" variant="warning">
+ {translate('users.change_admin_password.form.cannot_use_default_password')}
+ </Alert>
+ )}
+ </div>
+
+ <div className="form-field">
+ <SubmitButton disabled={!canSubmit || submitting}>
+ {translate('change_verb')}
+ {submitting && <i className="spinner spacer-left" />}
+ </SubmitButton>
+ </div>
+ </form>
+ </>
+ )}
+ </div>
+ </div>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import { changePassword } from '../../../api/users';
+import { mockLocation } from '../../../helpers/testMocks';
+import { getAppState, Store } from '../../../store/rootReducer';
+import { ChangeAdminPasswordApp, mapStateToProps } from '../ChangeAdminPasswordApp';
+import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from '../constants';
+
+jest.mock('react-redux', () => ({
+ connect: jest.fn(() => (a: any) => a)
+}));
+
+jest.mock('../../../store/rootReducer', () => ({
+ ...jest.requireActual('../../../store/rootReducer'),
+ getAppState: jest.fn()
+}));
+
+jest.mock('../../../api/users', () => ({
+ changePassword: jest.fn().mockResolvedValue(null)
+}));
+
+beforeEach(jest.clearAllMocks);
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ instanceUsesDefaultAdminCredentials: undefined })).toMatchSnapshot(
+ 'admin is not using the default password'
+ );
+});
+
+it('should correctly handle input changes', () => {
+ const wrapper = shallowRender();
+
+ // Set different values; should not allow submission.
+ wrapper.instance().handlePasswordChange('new pass');
+ wrapper.instance().handleConfirmPasswordChange('confirm pass');
+
+ expect(wrapper.state().passwordValue).toBe('new pass');
+ expect(wrapper.state().confirmPasswordValue).toBe('confirm pass');
+ expect(wrapper.state().canSubmit).toBe(false);
+
+ // Set the same values; should allow submission.
+ wrapper.instance().handleConfirmPasswordChange('new pass');
+ expect(wrapper.state().canSubmit).toBe(true);
+
+ // Set the default admin password; should not allow submission.
+ wrapper.instance().handlePasswordChange(DEFAULT_ADMIN_PASSWORD);
+ expect(wrapper.state().canSubmit).toBe(false);
+});
+
+it('should correctly update the password', async () => {
+ (changePassword as jest.Mock).mockResolvedValueOnce(null).mockRejectedValueOnce(null);
+ const wrapper = shallowRender();
+ wrapper.setState({ canSubmit: false, passwordValue: 'new pass' });
+
+ wrapper.instance().handleSubmit();
+ expect(wrapper.state().submitting).toBe(false);
+ expect(changePassword).not.toBeCalled();
+
+ wrapper.setState({ canSubmit: true });
+ wrapper.instance().handleSubmit();
+ expect(wrapper.state().submitting).toBe(true);
+
+ await waitAndUpdate(wrapper);
+
+ expect(wrapper.state().submitting).toBe(false);
+ expect(wrapper.state().success).toBe(true);
+ expect(changePassword).toBeCalledWith({
+ login: DEFAULT_ADMIN_LOGIN,
+ password: 'new pass'
+ });
+
+ wrapper.instance().handleSubmit();
+ expect(wrapper.state().submitting).toBe(true);
+
+ await waitAndUpdate(wrapper);
+
+ expect(wrapper.state().submitting).toBe(false);
+ expect(wrapper.state().success).toBe(false);
+});
+
+describe('redux', () => {
+ it('should correctly map state props', () => {
+ (getAppState as jest.Mock)
+ .mockReturnValueOnce({})
+ .mockReturnValueOnce({ canAdmin: false, instanceUsesDefaultAdminCredentials: true });
+
+ expect(mapStateToProps({} as Store)).toEqual({
+ canAdmin: undefined,
+ instanceUsesDefaultAdminCredentials: undefined
+ });
+
+ expect(mapStateToProps({} as Store)).toEqual({
+ canAdmin: false,
+ instanceUsesDefaultAdminCredentials: true
+ });
+ });
+});
+
+function shallowRender(props: Partial<ChangeAdminPasswordApp['props']> = {}) {
+ return shallow<ChangeAdminPasswordApp>(
+ <ChangeAdminPasswordApp
+ canAdmin={true}
+ instanceUsesDefaultAdminCredentials={true}
+ location={mockLocation()}
+ {...props}
+ />
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { change, submit } from 'sonar-ui-common/helpers/testUtils';
+import { mockLocation } from '../../../helpers/testMocks';
+import ChangeAdminPasswordAppRenderer, {
+ ChangeAdminPasswordAppRendererProps
+} from '../ChangeAdminPasswordAppRenderer';
+import { DEFAULT_ADMIN_PASSWORD } from '../constants';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ canAdmin: false })).toMatchSnapshot('access denied');
+ expect(shallowRender({ canSubmit: false })).toMatchSnapshot('cannot submit');
+ expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting');
+ expect(
+ shallowRender({
+ passwordValue: DEFAULT_ADMIN_PASSWORD,
+ confirmPasswordValue: DEFAULT_ADMIN_PASSWORD
+ })
+ ).toMatchSnapshot('trying to use default admin password');
+ expect(shallowRender({ success: true })).toMatchSnapshot('success');
+});
+
+it('should correctly react to input changes', () => {
+ const onConfirmPasswordChange = jest.fn();
+ const onPasswordChange = jest.fn();
+ const wrapper = shallowRender({ onConfirmPasswordChange, onPasswordChange });
+
+ change(wrapper.find('#user-password'), 'new pass');
+ change(wrapper.find('#confirm-user-password'), 'confirm pass');
+ expect(onPasswordChange).toBeCalledWith('new pass');
+ expect(onConfirmPasswordChange).toBeCalledWith('confirm pass');
+});
+
+it('should correctly submit the form', () => {
+ const onSubmit = jest.fn();
+ const wrapper = shallowRender({ onSubmit });
+
+ submit(wrapper.find('form'));
+ expect(onSubmit).toBeCalled();
+});
+
+function shallowRender(props: Partial<ChangeAdminPasswordAppRendererProps> = {}) {
+ return shallow<ChangeAdminPasswordAppRendererProps>(
+ <ChangeAdminPasswordAppRenderer
+ canAdmin={true}
+ canSubmit={true}
+ passwordValue="password"
+ confirmPasswordValue="confirm"
+ onConfirmPasswordChange={jest.fn()}
+ onPasswordChange={jest.fn()}
+ onSubmit={jest.fn()}
+ submitting={false}
+ success={false}
+ location={mockLocation()}
+ {...props}
+ />
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: admin is not using the default password 1`] = `
+<ChangeAdminPasswordAppRenderer
+ canAdmin={true}
+ confirmPasswordValue=""
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {},
+ "search": "",
+ "state": Object {},
+ }
+ }
+ onConfirmPasswordChange={[Function]}
+ onPasswordChange={[Function]}
+ onSubmit={[Function]}
+ passwordValue=""
+ submitting={false}
+ success={true}
+/>
+`;
+
+exports[`should render correctly: default 1`] = `
+<ChangeAdminPasswordAppRenderer
+ canAdmin={true}
+ confirmPasswordValue=""
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {},
+ "search": "",
+ "state": Object {},
+ }
+ }
+ onConfirmPasswordChange={[Function]}
+ onPasswordChange={[Function]}
+ onSubmit={[Function]}
+ passwordValue=""
+ submitting={false}
+ success={false}
+/>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: access denied 1`] = `<Unauthorized />`;
+
+exports[`should render correctly: cannot submit 1`] = `
+<div
+ className="page-wrapper-simple"
+>
+ <div
+ className="page-simple"
+ >
+ <Connect(GlobalMessages) />
+ <h1
+ className="text-center bg-danger big padded"
+ >
+ users.change_admin_password.instance_is_at_risk
+ </h1>
+ <p
+ className="text-center huge huge-spacer-top"
+ >
+ users.change_admin_password.header
+ </p>
+ <p
+ className="text-center huge-spacer-top huge-spacer-bottom"
+ >
+ users.change_admin_password.description
+ </p>
+ <form
+ className="text-center"
+ onSubmit={[Function]}
+ >
+ <h2
+ className="big-spacer-bottom big"
+ >
+ users.change_admin_password.form.header
+ </h2>
+ <MandatoryFieldsExplanation
+ className="form-field"
+ />
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="user-password"
+ >
+ users.change_admin_password.form.password
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="user-password"
+ name="password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="password"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="confirm-user-password"
+ >
+ users.change_admin_password.form.confirm
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="confirm-user-password"
+ name="confirm-password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="confirm"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <SubmitButton
+ disabled={true}
+ >
+ change_verb
+ </SubmitButton>
+ </div>
+ </form>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: default 1`] = `
+<div
+ className="page-wrapper-simple"
+>
+ <div
+ className="page-simple"
+ >
+ <Connect(GlobalMessages) />
+ <h1
+ className="text-center bg-danger big padded"
+ >
+ users.change_admin_password.instance_is_at_risk
+ </h1>
+ <p
+ className="text-center huge huge-spacer-top"
+ >
+ users.change_admin_password.header
+ </p>
+ <p
+ className="text-center huge-spacer-top huge-spacer-bottom"
+ >
+ users.change_admin_password.description
+ </p>
+ <form
+ className="text-center"
+ onSubmit={[Function]}
+ >
+ <h2
+ className="big-spacer-bottom big"
+ >
+ users.change_admin_password.form.header
+ </h2>
+ <MandatoryFieldsExplanation
+ className="form-field"
+ />
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="user-password"
+ >
+ users.change_admin_password.form.password
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="user-password"
+ name="password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="password"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="confirm-user-password"
+ >
+ users.change_admin_password.form.confirm
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="confirm-user-password"
+ name="confirm-password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="confirm"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <SubmitButton
+ disabled={false}
+ >
+ change_verb
+ </SubmitButton>
+ </div>
+ </form>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: submitting 1`] = `
+<div
+ className="page-wrapper-simple"
+>
+ <div
+ className="page-simple"
+ >
+ <Connect(GlobalMessages) />
+ <h1
+ className="text-center bg-danger big padded"
+ >
+ users.change_admin_password.instance_is_at_risk
+ </h1>
+ <p
+ className="text-center huge huge-spacer-top"
+ >
+ users.change_admin_password.header
+ </p>
+ <p
+ className="text-center huge-spacer-top huge-spacer-bottom"
+ >
+ users.change_admin_password.description
+ </p>
+ <form
+ className="text-center"
+ onSubmit={[Function]}
+ >
+ <h2
+ className="big-spacer-bottom big"
+ >
+ users.change_admin_password.form.header
+ </h2>
+ <MandatoryFieldsExplanation
+ className="form-field"
+ />
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="user-password"
+ >
+ users.change_admin_password.form.password
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="user-password"
+ name="password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="password"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="confirm-user-password"
+ >
+ users.change_admin_password.form.confirm
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="confirm-user-password"
+ name="confirm-password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="confirm"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <SubmitButton
+ disabled={true}
+ >
+ change_verb
+ <i
+ className="spinner spacer-left"
+ />
+ </SubmitButton>
+ </div>
+ </form>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: success 1`] = `
+<div
+ className="page-wrapper-simple"
+>
+ <div
+ className="page-simple"
+ >
+ <Alert
+ variant="success"
+ >
+ <p
+ className="spacer-bottom"
+ >
+ users.change_admin_password.form.success
+ </p>
+ <a
+ href="/"
+ >
+ users.change_admin_password.form.continue_to_app
+ </a>
+ </Alert>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: trying to use default admin password 1`] = `
+<div
+ className="page-wrapper-simple"
+>
+ <div
+ className="page-simple"
+ >
+ <Connect(GlobalMessages) />
+ <h1
+ className="text-center bg-danger big padded"
+ >
+ users.change_admin_password.instance_is_at_risk
+ </h1>
+ <p
+ className="text-center huge huge-spacer-top"
+ >
+ users.change_admin_password.header
+ </p>
+ <p
+ className="text-center huge-spacer-top huge-spacer-bottom"
+ >
+ users.change_admin_password.description
+ </p>
+ <form
+ className="text-center"
+ onSubmit={[Function]}
+ >
+ <h2
+ className="big-spacer-bottom big"
+ >
+ users.change_admin_password.form.header
+ </h2>
+ <MandatoryFieldsExplanation
+ className="form-field"
+ />
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="user-password"
+ >
+ users.change_admin_password.form.password
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="user-password"
+ name="password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="admin"
+ />
+ </div>
+ <div
+ className="form-field"
+ >
+ <label
+ htmlFor="confirm-user-password"
+ >
+ users.change_admin_password.form.confirm
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ id="confirm-user-password"
+ name="confirm-password"
+ onChange={[Function]}
+ required={true}
+ type="password"
+ value="admin"
+ />
+ <Alert
+ className="spacer-top"
+ variant="warning"
+ >
+ users.change_admin_password.form.cannot_use_default_password
+ </Alert>
+ </div>
+ <div
+ className="form-field"
+ >
+ <SubmitButton
+ disabled={false}
+ >
+ change_verb
+ </SubmitButton>
+ </div>
+ </form>
+ </div>
+</div>
+`;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+export const DEFAULT_ADMIN_LOGIN = 'admin';
+export const DEFAULT_ADMIN_PASSWORD = 'admin';
canAdmin?: boolean;
edition: 'community' | 'developer' | 'enterprise' | 'datacenter' | undefined;
globalPages?: Extension[];
+ instanceUsesDefaultAdminCredentials?: boolean;
multipleAlmEnabled?: boolean;
needIssueSync?: boolean;
productionDatabase: boolean;
users.enter_token_name=Enter Token Name
users.tokens.new_token_created=New token "{0}" has been created. Make sure you copy it now, you won't be able to see it again!
users.generate_new_token=Generate New Token
-
+users.change_admin_password.instance_is_at_risk=Secure your SonarQube instance
+users.change_admin_password.header=Default Administrator credentials are still used
+users.change_admin_password.description=Your SonarQube instance is still using default administrator credentials. You must change the password for the 'admin' account to secure your SonarQube instance.
+users.change_admin_password.form.header=Change the password for user 'admin'
+users.change_admin_password.form.password=New password for user 'admin'
+users.change_admin_password.form.confirm=Confirm password for user 'admin'
+users.change_admin_password.form.cannot_use_default_password=You must choose a password that is different from the default password.
+users.change_admin_password.form.success=The admin user's password was successfully changed.
+users.change_admin_password.form.continue_to_app=Continue to SonarQube
#------------------------------------------------------------------------------
#