Added validation on change and enable encryption checkbox
This commit is contained in:
parent
d44f68e844
commit
05cce8b107
@ -219,18 +219,27 @@
|
||||
"encryption_redirect": "Redirect to HTTPS automatically",
|
||||
"encryption_redirect_desc": "If checked, AdGuard Home will automatically redirect you from HTTP to HTTPS addresses.",
|
||||
"encryption_https": "HTTPS port",
|
||||
"encryption_https_desc": "If HTTPS port is configured, AdGuard Home admin interface will be accessible via HTTPS, and it will also provide DNS-over-HTTPS on '\\dns-query' location.",
|
||||
"encryption_https_desc": "If HTTPS port is configured, AdGuard Home admin interface will be accessible via HTTPS, and it will also provide DNS-over-HTTPS on '\/dns-query' location.",
|
||||
"encryption_dot": "DNS-over-TLS port",
|
||||
"encryption_dot_desc": "If this port is configured, AdGuard Home will run a DNS-over-TLS server on this port.",
|
||||
"encryption_certificates": "Certificates",
|
||||
"encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on <0>{{link}}</0> or you can buy it from one of the trusted Certificate Authorities.",
|
||||
"encryption_certificates_input": "Copy/paste your PEM-encoded cerificates here.",
|
||||
"encryption_status": "Status",
|
||||
"encryption_certificates_for": "Certificates for {{domains}}",
|
||||
"encryption_expire": "Expire on {{date}}",
|
||||
"encryption_expire": "Expires",
|
||||
"encryption_key": "Private key",
|
||||
"encryption_key_input": "Copy/paste your PEM-encoded private key for your cerficate here.",
|
||||
"encryption_enable": "Enable Encryption (HTTPS, DNS-over-HTTPS, and DNS-over-TLS)",
|
||||
"encryption_enable_desc": "If encryption is enabled, AdGuard Home admin interface will work over HTTPS, and the DNS server will listen for requests over DNS-over-HTTPS and DNS-over-TLS.",
|
||||
"encryption_chain_valid": "Certificate chain is valid",
|
||||
"encryption_chain_invalid": "Certificate chain is invalid",
|
||||
"encryption_key_valid": "This is a valid {{type}} private key",
|
||||
"encryption_key_invalid": "This is an invalid {{type}} private key",
|
||||
"encryption_subject": "Subject",
|
||||
"encryption_issuer": "Issuer",
|
||||
"encryption_hostnames": "Hostnames",
|
||||
"topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings</0>.",
|
||||
"topline_expired_certificate": "Your SSL certificate is expired. Update <0>Encryption settings</0>.",
|
||||
"form_error_port_range": "Enter port value in the range of 80-65535",
|
||||
"form_error_equal": "Shouldn't be equal",
|
||||
"form_error_password": "Password mismatched",
|
||||
|
70
client/src/actions/encryption.js
Normal file
70
client/src/actions/encryption.js
Normal file
@ -0,0 +1,70 @@
|
||||
import { createAction } from 'redux-actions';
|
||||
import Api from '../api/Api';
|
||||
import { addErrorToast, addSuccessToast } from './index';
|
||||
|
||||
const apiClient = new Api();
|
||||
|
||||
export const getTlsStatusRequest = createAction('GET_TLS_STATUS_REQUEST');
|
||||
export const getTlsStatusFailure = createAction('GET_TLS_STATUS_FAILURE');
|
||||
export const getTlsStatusSuccess = createAction('GET_TLS_STATUS_SUCCESS');
|
||||
|
||||
export const getTlsStatus = () => async (dispatch) => {
|
||||
dispatch(getTlsStatusRequest());
|
||||
try {
|
||||
const status = await apiClient.getTlsStatus();
|
||||
status.certificate_chain = atob(status.certificate_chain);
|
||||
status.private_key = atob(status.private_key);
|
||||
|
||||
dispatch(getTlsStatusSuccess(status));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(getTlsStatusFailure());
|
||||
}
|
||||
};
|
||||
|
||||
export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST');
|
||||
export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE');
|
||||
export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS');
|
||||
|
||||
export const setTlsConfig = config => async (dispatch) => {
|
||||
dispatch(setTlsConfigRequest());
|
||||
try {
|
||||
const values = { ...config };
|
||||
values.certificate_chain = btoa(values.certificate_chain);
|
||||
values.private_key = btoa(values.private_key);
|
||||
values.port_https = values.port_https || 0;
|
||||
values.port_dns_over_tls = values.port_dns_over_tls || 0;
|
||||
|
||||
const response = await apiClient.setTlsConfig(values);
|
||||
response.certificate_chain = atob(response.certificate_chain);
|
||||
response.private_key = atob(response.private_key);
|
||||
dispatch(setTlsConfigSuccess(response));
|
||||
dispatch(addSuccessToast('encryption_config_saved'));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(setTlsConfigFailure());
|
||||
}
|
||||
};
|
||||
|
||||
export const validateTlsConfigRequest = createAction('VALIDATE_TLS_CONFIG_REQUEST');
|
||||
export const validateTlsConfigFailure = createAction('VALIDATE_TLS_CONFIG_FAILURE');
|
||||
export const validateTlsConfigSuccess = createAction('VALIDATE_TLS_CONFIG_SUCCESS');
|
||||
|
||||
export const validateTlsConfig = config => async (dispatch) => {
|
||||
dispatch(validateTlsConfigRequest());
|
||||
try {
|
||||
const values = { ...config };
|
||||
values.certificate_chain = btoa(values.certificate_chain);
|
||||
values.private_key = btoa(values.private_key);
|
||||
values.port_https = values.port_https || 0;
|
||||
values.port_dns_over_tls = values.port_dns_over_tls || 0;
|
||||
|
||||
const response = await apiClient.validateTlsConfig(values);
|
||||
response.certificate_chain = atob(response.certificate_chain);
|
||||
response.private_key = atob(response.private_key);
|
||||
dispatch(validateTlsConfigSuccess(response));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(validateTlsConfigFailure());
|
||||
}
|
||||
};
|
@ -650,44 +650,3 @@ export const toggleDhcp = config => async (dispatch) => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getTlsStatusRequest = createAction('GET_TLS_STATUS_REQUEST');
|
||||
export const getTlsStatusFailure = createAction('GET_TLS_STATUS_FAILURE');
|
||||
export const getTlsStatusSuccess = createAction('GET_TLS_STATUS_SUCCESS');
|
||||
|
||||
export const getTlsStatus = () => async (dispatch) => {
|
||||
dispatch(getTlsStatusRequest());
|
||||
try {
|
||||
const status = await apiClient.getTlsStatus();
|
||||
status.certificate_chain = atob(status.certificate_chain);
|
||||
status.private_key = atob(status.private_key);
|
||||
|
||||
dispatch(getTlsStatusSuccess(status));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(getTlsStatusFailure());
|
||||
}
|
||||
};
|
||||
|
||||
export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST');
|
||||
export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE');
|
||||
export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS');
|
||||
|
||||
export const setTlsConfig = config => async (dispatch) => {
|
||||
dispatch(setTlsConfigRequest());
|
||||
try {
|
||||
const values = { ...config };
|
||||
values.certificate_chain = btoa(values.certificate_chain);
|
||||
values.private_key = btoa(values.private_key);
|
||||
values.port_https = values.port_https || 0;
|
||||
values.port_dns_over_tls = values.port_dns_over_tls || 0;
|
||||
|
||||
await apiClient.setTlsConfig(values);
|
||||
dispatch(setTlsConfigSuccess(config));
|
||||
dispatch(addSuccessToast('encryption_config_saved'));
|
||||
dispatch(getTlsStatus());
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(setTlsConfigFailure());
|
||||
}
|
||||
};
|
||||
|
@ -358,6 +358,7 @@ export default class Api {
|
||||
// DNS-over-HTTPS and DNS-over-TLS
|
||||
TLS_STATUS = { path: 'tls/status', method: 'GET' };
|
||||
TLS_CONFIG = { path: 'tls/configure', method: 'POST' };
|
||||
TLS_VALIDATE = { path: 'tls/validate', method: 'POST' };
|
||||
|
||||
getTlsStatus() {
|
||||
const { path, method } = this.TLS_STATUS;
|
||||
@ -372,4 +373,13 @@ export default class Api {
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
validateTlsConfig(config) {
|
||||
const { path, method } = this.TLS_VALIDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { HashRouter, Route } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import { withNamespaces } from 'react-i18next';
|
||||
import LoadingBar from 'react-redux-loading-bar';
|
||||
|
||||
import 'react-table/react-table.css';
|
||||
@ -18,6 +18,7 @@ import Footer from '../ui/Footer';
|
||||
import Toasts from '../Toasts';
|
||||
import Status from '../ui/Status';
|
||||
import Topline from '../ui/Topline';
|
||||
import EncryptionTopline from '../ui/EncryptionTopline';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
class App extends Component {
|
||||
@ -56,7 +57,6 @@ class App extends Component {
|
||||
!dashboard.processingVersions &&
|
||||
dashboard.isCoreRunning &&
|
||||
dashboard.isUpdateAvailable;
|
||||
const isExpiringCertificate = !encryption.processing && encryption.warning;
|
||||
|
||||
return (
|
||||
<HashRouter hashType='noslash'>
|
||||
@ -66,12 +66,8 @@ class App extends Component {
|
||||
{dashboard.announcement} <a href={dashboard.announcementUrl} target="_blank" rel="noopener noreferrer">Click here</a> for more info.
|
||||
</Topline>
|
||||
}
|
||||
{isExpiringCertificate &&
|
||||
<Topline type="warning">
|
||||
<Trans components={[<a href="#settings" key="0">link</a>]}>
|
||||
topline_expiring_certificate
|
||||
</Trans>
|
||||
</Topline>
|
||||
{!encryption.processing &&
|
||||
<EncryptionTopline notAfter={encryption.not_after} />
|
||||
}
|
||||
<LoadingBar className="loading-bar" updateTime={1000} />
|
||||
<Route component={Header} />
|
||||
|
@ -12,7 +12,6 @@ import './Header.css';
|
||||
class Header extends Component {
|
||||
state = {
|
||||
isMenuOpen: false,
|
||||
isDropdownOpen: false,
|
||||
};
|
||||
|
||||
toggleMenuOpen = () => {
|
||||
@ -25,6 +24,7 @@ class Header extends Component {
|
||||
|
||||
render() {
|
||||
const { dashboard } = this.props;
|
||||
const { isMenuOpen } = this.state;
|
||||
const badgeClass = classnames({
|
||||
'badge dns-status': true,
|
||||
'badge-success': dashboard.protectionEnabled,
|
||||
@ -52,7 +52,7 @@ class Header extends Component {
|
||||
</div>
|
||||
<Menu
|
||||
location={this.props.location}
|
||||
isMenuOpen={this.state.isMenuOpen}
|
||||
isMenuOpen={isMenuOpen}
|
||||
toggleMenuOpen={this.toggleMenuOpen}
|
||||
closeMenu={this.closeMenu}
|
||||
/>
|
||||
|
@ -1,10 +1,13 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
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 } from '../../../helpers/form';
|
||||
import { EMPTY_DATE } from '../../../helpers/constants';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
const validate = (values) => {
|
||||
@ -20,21 +23,46 @@ const validate = (values) => {
|
||||
return errors;
|
||||
};
|
||||
|
||||
const Form = (props) => {
|
||||
let Form = (props) => {
|
||||
const {
|
||||
t,
|
||||
handleSubmit,
|
||||
handleChange,
|
||||
isEnabled,
|
||||
certificateChain,
|
||||
privateKey,
|
||||
reset,
|
||||
invalid,
|
||||
submitting,
|
||||
processing,
|
||||
statusCert,
|
||||
statusKey,
|
||||
not_after,
|
||||
valid_chain,
|
||||
valid_key,
|
||||
dns_names,
|
||||
key_type,
|
||||
issuer,
|
||||
subject,
|
||||
warning_validation,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings">
|
||||
<Field
|
||||
name="enabled"
|
||||
type="checkbox"
|
||||
component={renderSelectField}
|
||||
placeholder={t('encryption_enable')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_enable_desc</Trans>
|
||||
</div>
|
||||
<hr/>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<label className="form__label" htmlFor="server_name">
|
||||
<Trans>encryption_server</Trans>
|
||||
@ -49,6 +77,8 @@ const Form = (props) => {
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={t('encryption_server_enter')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_server_desc</Trans>
|
||||
@ -62,6 +92,8 @@ const Form = (props) => {
|
||||
type="checkbox"
|
||||
component={renderSelectField}
|
||||
placeholder={t('encryption_redirect')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_redirect_desc</Trans>
|
||||
@ -84,6 +116,8 @@ const Form = (props) => {
|
||||
placeholder={t('encryption_https')}
|
||||
validate={[port]}
|
||||
normalize={toNumber}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_https_desc</Trans>
|
||||
@ -104,6 +138,8 @@ const Form = (props) => {
|
||||
placeholder={t('encryption_dot')}
|
||||
validate={[port]}
|
||||
normalize={toNumber}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__desc">
|
||||
<Trans>encryption_dot_desc</Trans>
|
||||
@ -132,28 +168,42 @@ const Form = (props) => {
|
||||
type="text"
|
||||
className="form-control form-control--textarea"
|
||||
placeholder={t('encryption_certificates_input')}
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__status">
|
||||
{statusCert &&
|
||||
{certificateChain &&
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
<div>
|
||||
{statusCert}
|
||||
</div>
|
||||
<ul>
|
||||
<li className={valid_chain ? 'text-success' : 'text-danger'}>
|
||||
{valid_chain ?
|
||||
<Trans>encryption_chain_valid</Trans>
|
||||
: <Trans>encryption_chain_invalid</Trans>
|
||||
}
|
||||
</li>
|
||||
{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>
|
||||
}
|
||||
</ul>
|
||||
</Fragment>
|
||||
}
|
||||
{/* <div>
|
||||
<Trans values={{ domains: '*.example.org, example.org' }}>
|
||||
encryption_certificates_for
|
||||
</Trans>
|
||||
</div>
|
||||
<div>
|
||||
<Trans values={{ date: '2022-01-01' }}>
|
||||
encryption_expire
|
||||
</Trans>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -171,33 +221,54 @@ const Form = (props) => {
|
||||
type="text"
|
||||
className="form-control form-control--textarea"
|
||||
placeholder="Copy/paste your PEM-encoded private key for your cerficate here."
|
||||
onChange={handleChange}
|
||||
disabled={!isEnabled}
|
||||
/>
|
||||
<div className="form__status">
|
||||
{statusKey &&
|
||||
{privateKey &&
|
||||
<Fragment>
|
||||
<div className="form__label form__label--bold">
|
||||
<Trans>encryption_status</Trans>:
|
||||
</div>
|
||||
<div>
|
||||
{statusKey}
|
||||
</div>
|
||||
<p 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>
|
||||
}
|
||||
</p>
|
||||
</Fragment>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<p className="text-danger">
|
||||
{warning_validation && warning_validation}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="btn-list">
|
||||
<div className="btn-list mt-2">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-success btn-standart"
|
||||
disabled={invalid || submitting || processing}
|
||||
disabled={
|
||||
invalid
|
||||
|| submitting
|
||||
|| processing
|
||||
|| !valid_chain
|
||||
|| !valid_key
|
||||
|| warning_validation
|
||||
}
|
||||
>
|
||||
<Trans>save_config</Trans>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
type="button"
|
||||
className="btn btn-secondary btn-standart"
|
||||
disabled={submitting || processing}
|
||||
onClick={reset}
|
||||
@ -211,16 +282,40 @@ const Form = (props) => {
|
||||
|
||||
Form.propTypes = {
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
handleChange: PropTypes.func,
|
||||
isEnabled: PropTypes.bool.isRequired,
|
||||
certificateChain: PropTypes.string.isRequired,
|
||||
privateKey: PropTypes.string.isRequired,
|
||||
reset: PropTypes.func.isRequired,
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
initialValues: PropTypes.object.isRequired,
|
||||
processing: PropTypes.bool.isRequired,
|
||||
statusCert: PropTypes.string,
|
||||
statusKey: PropTypes.string,
|
||||
status_key: PropTypes.string,
|
||||
not_after: PropTypes.string,
|
||||
warning_validation: PropTypes.string,
|
||||
valid_chain: PropTypes.bool,
|
||||
valid_key: PropTypes.bool,
|
||||
dns_names: PropTypes.string,
|
||||
key_type: PropTypes.string,
|
||||
issuer: PropTypes.string,
|
||||
subject: PropTypes.string,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const selector = formValueSelector('encryptionForm');
|
||||
|
||||
Form = connect((state) => {
|
||||
const isEnabled = selector(state, 'enabled');
|
||||
const certificateChain = selector(state, 'certificate_chain');
|
||||
const privateKey = selector(state, 'private_key');
|
||||
return {
|
||||
isEnabled,
|
||||
certificateChain,
|
||||
privateKey,
|
||||
};
|
||||
})(Form);
|
||||
|
||||
export default flow([
|
||||
withNamespaces(),
|
||||
reduxForm({
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withNamespaces } from 'react-i18next';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import Form from './Form';
|
||||
import Card from '../../ui/Card';
|
||||
@ -10,14 +11,20 @@ class Encryption extends Component {
|
||||
this.props.setTlsConfig(values);
|
||||
};
|
||||
|
||||
handleFormChange = debounce((values) => {
|
||||
this.props.validateTlsConfig(values);
|
||||
}, 300);
|
||||
|
||||
render() {
|
||||
const { encryption, t } = this.props;
|
||||
const {
|
||||
processing,
|
||||
processingConfig,
|
||||
status_cert: statusCert,
|
||||
status_key: statusKey,
|
||||
...values
|
||||
enabled,
|
||||
server_name,
|
||||
force_https,
|
||||
port_https,
|
||||
port_dns_over_tls,
|
||||
certificate_chain,
|
||||
private_key,
|
||||
} = encryption;
|
||||
|
||||
return (
|
||||
@ -29,11 +36,19 @@ class Encryption extends Component {
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<Form
|
||||
initialValues={{ ...values }}
|
||||
initialValues={{
|
||||
enabled,
|
||||
server_name,
|
||||
force_https,
|
||||
port_https,
|
||||
port_dns_over_tls,
|
||||
certificate_chain,
|
||||
private_key,
|
||||
}}
|
||||
processing={encryption.processingConfig}
|
||||
statusCert={statusCert}
|
||||
statusKey={statusKey}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
onChange={this.handleFormChange}
|
||||
{...this.props.encryption}
|
||||
/>
|
||||
</Card>
|
||||
}
|
||||
@ -44,6 +59,7 @@ class Encryption extends Component {
|
||||
|
||||
Encryption.propTypes = {
|
||||
setTlsConfig: PropTypes.func.isRequired,
|
||||
validateTlsConfig: PropTypes.func.isRequired,
|
||||
encryption: PropTypes.object.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -100,6 +100,7 @@ class Settings extends Component {
|
||||
<Encryption
|
||||
encryption={this.props.encryption}
|
||||
setTlsConfig={this.props.setTlsConfig}
|
||||
validateTlsConfig={this.props.validateTlsConfig}
|
||||
/>
|
||||
<Dhcp
|
||||
dhcp={this.props.dhcp}
|
||||
|
41
client/src/components/ui/EncryptionTopline.js
Normal file
41
client/src/components/ui/EncryptionTopline.js
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import isAfter from 'date-fns/is_after';
|
||||
import addDays from 'date-fns/add_days';
|
||||
|
||||
import Topline from './Topline';
|
||||
import { EMPTY_DATE } from '../../helpers/constants';
|
||||
|
||||
const EncryptionTopline = (props) => {
|
||||
if (props.notAfter !== EMPTY_DATE) {
|
||||
const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter);
|
||||
const isExpired = isAfter(Date.now(), props.notAfter);
|
||||
|
||||
if (isExpired) {
|
||||
return (
|
||||
<Topline type="danger">
|
||||
<Trans components={[<a href="#settings" key="0">link</a>]}>
|
||||
topline_expired_certificate
|
||||
</Trans>
|
||||
</Topline>
|
||||
);
|
||||
} else if (isAboutExpire) {
|
||||
return (
|
||||
<Topline type="warning">
|
||||
<Trans components={[<a href="#settings" key="0">link</a>]}>
|
||||
topline_expiring_certificate
|
||||
</Trans>
|
||||
</Topline>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
EncryptionTopline.propTypes = {
|
||||
notAfter: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withNamespaces()(EncryptionTopline);
|
@ -11,9 +11,12 @@ import {
|
||||
getDhcpInterfaces,
|
||||
setDhcpConfig,
|
||||
findActiveDhcp,
|
||||
} from '../actions';
|
||||
import {
|
||||
getTlsStatus,
|
||||
setTlsConfig,
|
||||
} from '../actions';
|
||||
validateTlsConfig,
|
||||
} from '../actions/encryption';
|
||||
import Settings from '../components/Settings';
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
@ -46,6 +49,7 @@ const mapDispatchToProps = {
|
||||
findActiveDhcp,
|
||||
getTlsStatus,
|
||||
setTlsConfig,
|
||||
validateTlsConfig,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
|
@ -73,3 +73,4 @@ export const SETTINGS_NAMES = {
|
||||
|
||||
export const STANDARD_DNS_PORT = 53;
|
||||
export const STANDARD_WEB_PORT = 80;
|
||||
export const EMPTY_DATE = '0001-01-01T00:00:00Z';
|
||||
|
62
client/src/reducers/encryption.js
Normal file
62
client/src/reducers/encryption.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { handleActions } from 'redux-actions';
|
||||
|
||||
import * as actions from '../actions/encryption';
|
||||
|
||||
const encryption = handleActions({
|
||||
[actions.getTlsStatusRequest]: state => ({ ...state, processing: true }),
|
||||
[actions.getTlsStatusFailure]: state => ({ ...state, processing: false }),
|
||||
[actions.getTlsStatusSuccess]: (state, { payload }) => {
|
||||
const newState = {
|
||||
...state,
|
||||
...payload,
|
||||
processing: false,
|
||||
};
|
||||
return newState;
|
||||
},
|
||||
|
||||
[actions.setTlsConfigRequest]: state => ({ ...state, processingConfig: true }),
|
||||
[actions.setTlsConfigFailure]: state => ({ ...state, processingConfig: false }),
|
||||
[actions.setTlsConfigSuccess]: (state, { payload }) => {
|
||||
const newState = {
|
||||
...state,
|
||||
...payload,
|
||||
processingConfig: false,
|
||||
};
|
||||
return newState;
|
||||
},
|
||||
|
||||
[actions.validateTlsConfigRequest]: state => ({ ...state, processingValidate: true }),
|
||||
[actions.validateTlsConfigFailure]: state => ({ ...state, processingValidate: false }),
|
||||
[actions.validateTlsConfigSuccess]: (state, { payload }) => {
|
||||
const newState = {
|
||||
...state,
|
||||
...payload,
|
||||
processingValidate: false,
|
||||
};
|
||||
return newState;
|
||||
},
|
||||
}, {
|
||||
processing: true,
|
||||
processingConfig: false,
|
||||
processingValidate: false,
|
||||
enabled: false,
|
||||
dns_names: null,
|
||||
force_https: false,
|
||||
issuer: '',
|
||||
key_type: '',
|
||||
not_after: '',
|
||||
not_before: '',
|
||||
port_dns_over_tls: 853,
|
||||
port_https: 443,
|
||||
subject: '',
|
||||
valid_chain: false,
|
||||
valid_key: false,
|
||||
status_cert: '',
|
||||
status_key: '',
|
||||
certificate_chain: '',
|
||||
private_key: '',
|
||||
server_name: '',
|
||||
warning_validation: '',
|
||||
});
|
||||
|
||||
export default encryption;
|
@ -6,6 +6,7 @@ import versionCompare from '../helpers/versionCompare';
|
||||
|
||||
import * as actions from '../actions';
|
||||
import toasts from './toasts';
|
||||
import encryption from './encryption';
|
||||
|
||||
const settings = handleActions({
|
||||
[actions.initSettingsRequest]: state => ({ ...state, processing: true }),
|
||||
@ -302,38 +303,6 @@ const dhcp = handleActions({
|
||||
leases: [],
|
||||
});
|
||||
|
||||
const encryption = handleActions({
|
||||
[actions.getTlsStatusRequest]: state => ({ ...state, processing: true }),
|
||||
[actions.getTlsStatusFailure]: state => ({ ...state, processing: false }),
|
||||
[actions.getTlsStatusSuccess]: (state, { payload }) => {
|
||||
const newState = {
|
||||
...state,
|
||||
...payload,
|
||||
processing: false,
|
||||
};
|
||||
return newState;
|
||||
},
|
||||
|
||||
[actions.setTlsConfigRequest]: state => ({ ...state, processingConfig: true }),
|
||||
[actions.setTlsConfigFailure]: state => ({ ...state, processingConfig: false }),
|
||||
[actions.setTlsConfigSuccess]: (state, { payload }) => {
|
||||
const newState = {
|
||||
...state,
|
||||
...payload,
|
||||
processingConfig: false,
|
||||
};
|
||||
return newState;
|
||||
},
|
||||
}, {
|
||||
processing: true,
|
||||
processingConfig: false,
|
||||
status_cert: '',
|
||||
status_key: '',
|
||||
certificate_chain: '',
|
||||
private_key: '',
|
||||
server_name: '',
|
||||
});
|
||||
|
||||
export default combineReducers({
|
||||
settings,
|
||||
dashboard,
|
||||
|
@ -63,7 +63,7 @@ type dnsConfig struct {
|
||||
var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"}
|
||||
|
||||
type tlsConfigSettings struct {
|
||||
Enabled bool `yaml:"enaled" json:"enabled"`
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
ServerName string `yaml:"server_name" json:"server_name,omitempty"`
|
||||
ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"`
|
||||
PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"`
|
||||
|
Loading…
Reference in New Issue
Block a user