- client: Fix client Access settings normalization

Close #1820

Squashed commit of the following:

commit 5aadec2e6e126588313ff006d6f95223ba19a526
Merge: a4db6b42 95f41285
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 11:15:31 2020 +0300

    Merge branch 'master' into fix/1820

commit a4db6b42ab9cbf43d96c783a72a99e0a2c594108
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 19:08:09 2020 +0300

    Remove textarea comma splitting

commit bb34797aac6602b405941dbd90fe6a81b663bb92
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 18:21:18 2020 +0300

    Fix client Access settings normalization

commit ac4fb536514f54c5722077d78dbbd981c4e906a8
Merge: 0c758ddc b9fca8d0
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 18:14:38 2020 +0300

    Merge branch 'master' into fix/1820

commit 0c758ddcd738136b92e6f947a8068ecc59f7ec25
Merge: 15650db3 f5a1f311
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 3 11:22:00 2020 +0300

    Merge branch 'master' into fix/1820

commit 15650db35323009001fd427a74a312705b54ac86
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jun 29 12:01:51 2020 +0300

    '- client: Don't normalise disallowed domains'
This commit is contained in:
Artem Baskal 2020-07-15 12:35:37 +03:00
parent 95f4128551
commit a32c1f2ee0
8 changed files with 54 additions and 32 deletions

View File

