'/admin/background_tasks',
'/admin/groups',
'/admin/users',
+ '/admin/settings/encryption',
];
export default function GlobalContainer() {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { LargeCenteredLayout, PageContentFontWrapper, Spinner, Title } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { checkSecretKey, generateSecretKey } from '../../../api/settings';
-import Spinner from '../../../components/ui/Spinner';
import { translate } from '../../../helpers/l10n';
import EncryptionForm from './EncryptionForm';
import GenerateSecretKeyForm from './GenerateSecretKeyForm';
render() {
const { loading, secretKey, secretKeyAvailable } = this.state;
+
return (
- <div className="page page-limited" id="encryption-page">
- <Helmet defer={false} title={translate('property.category.security.encryption')} />
- <header className="page-header">
- <h1 className="page-title">{translate('property.category.security.encryption')}</h1>
- <Spinner loading={loading} />
- </header>
+ <LargeCenteredLayout id="encryption-page">
+ <PageContentFontWrapper className="sw-my-8 sw-body-sm">
+ <Helmet defer={false} title={translate('property.category.security.encryption')} />
+ <header>
+ <Title>{translate('property.category.security.encryption')}</Title>
+ <Spinner loading={loading} />
+ </header>
- {!loading && !secretKeyAvailable && (
- <GenerateSecretKeyForm generateSecretKey={this.generateSecretKey} secretKey={secretKey} />
- )}
+ {!loading && !secretKeyAvailable && (
+ <GenerateSecretKeyForm
+ generateSecretKey={this.generateSecretKey}
+ secretKey={secretKey}
+ />
+ )}
- {secretKeyAvailable && <EncryptionForm generateSecretKey={this.generateSecretKey} />}
- </div>
+ {secretKeyAvailable && <EncryptionForm generateSecretKey={this.generateSecretKey} />}
+ </PageContentFontWrapper>
+ </LargeCenteredLayout>
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ ButtonPrimary,
+ ButtonSecondary,
+ ClipboardIconButton,
+ CodeSnippet,
+ InputTextArea,
+ Spinner,
+} from 'design-system';
import * as React from 'react';
+import { useCallback, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { encryptValue } from '../../../api/settings';
-import DocLink from '../../../components/common/DocLink';
-import { SubmitButton } from '../../../components/controls/buttons';
-import { ClipboardButton } from '../../../components/controls/clipboard';
-import Spinner from '../../../components/ui/Spinner';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
interface Props {
generateSecretKey: () => Promise<void>;
}
-interface State {
- encryptedValue?: string;
- encrypting: boolean;
- generating: boolean;
- value: string;
-}
-
-export default class EncryptionForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { encrypting: false, generating: false, value: '' };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
- this.setState({ value: event.currentTarget.value });
- };
+export default function EncryptionForm({ generateSecretKey }: Readonly<Props>) {
+ const [encrypting, setEncrypting] = useState(false);
+ const [generating, setGenerating] = useState(false);
+ const [encryptedValue, setEncryptedValue] = useState('');
+ const [value, setValue] = useState('');
- handleEncrypt = (event: React.FormEvent) => {
- event.preventDefault();
- this.setState({ encrypting: true });
- encryptValue(this.state.value).then(
- ({ encryptedValue }) => {
- if (this.mounted) {
- this.setState({ encryptedValue, encrypting: false });
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ encrypting: false });
- }
- },
- );
- };
+ const handleChange = useCallback((event: React.ChangeEvent<HTMLTextAreaElement>) => {
+ setValue(event.currentTarget.value);
+ }, []);
- handleGenerateSecretKey = (event: React.FormEvent) => {
- event.preventDefault();
- this.setState({ generating: true });
- this.props.generateSecretKey().then(this.stopGenerating, this.stopGenerating);
- };
+ const handleEncrypt = useCallback(
+ (event: React.FormEvent) => {
+ event.preventDefault();
+ setEncrypting(true);
+ encryptValue(value).then(
+ ({ encryptedValue }) => {
+ setEncryptedValue(encryptedValue);
+ setEncrypting(false);
+ },
+ () => {
+ setEncrypting(false);
+ },
+ );
+ },
+ [value],
+ );
- stopGenerating = () => {
- if (this.mounted) {
- this.setState({ generating: false });
- }
- };
+ const handleGenerateSecretKey = useCallback(
+ (event: React.FormEvent) => {
+ event.preventDefault();
+ setGenerating(true);
+ generateSecretKey().then(
+ () => setGenerating(false),
+ () => setGenerating(false),
+ );
+ },
+ [generateSecretKey],
+ );
- render() {
- const { encryptedValue, encrypting, generating } = this.state;
- return (
- <div id="encryption-form-container">
- <div className="spacer-bottom">{translate('encryption.form_intro')}</div>
- <form className="big-spacer-bottom" id="encryption-form" onSubmit={this.handleEncrypt}>
- <textarea
- autoFocus
- className="abs-width-600"
- id="encryption-form-value"
- onChange={this.handleChange}
- required
- rows={5}
- value={this.state.value}
- />
- <div className="spacer-top">
- <SubmitButton disabled={encrypting || generating}>
- {translate('encryption.encrypt')}
- </SubmitButton>
- <Spinner className="spacer-left" loading={encrypting} />
- </div>
- </form>
+ return (
+ <div id="encryption-form-container">
+ <div className="sw-mb-2">{translate('encryption.form_intro')}</div>
+ <form id="encryption-form" onSubmit={handleEncrypt}>
+ <InputTextArea
+ autoFocus
+ size="large"
+ id="encryption-form-value"
+ onChange={handleChange}
+ required
+ rows={5}
+ value={value}
+ />
+ <div>
+ <ButtonSecondary className="sw-my-2" type="submit" disabled={encrypting || generating}>
+ {translate('encryption.encrypt')}
+ </ButtonSecondary>
+ <Spinner loading={encrypting} />
+ </div>
+ </form>
- {encryptedValue && (
- <div>
- <label className="little-spacer-right" htmlFor="encrypted-value">
- {translate('encryption.encrypted_value')}
- </label>
- <input
- className="input-clear input-code input-super-large"
- id="encrypted-value"
- readOnly
- type="text"
- value={encryptedValue}
+ {encryptedValue && (
+ <div className="sw-my-2">
+ <label>{translate('encryption.encrypted_value')}</label>
+ <div className="sw-flex">
+ <CodeSnippet
+ className="it__encrypted-value sw-max-w-full sw-break-words sw-p-1"
+ isOneLine
+ noCopy
+ snippet={encryptedValue}
+ />
+ <ClipboardIconButton
+ aria-label={translate('copy_to_clipboard')}
+ className="sw-ml-4"
+ copyValue={encryptedValue}
/>
- <ClipboardButton className="little-spacer-left" copyValue={encryptedValue} />
</div>
- )}
+ </div>
+ )}
- <form
- className="huge-spacer-top bordered-top"
- id="encryption-new-key-form"
- onSubmit={this.handleGenerateSecretKey}
- >
- <p className="big-spacer-top spacer-bottom">
- <FormattedMessage
- defaultMessage={translate('encryption.form_note')}
- id="encryption.form_note"
- values={{
- moreInformationLink: (
- <DocLink to="/instance-administration/security/">
- {translate('more_information')}
- </DocLink>
- ),
- }}
- />
- </p>
+ <form id="encryption-new-key-form" onSubmit={handleGenerateSecretKey}>
+ <p className="sw-my-2">
+ <FormattedMessage
+ defaultMessage={translate('encryption.form_note')}
+ id="encryption.form_note"
+ values={{
+ moreInformationLink: (
+ <DocumentationLink to="/instance-administration/security/">
+ {translate('more_information')}
+ </DocumentationLink>
+ ),
+ }}
+ />
+ </p>
- <SubmitButton disabled={generating || encrypting}>
- {translate('encryption.generate_new_secret_key')}{' '}
- </SubmitButton>
- <Spinner className="spacer-left" loading={generating} />
- </form>
- </div>
- );
- }
+ <ButtonPrimary type="submit" disabled={generating || encrypting}>
+ {translate('encryption.generate_new_secret_key')}{' '}
+ </ButtonPrimary>
+ <Spinner loading={generating} />
+ </form>
+ </div>
+ );
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ ButtonPrimary,
+ ClipboardIconButton,
+ CodeSnippet,
+ ListItem,
+ SubHeading,
+ UnorderedList,
+} from 'design-system';
import * as React from 'react';
+import { useCallback, useState } from 'react';
import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
-import { SubmitButton } from '../../../components/controls/buttons';
-import { ClipboardButton } from '../../../components/controls/clipboard';
+import DocumentationLink from '../../../components/common/DocumentationLink';
import Spinner from '../../../components/ui/Spinner';
import { translate } from '../../../helpers/l10n';
secretKey?: string;
}
-interface State {
- submitting: boolean;
-}
-
-export default class GenerateSecretKeyForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { submitting: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
+export default function GenerateSecretKeyForm({ secretKey, generateSecretKey }: Readonly<Props>) {
+ const [submitting, setSubmitting] = useState(false);
- handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
- event.preventDefault();
- this.setState({ submitting: true });
- this.props.generateSecretKey().then(this.stopSubmitting, this.stopSubmitting);
- };
+ const handleSubmit = useCallback(
+ (event: React.FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ setSubmitting(true);
+ generateSecretKey().then(
+ () => setSubmitting(false),
+ () => setSubmitting(false),
+ );
+ },
+ [generateSecretKey],
+ );
- stopSubmitting = () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- };
-
- render() {
- const { secretKey } = this.props;
- const { submitting } = this.state;
- return (
- <div id="generate-secret-key-form-container">
- {secretKey ? (
- <>
- <div className="big-spacer-bottom">
- <h3 className="spacer-bottom" id="secret-key-title">
- {translate('encryption.secret_key')}
- </h3>
- <input
- aria-labelledby="secret-key-title"
- className="input-clear input-code input-large"
- id="secret-key"
- readOnly
- type="text"
- value={secretKey}
+ return (
+ <div id="generate-secret-key-form-container">
+ {secretKey ? (
+ <>
+ <div className="sw-mb-4">
+ <SubHeading id="secret-key-title">{translate('encryption.secret_key')}</SubHeading>
+ <div className="sw-flex">
+ <CodeSnippet className="it__secret-key sw-p-1" isOneLine noCopy snippet={secretKey} />
+ <ClipboardIconButton
+ aria-label={translate('copy_to_clipboard')}
+ className="sw-ml-2"
+ copyValue={secretKey}
/>
- <ClipboardButton className="little-spacer-left" copyValue={secretKey} />
- </div>
- <h3 className="spacer-bottom">{translate('encryption.how_to_use')}</h3>
- <div className="markdown">
- <ul>
- <li>
- <FormattedMessage
- defaultMessage={translate('encryption.how_to_use.content1')}
- id="encryption.how_to_use.content1"
- values={{
- secret_file: <code>~/.sonar/sonar-secret.txt</code>,
- property: <code>sonar.secretKeyPath</code>,
- propreties_file: <code>conf/sonar.properties</code>,
- }}
- />
- </li>
- <li>{translate('encryption.how_to_use.content2')}</li>
- <li>
- <FormattedMessage
- defaultMessage={translate('encryption.how_to_use.content3')}
- id="encryption.how_to_use.content3"
- values={{
- property: <code>sonar.secretKeyPath</code>,
- }}
- />
- </li>
- <li>{translate('encryption.how_to_use.content4')}</li>
- </ul>
</div>
- </>
- ) : (
- <form id="generate-secret-key-form" onSubmit={this.handleSubmit}>
- <p className="spacer-bottom">
- <FormattedMessage
- defaultMessage={translate('encryption.secret_key_description')}
- id="encryption.secret_key_description"
- values={{
- moreInformationLink: (
- <DocLink to="/instance-administration/security/">
- {translate('more_information')}
- </DocLink>
- ),
- }}
- />
- </p>
- <SubmitButton disabled={submitting}>
- {translate('encryption.generate_secret_key')}
- </SubmitButton>
- <Spinner className="spacer-left" loading={submitting} />
- </form>
- )}
- </div>
- );
- }
+ </div>
+ <SubHeading className="sw-mb-2">{translate('encryption.how_to_use')}</SubHeading>
+ <div>
+ <UnorderedList ticks>
+ <ListItem>
+ <FormattedMessage
+ defaultMessage={translate('encryption.how_to_use.content1')}
+ id="encryption.how_to_use.content1"
+ values={{
+ secret_file: (
+ <CodeSnippet isOneLine noCopy snippet="~/.sonar/sonar-secret.txt" />
+ ),
+ property: <CodeSnippet isOneLine noCopy snippet="sonar.secretKeyPath" />,
+ propreties_file: (
+ <CodeSnippet isOneLine noCopy snippet="conf/sonar.properties" />
+ ),
+ }}
+ />
+ </ListItem>
+ <ListItem>{translate('encryption.how_to_use.content2')}</ListItem>
+ <ListItem>
+ <FormattedMessage
+ defaultMessage={translate('encryption.how_to_use.content3')}
+ id="encryption.how_to_use.content3"
+ values={{
+ property: <CodeSnippet isOneLine noCopy snippet="sonar.secretKeyPath" />,
+ }}
+ />
+ </ListItem>
+ <ListItem>{translate('encryption.how_to_use.content4')}</ListItem>
+ </UnorderedList>
+ </div>
+ </>
+ ) : (
+ <form id="generate-secret-key-form" onSubmit={handleSubmit}>
+ <p>
+ <FormattedMessage
+ defaultMessage={translate('encryption.secret_key_description')}
+ id="encryption.secret_key_description"
+ values={{
+ moreInformationLink: (
+ <DocumentationLink to="/instance-administration/security/">
+ {translate('more_information')}
+ </DocumentationLink>
+ ),
+ }}
+ />
+ </p>
+ <ButtonPrimary className="sw-mt-4" type="submit" disabled={submitting}>
+ {translate('encryption.generate_secret_key')}
+ </ButtonPrimary>
+ <Spinner loading={submitting} />
+ </form>
+ )}
+ </div>
+ );
}