From e393acf5eb08251cd38073a181f5ddce08264af5 Mon Sep 17 00:00:00 2001 From: jvoisin Date: Mon, 21 Dec 2020 14:51:48 +0100 Subject: [PATCH 01/19] Remove a superfluous argument --- client/src/actions/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 04054091..58e2d018 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -287,7 +287,7 @@ export const getDnsStatus = () => async (dispatch) => { try { checkStatus(handleRequestSuccess, handleRequestError); } catch (error) { - handleRequestError(error); + handleRequestError(); } }; From 026cc2ecbfcb1d61c9c977ad0c515101c23abf1c Mon Sep 17 00:00:00 2001 From: jvoisin Date: Mon, 21 Dec 2020 17:00:27 +0100 Subject: [PATCH 02/19] Add a title to the refresh buttons This increases a bit the accessibility. --- client/src/components/Dashboard/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js index 4de5b8dd..fb9e3002 100644 --- a/client/src/components/Dashboard/index.js +++ b/client/src/components/Dashboard/index.js @@ -44,6 +44,7 @@ const Dashboard = ({ const refreshButton = ; }; -const getTitle = (reason) => { +const getTitle = () => { const { t } = useTranslation(); const filters = useSelector((state) => state.filtering.filters, shallowEqual); const whitelistFilters = useSelector((state) => state.filtering.whitelistFilters, shallowEqual); - const filter_id = useSelector((state) => state.filtering.check.filter_id); - - const filterName = getFilterName( - filters, - whitelistFilters, - filter_id, - 'filtered_custom_rules', - (filter) => (filter?.name ? t('query_log_filtered', { filter: filter.name }) : ''), - ); + const rules = useSelector((state) => state.filtering.check.rules, shallowEqual); + const reason = useSelector((state) => state.filtering.check.reason); const getReasonFiltered = (reason) => { const filterKey = reason.replace(FILTERED, ''); return i18next.t('query_log_filtered', { filter: filterKey }); }; + const ruleAndFilterNames = getRulesToFilterList(rules, filters, whitelistFilters); + const REASON_TO_TITLE_MAP = { [FILTERED_STATUS.NOT_FILTERED_NOT_FOUND]: t('check_not_found'), [FILTERED_STATUS.REWRITE]: t('rewrite_applied'), [FILTERED_STATUS.REWRITE_HOSTS]: t('rewrite_hosts_applied'), - [FILTERED_STATUS.FILTERED_BLACK_LIST]: filterName, - [FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: filterName, + [FILTERED_STATUS.FILTERED_BLACK_LIST]: ruleAndFilterNames, + [FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: ruleAndFilterNames, [FILTERED_STATUS.FILTERED_SAFE_SEARCH]: getReasonFiltered(reason), [FILTERED_STATUS.FILTERED_SAFE_BROWSING]: getReasonFiltered(reason), [FILTERED_STATUS.FILTERED_PARENTAL]: getReasonFiltered(reason), @@ -78,7 +73,11 @@ const getTitle = (reason) => { return <>
{t('check_reason', { reason })}
-
{filterName}
+
+ {t('rule_label')}: +   + {ruleAndFilterNames} +
; }; @@ -86,14 +85,13 @@ const Info = () => { const { hostname, reason, - rule, service_name, cname, ip_addrs, } = useSelector((state) => state.filtering.check, shallowEqual); const { t } = useTranslation(); - const title = getTitle(reason); + const title = getTitle(); const className = classNames('card mb-0 p-3', { 'logs__row--red': checkFiltered(reason), @@ -112,7 +110,6 @@ const Info = () => {
{title}
{!onlyFiltered && <> - {rule &&
{t('check_rule', { rule })}
} {service_name &&
{t('check_service', { service: service_name })}
} {cname &&
{t('check_cname', { cname })}
} {ip_addrs &&
{t('check_ip', { ip: ip_addrs.join(', ') })}
} diff --git a/client/src/components/Logs/Cells/ResponseCell.js b/client/src/components/Logs/Cells/ResponseCell.js index 816f35a3..026dbce1 100644 --- a/client/src/components/Logs/Cells/ResponseCell.js +++ b/client/src/components/Logs/Cells/ResponseCell.js @@ -4,8 +4,9 @@ import classNames from 'classnames'; import React from 'react'; import propTypes from 'prop-types'; import { + getRulesToFilterList, formatElapsedMs, - getFilterName, + getFilterNames, getServiceName, } from '../../../helpers/helpers'; import { FILTERED_STATUS, FILTERED_STATUS_TO_META_MAP } from '../../../helpers/constants'; @@ -18,8 +19,7 @@ const ResponseCell = ({ response, status, upstream, - rule, - filterId, + rules, service_name, }) => { const { t } = useTranslation(); @@ -36,7 +36,6 @@ const ResponseCell = ({ const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.LABEL || reason); const boldStatusLabel = {statusLabel}; - const filter = getFilterName(filters, whitelistFilters, filterId); const renderResponses = (responseArr) => { if (!responseArr || responseArr.length === 0) { @@ -52,18 +51,23 @@ const ResponseCell = ({ })}; }; + const rulesList = getRulesToFilterList(rules, filters, whitelistFilters); + const COMMON_CONTENT = { encryption_status: boldStatusLabel, install_settings_dns: upstream, elapsed: formattedElapsedMs, response_code: status, - ...(service_name ? { service_name: getServiceName(service_name) } : { filter }), - rule_label: rule, + ...(service_name + ? { service_name: getServiceName(service_name) } + : { } + ), + rule_label: rulesList, response_table_header: renderResponses(response), original_response: renderResponses(originalResponse), }; - const content = rule + const content = rules.length > 0 ? Object.entries(COMMON_CONTENT) : Object.entries({ ...COMMON_CONTENT, @@ -78,7 +82,8 @@ const ResponseCell = ({ } return getServiceName(service_name); case FILTERED_STATUS.FILTERED_BLACK_LIST: - return filter; + case FILTERED_STATUS.NOT_FILTERED_WHITE_LIST: + return getFilterNames(rules, filters, whitelistFilters).join(', '); default: return formattedElapsedMs; } @@ -113,8 +118,10 @@ ResponseCell.propTypes = { response: propTypes.array.isRequired, status: propTypes.string.isRequired, upstream: propTypes.string.isRequired, - rule: propTypes.string, - filterId: propTypes.number, + rules: propTypes.arrayOf(propTypes.shape({ + text: propTypes.string.isRequired, + filter_list_id: propTypes.number.isRequired, + })), service_name: propTypes.string, }; diff --git a/client/src/components/Logs/Cells/index.js b/client/src/components/Logs/Cells/index.js index 2e2635d9..8435a617 100644 --- a/client/src/components/Logs/Cells/index.js +++ b/client/src/components/Logs/Cells/index.js @@ -6,11 +6,11 @@ import propTypes from 'prop-types'; import { captitalizeWords, checkFiltered, + getRulesToFilterList, formatDateTime, formatElapsedMs, formatTime, getBlockingClientName, - getFilterName, getServiceName, processContent, } from '../../../helpers/helpers'; @@ -70,8 +70,7 @@ const Row = memo(({ upstream, type, client_proto, - filterId, - rule, + rules, originalResponse, status, service_name, @@ -107,8 +106,6 @@ const Row = memo(({ const sourceData = getSourceData(tracker); - const filter = getFilterName(filters, whitelistFilters, filterId); - const { confirmMessage, buttonKey: blockingClientKey, @@ -172,8 +169,8 @@ const Row = memo(({ response_details: 'title', install_settings_dns: upstream, elapsed: formattedElapsedMs, - filter: rule ? filter : null, - rule_label: rule, + rule_label: rules.length > 0 + && getRulesToFilterList(rules, filters, whitelistFilters), response_table_header: response?.join('\n'), response_code: status, client_details: 'title', @@ -235,8 +232,10 @@ Row.propTypes = { upstream: propTypes.string.isRequired, type: propTypes.string.isRequired, client_proto: propTypes.string.isRequired, - filterId: propTypes.number, - rule: propTypes.string, + rules: propTypes.arrayOf(propTypes.shape({ + text: propTypes.string.isRequired, + filter_list_id: propTypes.number.isRequired, + })), originalResponse: propTypes.array, status: propTypes.string.isRequired, service_name: propTypes.string, diff --git a/client/src/components/Logs/Logs.css b/client/src/components/Logs/Logs.css index 64300bb9..a48a8d15 100644 --- a/client/src/components/Logs/Logs.css +++ b/client/src/components/Logs/Logs.css @@ -428,3 +428,13 @@ margin-right: 1px; opacity: 0.5; } + +.filteringRules__rule { + margin-bottom: 0; +} + +.filteringRules__filter { + font-style: italic; + font-weight: normal; + margin-bottom: 1rem; +} diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 7d44b400..82f30245 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -7,6 +7,7 @@ import i18n from 'i18next'; import uniqBy from 'lodash/uniqBy'; import ipaddr from 'ipaddr.js'; import queryString from 'query-string'; +import React from 'react'; import { getTrackerData } from './trackers/trackers'; import { @@ -68,6 +69,7 @@ export const normalizeLogs = (logs) => logs.map((log) => { time, filterId, rule, + rules, service_name, original_answer, upstream, @@ -80,6 +82,15 @@ export const normalizeLogs = (logs) => logs.map((log) => { return `${type}: ${value} (ttl=${ttl})`; }) : []); + let newRules = rules; + /* TODO 'filterId' and 'rule' are deprecated, will be removed in 0.106 */ + if (rule !== undefined && filterId !== undefined && rules !== undefined && rules.length === 0) { + newRules = { + filter_list_id: filterId, + text: rule, + }; + } + return { time, domain, @@ -88,8 +99,10 @@ export const normalizeLogs = (logs) => logs.map((log) => { reason, client, client_proto, + /* TODO 'filterId' and 'rule' are deprecated, will be removed in 0.106 */ filterId, rule, + rules: newRules, status, service_name, originalAnswer: original_answer, @@ -726,6 +739,75 @@ export const getFilterName = ( return resolveFilterName(filter); }; +/** + * @param {array} rules + * @param {array} filters + * @param {array} whitelistFilters + * @returns {string[]} + */ +export const getFilterNames = (rules, filters, whitelistFilters) => rules.map( + ({ filter_list_id }) => getFilterName(filters, whitelistFilters, filter_list_id), +); + +/** + * @param {array} rules + * @returns {string[]} + */ +export const getRuleNames = (rules) => rules.map(({ text }) => text); + +/** + * @param {array} rules + * @param {array} filters + * @param {array} whitelistFilters + * @returns {object} + */ +export const getFilterNameToRulesMap = (rules, filters, whitelistFilters) => rules.reduce( + (acc, { text, filter_list_id }) => { + const filterName = getFilterName(filters, whitelistFilters, filter_list_id); + + acc[filterName] = (acc[filterName] || []).concat(text); + return acc; + }, {}, +); + +/** + * @param {array} rules + * @param {array} filters + * @param {array} whitelistFilters + * @param {object} classes + * @returns {JSXElement} + */ +export const getRulesToFilterList = (rules, filters, whitelistFilters, classes = { + list: 'filteringRules', + rule: 'filteringRules__rule font-monospace', + filter: 'filteringRules__filter', +}) => { + const filterNameToRulesMap = getFilterNameToRulesMap(rules, filters, whitelistFilters); + + return
+ {Object.entries(filterNameToRulesMap).reduce( + (acc, [filterName, rulesArr]) => acc + .concat(rulesArr.map((rule, i) =>
{rule}
)) + .concat(
{filterName}
), + [], + )} +
; +}; + +/** +* @param {array} rules +* @param {array} filters +* @param {array} whitelistFilters +* @returns {string} +*/ +export const getRulesAndFilterNames = (rules, filters, whitelistFilters) => { + const filterNameToRulesMap = getFilterNameToRulesMap(rules, filters, whitelistFilters); + + return Object.entries(filterNameToRulesMap).map( + ([filterName, filterRules]) => filterRules.concat(filterName).join('\n'), + ).join('\n\n'); +}; + /** * @param ip {string} * @param gateway_ip {string} diff --git a/client/src/helpers/renderFormattedClientCell.js b/client/src/helpers/renderFormattedClientCell.js index d677c4ca..f7e59a84 100644 --- a/client/src/helpers/renderFormattedClientCell.js +++ b/client/src/helpers/renderFormattedClientCell.js @@ -31,7 +31,7 @@ const getFormattedWhois = (whois) => { * @param {object} info.whois_info * @param {boolean} [isDetailed] * @param {boolean} [isLogs] - * @returns {JSX.Element} + * @returns {JSXElement} */ export const renderFormattedClientCell = (value, info, isDetailed = false, isLogs = false) => { let whoisContainer = null;