@ -2,9 +2,9 @@ import { createAction } from 'redux-actions';
import i18next from 'i18next'; import i18next from 'i18next';
import apiClient from '../api/Api'; import apiClient from '../api/Api';
import { normalizeTextarea } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts'; import { addErrorToast, addSuccessToast } from './toasts';
import { BLOCK_ACTIONS } from '../helpers/constants'; import { BLOCK_ACTIONS } from '../helpers/constants';
import { splitByNewLine } from '../helpers/helpers';
export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST'); export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST');
export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE'); export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE');
@ -31,9 +31,9 @@ export const setAccessList = (config) => async (dispatch) => {
const { allowed_clients, disallowed_clients, blocked_hosts } = config; const { allowed_clients, disallowed_clients, blocked_hosts } = config;
const values = { const values = {
allowed_clients: normalizeTextarea(allowed_clients), allowed_clients: splitByNewLine(allowed_clients),
disallowed_clients: normalizeTextarea(disallowed_clients), disallowed_clients: splitByNewLine(disallowed_clients),
blocked_hosts: normalizeTextarea(blocked_hosts), blocked_hosts: splitByNewLine(blocked_hosts),
}; };
await apiClient.setAccessList(values); await apiClient.setAccessList(values);

View File

@ -1,7 +1,7 @@
import { createAction } from 'redux-actions'; import { createAction } from 'redux-actions';
import apiClient from '../api/Api'; import apiClient from '../api/Api';
import { normalizeTextarea } from '../helpers/helpers'; import { splitByNewLine } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts'; import { addErrorToast, addSuccessToast } from './toasts';
export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST'); export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST');
@ -30,11 +30,11 @@ export const setDnsConfig = (config) => async (dispatch) => {
let hasDnsSettings = false; let hasDnsSettings = false;
if (Object.prototype.hasOwnProperty.call(data, 'bootstrap_dns')) { if (Object.prototype.hasOwnProperty.call(data, 'bootstrap_dns')) {
data.bootstrap_dns = normalizeTextarea(config.bootstrap_dns); data.bootstrap_dns = splitByNewLine(config.bootstrap_dns);
hasDnsSettings = true; hasDnsSettings = true;
} }
if (Object.prototype.hasOwnProperty.call(data, 'upstream_dns')) { if (Object.prototype.hasOwnProperty.call(data, 'upstream_dns')) {
data.upstream_dns = normalizeTextarea(config.upstream_dns); data.upstream_dns = splitByNewLine(config.upstream_dns);
hasDnsSettings = true; hasDnsSettings = true;
} }

View File

@ -2,7 +2,7 @@ import { createAction } from 'redux-actions';
import i18next from 'i18next'; import i18next from 'i18next';
import axios from 'axios'; import axios from 'axios';
import { isVersionGreater, normalizeTextarea, sortClients } from '../helpers/helpers'; import { isVersionGreater, splitByNewLine, sortClients } from '../helpers/helpers';
import { CHECK_TIMEOUT, SETTINGS_NAMES } from '../helpers/constants'; import { CHECK_TIMEOUT, SETTINGS_NAMES } from '../helpers/constants';
import { getTlsStatus } from './encryption'; import { getTlsStatus } from './encryption';
import apiClient from '../api/Api'; import apiClient from '../api/Api';
@ -279,8 +279,8 @@ export const testUpstream = (config) => async (dispatch) => {
dispatch(testUpstreamRequest()); dispatch(testUpstreamRequest());
try { try {
const values = { ...config }; const values = { ...config };
values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns); values.bootstrap_dns = splitByNewLine(values.bootstrap_dns);
values.upstream_dns = normalizeTextarea(values.upstream_dns); values.upstream_dns = splitByNewLine(values.upstream_dns);
const upstreamResponse = await apiClient.testUpstream(values); const upstreamResponse = await apiClient.testUpstream(values);
const testMessages = Object.keys(upstreamResponse) const testMessages = Object.keys(upstreamResponse)

View File

@ -4,7 +4,7 @@ import { Trans, withTranslation } from 'react-i18next';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import { MODAL_TYPE } from '../../../helpers/constants'; import { MODAL_TYPE } from '../../../helpers/constants';
import { normalizeTextarea } from '../../../helpers/helpers'; import { splitByNewLine } from '../../../helpers/helpers';
import Card from '../../ui/Card'; import Card from '../../ui/Card';
import Modal from './Modal'; import Modal from './Modal';
import CellWrap from '../../ui/CellWrap'; import CellWrap from '../../ui/CellWrap';
@ -30,7 +30,7 @@ class ClientsTable extends Component {
} }
if (values.upstreams && typeof values.upstreams === 'string') { if (values.upstreams && typeof values.upstreams === 'string') {
config.upstreams = normalizeTextarea(values.upstreams); config.upstreams = splitByNewLine(values.upstreams);
} else { } else {
config.upstreams = []; config.upstreams = [];
} }

View File

@ -4,7 +4,10 @@ import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next'; import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow'; import flow from 'lodash/flow';
import { renderTextareaField } from '../../../../helpers/form'; import { renderTextareaField } from '../../../../helpers/form';
import { normalizeMultiline } from '../../../../helpers/helpers'; import {
trimMultilineString,
removeEmptyLines,
} from '../../../../helpers/helpers';
import { FORM_NAME } from '../../../../helpers/constants'; import { FORM_NAME } from '../../../../helpers/constants';
const fields = [ const fields = [
@ -12,16 +15,19 @@ const fields = [
id: 'allowed_clients', id: 'allowed_clients',
title: 'access_allowed_title', title: 'access_allowed_title',
subtitle: 'access_allowed_desc', subtitle: 'access_allowed_desc',
normalizeOnBlur: removeEmptyLines,
}, },
{ {
id: 'disallowed_clients', id: 'disallowed_clients',
title: 'access_disallowed_title', title: 'access_disallowed_title',
subtitle: 'access_disallowed_desc', subtitle: 'access_disallowed_desc',
normalizeOnBlur: trimMultilineString,
}, },
{ {
id: 'blocked_hosts', id: 'blocked_hosts',
title: 'access_blocked_title', title: 'access_blocked_title',
subtitle: 'access_blocked_desc', subtitle: 'access_blocked_desc',
normalizeOnBlur: removeEmptyLines,
}, },
]; ];
@ -31,7 +37,7 @@ const Form = (props) => {
} = props; } = props;
const renderField = ({ const renderField = ({
id, title, subtitle, disabled = processingSet, id, title, subtitle, disabled = processingSet, normalizeOnBlur,
}) => <div key={id} className="form__group mb-5"> }) => <div key={id} className="form__group mb-5">
<label className="form__label form__label--with-desc" htmlFor={id}> <label className="form__label form__label--with-desc" htmlFor={id}>
<Trans>{title}</Trans> <Trans>{title}</Trans>
@ -46,7 +52,7 @@ const Form = (props) => {
type="text" type="text"
className="form-control form-control--textarea font-monospace" className="form-control form-control--textarea font-monospace"
disabled={disabled} disabled={disabled}
normalizeOnBlur={id === 'disallowed_clients' ? normalizeMultiline : undefined} normalizeOnBlur={normalizeOnBlur}
/> />
</div>; </div>;
@ -55,6 +61,7 @@ const Form = (props) => {
title: PropTypes.string, title: PropTypes.string,
subtitle: PropTypes.string, subtitle: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
normalizeOnBlur: PropTypes.func,
}; };
return ( return (

View File

@ -6,9 +6,10 @@ import { Trans, useTranslation } from 'react-i18next';
import classnames from 'classnames'; import classnames from 'classnames';
import Examples from './Examples'; import Examples from './Examples';
import { renderRadioField } from '../../../../helpers/form'; import { renderRadioField, renderTextareaField } from '../../../../helpers/form';
import { DNS_REQUEST_OPTIONS, FORM_NAME } from '../../../../helpers/constants'; import { DNS_REQUEST_OPTIONS, FORM_NAME } from '../../../../helpers/constants';
import { testUpstream } from '../../../../actions'; import { testUpstream } from '../../../../actions';
import { removeEmptyLines } from '../../../../helpers/helpers';
const getInputFields = () => [{ const getInputFields = () => [{
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
@ -17,9 +18,10 @@ const getInputFields = () => [{
</label>, </label>,
name: 'upstream_dns', name: 'upstream_dns',
type: 'text', type: 'text',
component: 'textarea', component: renderTextareaField,
className: 'form-control form-control--textarea font-monospace', className: 'form-control form-control--textarea font-monospace',
placeholder: 'upstream_dns', placeholder: 'upstream_dns',
normalizeOnBlur: removeEmptyLines,
}, },
{ {
name: 'upstream_mode', name: 'upstream_mode',
@ -69,7 +71,8 @@ const Form = ({
return <form onSubmit={handleSubmit}> return <form onSubmit={handleSubmit}>
<div className="row"> <div className="row">
{INPUT_FIELDS.map(({ {INPUT_FIELDS.map(({
name, component, type, className, placeholder, getTitle, subtitle, disabled, value, name, component, type, className, placeholder,
getTitle, subtitle, disabled, value, normalizeOnBlur,
}) => <div className="col-12 mb-4" key={placeholder}> }) => <div className="col-12 mb-4" key={placeholder}>
{typeof getTitle === 'function' && getTitle()} {typeof getTitle === 'function' && getTitle()}
<Field <Field
@ -82,6 +85,7 @@ const Form = ({
placeholder={t(placeholder)} placeholder={t(placeholder)}
subtitle={t(subtitle)} subtitle={t(subtitle)}
disabled={processingSetConfig || processingTestUpstream || disabled} disabled={processingSetConfig || processingTestUpstream || disabled}
normalizeOnBlur={normalizeOnBlur}
/> />
</div>)} </div>)}
<div className="col-12"> <div className="col-12">
@ -101,11 +105,12 @@ const Form = ({
<Field <Field
id="bootstrap_dns" id="bootstrap_dns"
name="bootstrap_dns" name="bootstrap_dns"
component="textarea" component={renderTextareaField}
type="text" type="text"
className="form-control form-control--textarea form-control--textarea-small font-monospace" className="form-control form-control--textarea form-control--textarea-small font-monospace"
placeholder={t('bootstrap_dns')} placeholder={t('bootstrap_dns')}
disabled={processingSetConfig} disabled={processingSetConfig}
normalizeOnBlur={removeEmptyLines}
/> />
</div> </div>
</div> </div>

View File

@ -45,7 +45,10 @@ const Dns = (props) => {
dnsConfig={dnsConfig} dnsConfig={dnsConfig}
setDnsConfig={setDnsConfig} setDnsConfig={setDnsConfig}
/> />
<Access access={access} setAccessList={setAccessList} /> <Access
access={access}
setAccessList={setAccessList}
/>
</>} </>}
</> </>
); );

View File

@ -306,15 +306,26 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => {
} }
}; };
export const normalizeTextarea = (text) => { /**
if (!text) { * @param {string} text
return []; * @returns []string
} */
export const splitByNewLine = (text) => text.split('\n')
.filter((n) => n.trim());
return text.replace(/[;, ]/g, '\n') /**
.split('\n') * @param {string} text
.filter((n) => n); * @returns {string}
}; */
export const trimMultilineString = (text) => splitByNewLine(text)
.map((line) => line.trim()).join('\n');
/**
* @param {string} text
* @returns {string}
*/
export const removeEmptyLines = (text) => splitByNewLine(text)
.join('\n');
/** /**
* Normalizes the topClients array * Normalizes the topClients array
@ -533,10 +544,6 @@ export const getMap = (arr, key, value) => arr.reduce((acc, curr) => {
return acc; return acc;
}, {}); }, {});
export const normalizeMultiline = (multiline) => `${normalizeTextarea(multiline)
.map((line) => line.trim())
.join('\n')}\n`;
/** /**
* @param parsedIp {object} ipaddr.js IPv4 or IPv6 object * @param parsedIp {object} ipaddr.js IPv4 or IPv6 object
* @param cidr {array} ipaddr.js CIDR array * @param cidr {array} ipaddr.js CIDR array