+ client: handle access settings
This commit is contained in:
parent
821ad3edd9
commit
02db488b30
|
@ -298,5 +298,14 @@
|
||||||
"clients_not_found": "No clients found",
|
"clients_not_found": "No clients found",
|
||||||
"client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?",
|
"client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?",
|
||||||
"auto_clients_title": "Clients (runtime)",
|
"auto_clients_title": "Clients (runtime)",
|
||||||
"auto_clients_desc": "Data on the clients that use AdGuard Home, but not stored in the configuration"
|
"auto_clients_desc": "Data on the clients that use AdGuard Home, but not stored in the configuration",
|
||||||
}
|
"access_title": "Access settings",
|
||||||
|
"access_desc": "Here you can configure access rules for the AdGuard Home DNS server.",
|
||||||
|
"access_allowed_title": "Allowed clients",
|
||||||
|
"access_allowed_desc": "A list of CIDR or IP addresses. If configured, AdGuard Home will accept requests from these IP addresses only.",
|
||||||
|
"access_disallowed_title": "Disallowed clients",
|
||||||
|
"access_disallowed_desc": "A list of CIDR or IP addresses. If configured, AdGuard Home will drop requests from these IP addresses.",
|
||||||
|
"access_blocked_title": "Blocked domains",
|
||||||
|
"access_blocked_desc": "Don't confuse this with filters. AdGuard Home will drop DNS queries with these domains in query's question.",
|
||||||
|
"access_settings_saved": "Access settings successfully saved"
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { createAction } from 'redux-actions';
|
||||||
|
import Api from '../api/Api';
|
||||||
|
import { addErrorToast, addSuccessToast } from './index';
|
||||||
|
import { normalizeTextarea } from '../helpers/helpers';
|
||||||
|
|
||||||
|
const apiClient = new Api();
|
||||||
|
|
||||||
|
export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST');
|
||||||
|
export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE');
|
||||||
|
export const getAccessListSuccess = createAction('GET_ACCESS_LIST_SUCCESS');
|
||||||
|
|
||||||
|
export const getAccessList = () => async (dispatch) => {
|
||||||
|
dispatch(getAccessListRequest());
|
||||||
|
try {
|
||||||
|
const data = await apiClient.getAccessList();
|
||||||
|
dispatch(getAccessListSuccess(data));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(getAccessListFailure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setAccessListRequest = createAction('SET_ACCESS_LIST_REQUEST');
|
||||||
|
export const setAccessListFailure = createAction('SET_ACCESS_LIST_FAILURE');
|
||||||
|
export const setAccessListSuccess = createAction('SET_ACCESS_LIST_SUCCESS');
|
||||||
|
|
||||||
|
export const setAccessList = config => async (dispatch) => {
|
||||||
|
dispatch(setAccessListRequest());
|
||||||
|
try {
|
||||||
|
const { allowed_clients, disallowed_clients, blocked_hosts } = config;
|
||||||
|
|
||||||
|
const values = {
|
||||||
|
allowed_clients: (allowed_clients && normalizeTextarea(allowed_clients)) || [],
|
||||||
|
disallowed_clients: (disallowed_clients && normalizeTextarea(disallowed_clients)) || [],
|
||||||
|
blocked_hosts: (blocked_hosts && normalizeTextarea(blocked_hosts)) || [],
|
||||||
|
};
|
||||||
|
|
||||||
|
await apiClient.setAccessList(values);
|
||||||
|
dispatch(setAccessListSuccess());
|
||||||
|
dispatch(addSuccessToast('access_settings_saved'));
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(addErrorToast({ error }));
|
||||||
|
dispatch(setAccessListFailure());
|
||||||
|
}
|
||||||
|
};
|
|
@ -460,4 +460,22 @@ export default class Api {
|
||||||
};
|
};
|
||||||
return this.makeRequest(path, method, parameters);
|
return this.makeRequest(path, method, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNS access settings
|
||||||
|
ACCESS_LIST = { path: 'access/list', method: 'GET' };
|
||||||
|
ACCESS_SET = { path: 'access/set', method: 'POST' };
|
||||||
|
|
||||||
|
getAccessList() {
|
||||||
|
const { path, method } = this.ACCESS_LIST;
|
||||||
|
return this.makeRequest(path, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAccessList(config) {
|
||||||
|
const { path, method } = this.ACCESS_SET;
|
||||||
|
const parameters = {
|
||||||
|
data: config,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
};
|
||||||
|
return this.makeRequest(path, method, parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
|
const Form = (props) => {
|
||||||
|
const { handleSubmit, submitting, invalid } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="form__group mb-5">
|
||||||
|
<label className="form__label form__label--with-desc" htmlFor="allowed_clients">
|
||||||
|
<Trans>access_allowed_title</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>access_allowed_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id="allowed_clients"
|
||||||
|
name="allowed_clients"
|
||||||
|
component="textarea"
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form__group mb-5">
|
||||||
|
<label className="form__label form__label--with-desc" htmlFor="disallowed_clients">
|
||||||
|
<Trans>access_disallowed_title</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>access_disallowed_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id="disallowed_clients"
|
||||||
|
name="disallowed_clients"
|
||||||
|
component="textarea"
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form__group mb-5">
|
||||||
|
<label className="form__label form__label--with-desc" htmlFor="blocked_hosts">
|
||||||
|
<Trans>access_blocked_title</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>access_blocked_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id="blocked_hosts"
|
||||||
|
name="blocked_hosts"
|
||||||
|
component="textarea"
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="card-actions">
|
||||||
|
<div className="btn-list">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard"
|
||||||
|
disabled={submitting || invalid}
|
||||||
|
>
|
||||||
|
<Trans>save_config</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
handleSubmit: PropTypes.func,
|
||||||
|
submitting: PropTypes.bool,
|
||||||
|
invalid: PropTypes.bool,
|
||||||
|
initialValues: PropTypes.object,
|
||||||
|
t: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default flow([withNamespaces(), reduxForm({ form: 'accessForm' })])(Form);
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
import Form from './Form';
|
||||||
|
import Card from '../../ui/Card';
|
||||||
|
|
||||||
|
class Access extends Component {
|
||||||
|
handleFormSubmit = (values) => {
|
||||||
|
this.props.setAccessList(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { t, access } = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
processing,
|
||||||
|
processingSet,
|
||||||
|
...values
|
||||||
|
} = access;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t('access_title')}
|
||||||
|
subtitle={t('access_desc')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
initialValues={values}
|
||||||
|
onSubmit={this.handleFormSubmit}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Access.propTypes = {
|
||||||
|
access: PropTypes.object.isRequired,
|
||||||
|
setAccessList: PropTypes.func.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withNamespaces()(Access);
|
|
@ -63,6 +63,10 @@
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form__label--with-desc {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.form__status {
|
.form__status {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
|
@ -62,7 +62,7 @@ let Form = (props) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<label className="form__label" htmlFor="bootstrap_dns">
|
<label className="form__label form__label--with-desc" htmlFor="bootstrap_dns">
|
||||||
<Trans>bootstrap_dns</Trans>
|
<Trans>bootstrap_dns</Trans>
|
||||||
</label>
|
</label>
|
||||||
<div className="form__desc form__desc--top">
|
<div className="form__desc form__desc--top">
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Dhcp from './Dhcp';
|
||||||
import Encryption from './Encryption';
|
import Encryption from './Encryption';
|
||||||
import Clients from './Clients';
|
import Clients from './Clients';
|
||||||
import AutoClients from './Clients/AutoClients';
|
import AutoClients from './Clients/AutoClients';
|
||||||
|
import Access from './Access';
|
||||||
import Checkbox from '../ui/Checkbox';
|
import Checkbox from '../ui/Checkbox';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import PageTitle from '../ui/PageTitle';
|
import PageTitle from '../ui/PageTitle';
|
||||||
|
@ -43,6 +44,7 @@ class Settings extends Component {
|
||||||
this.props.getDhcpStatus();
|
this.props.getDhcpStatus();
|
||||||
this.props.getDhcpInterfaces();
|
this.props.getDhcpInterfaces();
|
||||||
this.props.getTlsStatus();
|
this.props.getTlsStatus();
|
||||||
|
this.props.getAccessList();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSettings = (settings) => {
|
renderSettings = (settings) => {
|
||||||
|
@ -68,7 +70,7 @@ class Settings extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
settings, dashboard, clients, t,
|
settings, dashboard, clients, access, t,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -117,6 +119,7 @@ class Settings extends Component {
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
|
<Access access={access} setAccessList={this.props.setAccessList} />
|
||||||
<Encryption
|
<Encryption
|
||||||
encryption={this.props.encryption}
|
encryption={this.props.encryption}
|
||||||
setTlsConfig={this.props.setTlsConfig}
|
setTlsConfig={this.props.setTlsConfig}
|
||||||
|
|
|
@ -26,6 +26,10 @@ import {
|
||||||
deleteClient,
|
deleteClient,
|
||||||
toggleClientModal,
|
toggleClientModal,
|
||||||
} from '../actions/clients';
|
} from '../actions/clients';
|
||||||
|
import {
|
||||||
|
getAccessList,
|
||||||
|
setAccessList,
|
||||||
|
} from '../actions/access';
|
||||||
import Settings from '../components/Settings';
|
import Settings from '../components/Settings';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
|
@ -35,6 +39,7 @@ const mapStateToProps = (state) => {
|
||||||
dhcp,
|
dhcp,
|
||||||
encryption,
|
encryption,
|
||||||
clients,
|
clients,
|
||||||
|
access,
|
||||||
} = state;
|
} = state;
|
||||||
const props = {
|
const props = {
|
||||||
settings,
|
settings,
|
||||||
|
@ -42,6 +47,7 @@ const mapStateToProps = (state) => {
|
||||||
dhcp,
|
dhcp,
|
||||||
encryption,
|
encryption,
|
||||||
clients,
|
clients,
|
||||||
|
access,
|
||||||
};
|
};
|
||||||
return props;
|
return props;
|
||||||
};
|
};
|
||||||
|
@ -68,6 +74,8 @@ const mapDispatchToProps = {
|
||||||
addStaticLease,
|
addStaticLease,
|
||||||
removeStaticLease,
|
removeStaticLease,
|
||||||
toggleLeaseModal,
|
toggleLeaseModal,
|
||||||
|
getAccessList,
|
||||||
|
setAccessList,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { handleActions } from 'redux-actions';
|
||||||
|
|
||||||
|
import * as actions from '../actions/access';
|
||||||
|
|
||||||
|
const access = handleActions(
|
||||||
|
{
|
||||||
|
[actions.getAccessListRequest]: state => ({ ...state, processing: true }),
|
||||||
|
[actions.getAccessListFailure]: state => ({ ...state, processing: false }),
|
||||||
|
[actions.getAccessListSuccess]: (state, { payload }) => {
|
||||||
|
const {
|
||||||
|
allowed_clients,
|
||||||
|
disallowed_clients,
|
||||||
|
blocked_hosts,
|
||||||
|
} = payload;
|
||||||
|
const newState = {
|
||||||
|
...state,
|
||||||
|
allowed_clients: allowed_clients.join('\n'),
|
||||||
|
disallowed_clients: disallowed_clients.join('\n'),
|
||||||
|
blocked_hosts: blocked_hosts.join('\n'),
|
||||||
|
};
|
||||||
|
return newState;
|
||||||
|
},
|
||||||
|
|
||||||
|
[actions.setAccessListRequest]: state => ({ ...state, processingSet: true }),
|
||||||
|
[actions.setAccessListFailure]: state => ({ ...state, processingSet: false }),
|
||||||
|
[actions.setAccessListSuccess]: (state) => {
|
||||||
|
const newState = {
|
||||||
|
...state,
|
||||||
|
processingSet: false,
|
||||||
|
};
|
||||||
|
return newState;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
processing: true,
|
||||||
|
processingSet: false,
|
||||||
|
allowed_clients: null,
|
||||||
|
disallowed_clients: null,
|
||||||
|
blocked_hosts: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default access;
|
|
@ -8,6 +8,7 @@ import * as actions from '../actions';
|
||||||
import toasts from './toasts';
|
import toasts from './toasts';
|
||||||
import encryption from './encryption';
|
import encryption from './encryption';
|
||||||
import clients from './clients';
|
import clients from './clients';
|
||||||
|
import access from './access';
|
||||||
|
|
||||||
const settings = handleActions({
|
const settings = handleActions({
|
||||||
[actions.initSettingsRequest]: state => ({ ...state, processing: true }),
|
[actions.initSettingsRequest]: state => ({ ...state, processing: true }),
|
||||||
|
@ -418,6 +419,7 @@ export default combineReducers({
|
||||||
dhcp,
|
dhcp,
|
||||||
encryption,
|
encryption,
|
||||||
clients,
|
clients,
|
||||||
|
access,
|
||||||
loadingBar: loadingBarReducer,
|
loadingBar: loadingBarReducer,
|
||||||
form: formReducer,
|
form: formReducer,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue