+ client: handle fields for certificate path and private key path
This commit is contained in:
parent
4445c4b669
commit
6d63450f03
@ -353,5 +353,13 @@
|
||||
"blocked_services_global": "Use global blocked services",
|
||||
"blocked_service": "Blocked service",
|
||||
"block_all": "Block all",
|
||||
"unblock_all": "Unblock all"
|
||||
}
|
||||
"unblock_all": "Unblock all",
|
||||
"encryption_certificate_path": "Certificate path",
|
||||
"encryption_certificate_path_notice": "To add a path to the certificate, clear the certificate chain textarea.",
|
||||
"encryption_private_key_path": "Private key path",
|
||||
"encryption_private_key_path_notice": "To add a path to the private key, clear the private key textarea.",
|
||||
"encryption_certificates_source_path": "Set a certificates file path",
|
||||
"encryption_certificates_source_content":"Paste the certificates contents",
|
||||
"encryption_key_source_path": "Set a private key file",
|
||||
"encryption_key_source_content": "Paste the private key contents"
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withNamespaces, Trans } from 'react-i18next';
|
||||
import format from 'date-fns/format';
|
||||
|
||||
import { EMPTY_DATE } from '../../../helpers/constants';
|
||||
|
||||
const CertificateStatus = ({
|
||||
validChain,
|
||||
validCert,
|
||||
subject,
|
||||
issuer,
|
||||
notAfter,
|
||||
dnsNames,
|
||||
}) => (
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
<ul className="encryption__list">
|
||||
<li
|
||||
className={validChain ? 'text-success' : 'text-danger'}
|
||||
>
|
||||
{validChain ? (
|
||||
<Trans>encryption_chain_valid</Trans>
|
||||
) : (
|
||||
<Trans>encryption_chain_invalid</Trans>
|
||||
)}
|
||||
</li>
|
||||
{validCert && (
|
||||
<Fragment>
|
||||
{subject && (
|
||||
<li>
|
||||
<Trans>encryption_subject</Trans>:
|
||||
{subject}
|
||||
</li>
|
||||
)}
|
||||
{issuer && (
|
||||
<li>
|
||||
<Trans>encryption_issuer</Trans>:
|
||||
{issuer}
|
||||
</li>
|
||||
)}
|
||||
{notAfter && notAfter !== EMPTY_DATE && (
|
||||
<li>
|
||||
<Trans>encryption_expire</Trans>:
|
||||
{format(notAfter, 'YYYY-MM-DD HH:mm:ss')}
|
||||
</li>
|
||||
)}
|
||||
{dnsNames && (
|
||||
<li>
|
||||
<Trans>encryption_hostnames</Trans>:
|
||||
{dnsNames}
|
||||
</li>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</ul>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
CertificateStatus.propTypes = {
|
||||
validChain: PropTypes.bool.isRequired,
|
||||
validCert: PropTypes.bool.isRequired,
|
||||
subject: PropTypes.string,
|
||||
issuer: PropTypes.string,
|
||||
notAfter: PropTypes.string,
|
||||
dnsNames: PropTypes.string,
|
||||
};
|
||||
|
||||
export default withNamespaces()(CertificateStatus);
|
@ -1,14 +1,22 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
import format from 'date-fns/format';
|
||||
|
||||
import { renderField, renderSelectField, toNumber, port, portTLS, isSafePort } from '../../../helpers/form';
|
||||
import { EMPTY_DATE } from '../../../helpers/constants';
|
||||
import {
|
||||
renderField,
|
||||
renderSelectField,
|
||||
renderRadioField,
|
||||
toNumber,
|
||||
port,
|
||||
portTLS,
|
||||
isSafePort,
|
||||
} from '../../../helpers/form';
|
||||
import i18n from '../../../i18n';
|
||||
import KeyStatus from './KeyStatus';
|
||||
import CertificateStatus from './CertificateStatus';
|
||||
|
||||
const validate = (values) => {
|
||||
const errors = {};
|
||||
@ -27,6 +35,8 @@ const clearFields = (change, setTlsConfig, t) => {
|
||||
const fields = {
|
||||
private_key: '',
|
||||
certificate_chain: '',
|
||||
private_key_path: '',
|
||||
certificate_path: '',
|
||||
port_https: 443,
|
||||
port_dns_over_tls: 853,
|
||||
server_name: '',
|
||||
@ -48,6 +58,8 @@ let Form = (props) => {
|
||||
isEnabled,
|
||||
certificateChain,
|
||||
privateKey,
|
||||
certificatePath,
|
||||
privateKeyPath,
|
||||
change,
|
||||
invalid,
|
||||
submitting,
|
||||
@ -64,6 +76,8 @@ let Form = (props) => {
|
||||
subject,
|
||||
warning_validation,
|
||||
setTlsConfig,
|
||||
certificateSource,
|
||||
privateKeySource,
|
||||
} = props;
|
||||
|
||||
const isSavingDisabled =
|
||||
@ -71,10 +85,9 @@ let Form = (props) => {
|
||||
submitting ||
|
||||
processingConfig ||
|
||||
processingValidate ||
|
||||
(isEnabled && (!privateKey || !certificateChain)) ||
|
||||
(privateKey && !valid_key) ||
|
||||
(certificateChain && !valid_cert) ||
|
||||
(privateKey && certificateChain && !valid_pair);
|
||||
!valid_key ||
|
||||
!valid_cert ||
|
||||
!valid_pair;
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
@ -182,7 +195,7 @@ let Form = (props) => {
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings">
|
||||
<label
|
||||
className="form__label form__label--bold"
|
||||
className="form__label form__label--with-desc form__label--bold"
|
||||
htmlFor="certificate_chain"
|
||||
>
|
||||
<Trans>encryption_certificates</Trans>
|
||||
@ -199,105 +212,124 @@ let Form = (props) => {
|
||||
encryption_certificates_desc
|
||||
</Trans>
|
||||
</div>
|
||||
<Field
|
||||
id="certificate_chain"
|
||||
name="certificate_chain"
|
||||
component="textarea"
|
||||
type="text"
|
||||
className="form-control form-control--textarea"
|
||||
placeholder={t('encryption_certificates_input')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__status">
|
||||
{certificateChain && (
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
<ul className="encryption__list">
|
||||
<li
|
||||
className={valid_chain ? 'text-success' : 'text-danger'}
|
||||
>
|
||||
{valid_chain ? (
|
||||
<Trans>encryption_chain_valid</Trans>
|
||||
) : (
|
||||
<Trans>encryption_chain_invalid</Trans>
|
||||
)}
|
||||
</li>
|
||||
{valid_cert && (
|
||||
<Fragment>
|
||||
{subject && (
|
||||
<li>
|
||||
<Trans>encryption_subject</Trans>:
|
||||
{subject}
|
||||
</li>
|
||||
)}
|
||||
{issuer && (
|
||||
<li>
|
||||
<Trans>encryption_issuer</Trans>:
|
||||
{issuer}
|
||||
</li>
|
||||
)}
|
||||
{not_after && not_after !== EMPTY_DATE && (
|
||||
<li>
|
||||
<Trans>encryption_expire</Trans>:
|
||||
{format(not_after, 'YYYY-MM-DD HH:mm:ss')}
|
||||
</li>
|
||||
)}
|
||||
{dns_names && (
|
||||
<li>
|
||||
<Trans>encryption_hostnames</Trans>:
|
||||
{dns_names}
|
||||
</li>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</ul>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
<div className="form__inline mb-2">
|
||||
<div className="custom-controls-stacked">
|
||||
<Field
|
||||
name="certificate_source"
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
className="form-control mr-2"
|
||||
value="path"
|
||||
placeholder={t('encryption_certificates_source_path')}
|
||||
/>
|
||||
<Field
|
||||
name="certificate_source"
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
className="form-control mr-2"
|
||||
value="content"
|
||||
placeholder={t('encryption_certificates_source_content')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{certificateSource === 'content' && (
|
||||
<Field
|
||||
id="certificate_chain"
|
||||
name="certificate_chain"
|
||||
component="textarea"
|
||||
type="text"
|
||||
className="form-control form-control--textarea"
|
||||
placeholder={t('encryption_certificates_input')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
)}
|
||||
{certificateSource === 'path' && (
|
||||
<Field
|
||||
id="certificate_path"
|
||||
name="certificate_path"
|
||||
component={renderField}
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={t('encryption_certificate_path')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="form__status">
|
||||
{(certificateChain || certificatePath) && (
|
||||
<CertificateStatus
|
||||
validChain={valid_chain}
|
||||
validCert={valid_cert}
|
||||
subject={subject}
|
||||
issuer={issuer}
|
||||
notAfter={not_after}
|
||||
dnsNames={dns_names}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings">
|
||||
<div className="form__group form__group--settings mt-3">
|
||||
<label className="form__label form__label--bold" htmlFor="private_key">
|
||||
<Trans>encryption_key</Trans>
|
||||
</label>
|
||||
<Field
|
||||
id="private_key"
|
||||
name="private_key"
|
||||
component="textarea"
|
||||
type="text"
|
||||
className="form-control form-control--textarea"
|
||||
placeholder={t('encryption_key_input')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__status">
|
||||
{privateKey && (
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
<ul className="encryption__list">
|
||||
<li className={valid_key ? 'text-success' : 'text-danger'}>
|
||||
{valid_key ? (
|
||||
<Trans values={{ type: key_type }}>
|
||||
encryption_key_valid
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans values={{ type: key_type }}>
|
||||
encryption_key_invalid
|
||||
</Trans>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
<div className="form__inline mb-2">
|
||||
<div className="custom-controls-stacked">
|
||||
<Field
|
||||
name="key_source"
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
className="form-control mr-2"
|
||||
value="path"
|
||||
placeholder={t('encryption_key_source_path')}
|
||||
/>
|
||||
<Field
|
||||
name="key_source"
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
className="form-control mr-2"
|
||||
value="content"
|
||||
placeholder={t('encryption_key_source_content')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{privateKeySource === 'content' && (
|
||||
<Field
|
||||
id="private_key"
|
||||
name="private_key"
|
||||
component="textarea"
|
||||
type="text"
|
||||
className="form-control form-control--textarea"
|
||||
placeholder={t('encryption_key_input')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
)}
|
||||
{privateKeySource === 'path' && (
|
||||
<Field
|
||||
id="private_key_path"
|
||||
name="private_key_path"
|
||||
component={renderField}
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={t('encryption_private_key_path')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="form__status">
|
||||
{(privateKey || privateKeyPath) && (
|
||||
<KeyStatus validKey={valid_key} keyType={key_type} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{warning_validation && (
|
||||
@ -334,6 +366,8 @@ Form.propTypes = {
|
||||
isEnabled: PropTypes.bool.isRequired,
|
||||
certificateChain: PropTypes.string.isRequired,
|
||||
privateKey: PropTypes.string.isRequired,
|
||||
certificatePath: PropTypes.string.isRequired,
|
||||
privateKeyPath: PropTypes.string.isRequired,
|
||||
change: PropTypes.func.isRequired,
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
@ -353,6 +387,8 @@ Form.propTypes = {
|
||||
subject: PropTypes.string,
|
||||
t: PropTypes.func.isRequired,
|
||||
setTlsConfig: PropTypes.func.isRequired,
|
||||
certificateSource: PropTypes.string,
|
||||
privateKeySource: PropTypes.string,
|
||||
};
|
||||
|
||||
const selector = formValueSelector('encryptionForm');
|
||||
@ -361,10 +397,18 @@ Form = connect((state) => {
|
||||
const isEnabled = selector(state, 'enabled');
|
||||
const certificateChain = selector(state, 'certificate_chain');
|
||||
const privateKey = selector(state, 'private_key');
|
||||
const certificatePath = selector(state, 'certificate_path');
|
||||
const privateKeyPath = selector(state, 'private_key_path');
|
||||
const certificateSource = selector(state, 'certificate_source');
|
||||
const privateKeySource = selector(state, 'key_source');
|
||||
return {
|
||||
isEnabled,
|
||||
certificateChain,
|
||||
privateKey,
|
||||
certificatePath,
|
||||
privateKeyPath,
|
||||
certificateSource,
|
||||
privateKeySource,
|
||||
};
|
||||
})(Form);
|
||||
|
||||
|
31
client/src/components/Settings/Encryption/KeyStatus.js
Normal file
31
client/src/components/Settings/Encryption/KeyStatus.js
Normal file
@ -0,0 +1,31 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withNamespaces, Trans } from 'react-i18next';
|
||||
|
||||
const KeyStatus = ({ validKey, keyType }) => (
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
<ul className="encryption__list">
|
||||
<li className={validKey ? 'text-success' : 'text-danger'}>
|
||||
{validKey ? (
|
||||
<Trans values={{ type: keyType }}>
|
||||
encryption_key_valid
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans values={{ type: keyType }}>
|
||||
encryption_key_invalid
|
||||
</Trans>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
KeyStatus.propTypes = {
|
||||
validKey: PropTypes.bool.isRequired,
|
||||
keyType: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withNamespaces()(KeyStatus);
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { withNamespaces } from 'react-i18next';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import { DEBOUNCE_TIMEOUT } from '../../../helpers/constants';
|
||||
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
|
||||
import Form from './Form';
|
||||
import Card from '../../ui/Card';
|
||||
import PageTitle from '../../ui/PageTitle';
|
||||
@ -19,13 +19,45 @@ class Encryption extends Component {
|
||||
}
|
||||
|
||||
handleFormSubmit = (values) => {
|
||||
this.props.setTlsConfig(values);
|
||||
const submitValues = this.getSubmitValues(values);
|
||||
this.props.setTlsConfig(submitValues);
|
||||
};
|
||||
|
||||
handleFormChange = debounce((values) => {
|
||||
this.props.validateTlsConfig(values);
|
||||
const submitValues = this.getSubmitValues(values);
|
||||
this.props.validateTlsConfig(submitValues);
|
||||
}, DEBOUNCE_TIMEOUT);
|
||||
|
||||
getInitialValues = (data) => {
|
||||
const { certificate_chain, private_key } = data;
|
||||
const certificate_source = certificate_chain ? 'content' : 'path';
|
||||
const key_source = private_key ? 'content' : 'path';
|
||||
|
||||
return {
|
||||
...data,
|
||||
certificate_source,
|
||||
key_source,
|
||||
};
|
||||
};
|
||||
|
||||
getSubmitValues = (values) => {
|
||||
const { certificate_source, key_source, ...config } = values;
|
||||
|
||||
if (certificate_source === ENCRYPTION_SOURCE.PATH) {
|
||||
config.certificate_chain = '';
|
||||
} else {
|
||||
config.certificate_path = '';
|
||||
}
|
||||
|
||||
if (values.key_source === ENCRYPTION_SOURCE.PATH) {
|
||||
config.private_key = '';
|
||||
} else {
|
||||
config.private_key_path = '';
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { encryption, t } = this.props;
|
||||
const {
|
||||
@ -36,8 +68,22 @@ class Encryption extends Component {
|
||||
port_dns_over_tls,
|
||||
certificate_chain,
|
||||
private_key,
|
||||
certificate_path,
|
||||
private_key_path,
|
||||
} = encryption;
|
||||
|
||||
const initialValues = this.getInitialValues({
|
||||
enabled,
|
||||
server_name,
|
||||
force_https,
|
||||
port_https,
|
||||
port_dns_over_tls,
|
||||
certificate_chain,
|
||||
private_key,
|
||||
certificate_path,
|
||||
private_key_path,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="encryption">
|
||||
<PageTitle title={t('encryption_settings')} />
|
||||
@ -49,15 +95,7 @@ class Encryption extends Component {
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<Form
|
||||
initialValues={{
|
||||
enabled,
|
||||
server_name,
|
||||
force_https,
|
||||
port_https,
|
||||
port_dns_over_tls,
|
||||
certificate_chain,
|
||||
private_key,
|
||||
}}
|
||||
initialValues={initialValues}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
onChange={this.handleFormChange}
|
||||
setTlsConfig={this.props.setTlsConfig}
|
||||
|
@ -248,3 +248,8 @@ export const SERVICES = [
|
||||
name: 'TikTok',
|
||||
},
|
||||
];
|
||||
|
||||
export const ENCRYPTION_SOURCE = {
|
||||
PATH: 'path',
|
||||
CONTENT: 'content',
|
||||
};
|
||||
|
@ -77,6 +77,8 @@ const encryption = handleActions({
|
||||
private_key: '',
|
||||
server_name: '',
|
||||
warning_validation: '',
|
||||
certificate_path: '',
|
||||
private_key_path: '',
|
||||
});
|
||||
|
||||
export default encryption;
|
||||
|
Loading…
Reference in New Issue
Block a user