diff --git a/bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java b/bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java index 868f564b..126d16b1 100644 --- a/bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java +++ b/bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java @@ -429,6 +429,14 @@ public class FilterHttpResource { return ok(ruleEngine.flushCaches()); } + @DELETE @Path(EP_MATCHERS) + @Produces(APPLICATION_JSON) + public Response flushMatchers(@Context ContainerRequest request) { + final Account caller = userPrincipal(request); + if (!caller.admin()) return forbidden(); + return ok(ruleEngine.flushMatchers()); + } + @POST @Path(EP_APPLY+"/{requestId}") @Consumes(MediaType.WILDCARD) @Produces(MediaType.WILDCARD) diff --git a/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java b/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java index 22ab0706..df8f4848 100644 --- a/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java +++ b/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java @@ -214,14 +214,14 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver return FilterMatchDecision.abort_not_found; // block this request case allow: default: - subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix, bubbleBlockConfig); + subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix); if (subDecision != null) return subDecision; if (log.isInfoEnabled()) log.info(prefix+"decision is ALLOW"); else if (extraLog) log.error(prefix+"decision is ALLOW"); return FilterMatchDecision.no_match; case filter: - subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix, bubbleBlockConfig); + subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix); if (subDecision != null) return subDecision; final List specs = decision.getSpecs(); if (empty(specs)) { @@ -237,8 +237,8 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver return FilterMatchDecision.abort_not_found; } } - if (log.isInfoEnabled()) log.info(prefix+"decision was FILTER but no URL-blocks and inPageBlocks are disabled (returning no_match)"); - else if (extraLog) log.error(prefix+"decision was FILTER but no URL-blocks and inPageBlocks are disabled (returning no_match)"); + if (log.isInfoEnabled()) log.info(prefix+"decision was FILTER but no URL-blocks and both inPageBlocks and showStats are disabled (returning no_match)"); + else if (extraLog) log.error(prefix+"decision was FILTER but no URL-blocks and both inPageBlocks and showStats are disabled (returning no_match)"); return FilterMatchDecision.no_match; } if (log.isInfoEnabled()) log.info(prefix+"decision is FILTER (returning match)"); @@ -248,7 +248,8 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver } } - public FilterMatchDecision checkRefererAndShowStats(BlockDecisionType decisionType, FilterMatchersRequest filter, Account account, Device device, boolean extraLog, String app, String site, String prefix, BubbleBlockConfig bubbleBlockConfig) { + public FilterMatchDecision checkRefererAndShowStats(BlockDecisionType decisionType, FilterMatchersRequest filter, Account account, Device device, boolean extraLog, String app, String site, String prefix) { + prefix += "(checkRefererAndShowStats): "; if (filter.hasReferer()) { final FilterMatchDecision refererDecision = checkRefererDecision(filter, account, device, app, site, prefix); if (refererDecision != null && refererDecision.isAbort()) { @@ -261,6 +262,9 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver if (log.isInfoEnabled()) log.info(prefix+"decision was "+decisionType+" but showStats=true, returning match"); else if (extraLog) log.error(prefix+"decision was "+decisionType+" but showStats=true, returning match"); return FilterMatchDecision.match; + } else { + if (log.isInfoEnabled()) log.info(prefix+"decision was "+decisionType+" but showStats=false, returning null"); + else if (extraLog) log.error(prefix+"decision was "+decisionType+" but showStats=false, returning null"); } return null; } @@ -419,7 +423,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver final AppDataDAO dataDAO = configuration.getBean(AppDataDAO.class); log.info("priming app="+app.getName()); dataDAO.findByAccountAndAppAndAndKeyPrefix(account.getUuid(), app.getUuid(), PREFIX_APPDATA_HIDE_STATS) - .forEach(data -> deviceIdService.setBlockStatsForFqdn(account, fqdnFromKey(data.getKey()), false)); + .forEach(data -> deviceIdService.setBlockStatsForFqdn(account, fqdnFromKey(data.getKey()), !Boolean.parseBoolean(data.getData()))); } @Override public Function createCallback(Account account, @@ -437,7 +441,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver deviceIdService.unsetBlockStatsForFqdn(account, fqdn); } else { log.info(prefix+"setting fqdn: "+fqdn); - deviceIdService.setBlockStatsForFqdn(account, fqdn, false); + deviceIdService.setBlockStatsForFqdn(account, fqdn, !Boolean.parseBoolean(data.getData())); } } else { throw invalidEx("err.fqdn.invalid", "not a valid FQDN: "+fqdn, fqdn); diff --git a/bubble-server/src/main/resources/META-INF/bubble/bubble.properties b/bubble-server/src/main/resources/META-INF/bubble/bubble.properties index 9662183a..0283ec9a 100644 --- a/bubble-server/src/main/resources/META-INF/bubble/bubble.properties +++ b/bubble-server/src/main/resources/META-INF/bubble/bubble.properties @@ -1 +1 @@ -bubble.version=Adventure 1.0.6 +bubble.version=Adventure 1.0.7 diff --git a/bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs b/bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs index d86a1fe7..855b0a7e 100644 --- a/bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs +++ b/bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs @@ -30,15 +30,38 @@ .then(response => response.text()) .then(data => set_func(data)) .catch((error) => { - console.error('Error getting link message ('+messages+'): '+error); + console.error('Error getting link message ('+link+'): '+error); }); } +{{JS_PREFIX}}_app_entry_div = function () { + const entry = document.createElement('div'); + entry.style.all = 'revert'; + entry.style.fontSize = 'small'; + return entry; +} + +{{JS_PREFIX}}_app_header_element = function () { + const header = document.createElement('strong'); + header.style.all = 'revert'; + header.style.fontSize = 'medium'; + return header; +} + +{{JS_PREFIX}}_app_header = function (title, defaultTitle, formatFunc) { + const labelText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, title, defaultTitle); + const label = {{JS_PREFIX}}_app_header_element(); + label.appendChild(document.createTextNode(formatFunc ? formatFunc(labelText) : labelText)); + return label; +} + {{JS_PREFIX}}_app_title_span = function (defaultName) { const appTitleSpan = document.createElement('span'); - appTitleSpan.style.fontSize = 'medium'; + appTitleSpan.style.fontSize = 'large'; const appImage = document.createElement('img'); appImage.src = {{JS_PREFIX}}_asset_img_url('icon'); + appImage.style.all = 'revert'; + appImage.style.width = '16px'; appImage.width = 16; const appTitle = document.createElement('strong'); const appName = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_appName', defaultName); @@ -48,10 +71,18 @@ return appTitleSpan; } -function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelFormat) { - const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault); +function {{JS_PREFIX}}_create_button_element() { const btn = document.createElement('button'); + btn.style.color = 'black'; btn.style.fontSize = 'x-small'; + btn.style.border = 'revert'; + btn.style.background = 'revert'; + return btn; +} + +function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelFormat) { + const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault); + const btn = {{JS_PREFIX}}_create_button_element(); btn.addEventListener('click', onclick); btn.appendChild(document.createTextNode(labelFormat ? labelFormat(label) : label)); return btn; @@ -97,6 +128,16 @@ if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') { return msg && msg in messages ? messages[msg] : def; } + {{PAGE_PREFIX}}_get_fqdn_tld = function () { + const host = window.location.hostname; + const fqdnParts = host.split('.'); + switch (fqdnParts.length) { + case 0: return host; + case 1: return host; + default: return fqdnParts[fqdnParts.length-2] + '.' + fqdnParts[fqdnParts.length-1]; + } + } + {{PAGE_PREFIX}}_log = function (data) { const logData = JSON.stringify(data); const requestOptions = { diff --git a/bubble-server/src/main/resources/bubble/rule/bblock/BubbleBlockRuleDriver_stats.js.hbs b/bubble-server/src/main/resources/bubble/rule/bblock/BubbleBlockRuleDriver_stats.js.hbs index 50ed20e2..3440a716 100644 --- a/bubble-server/src/main/resources/bubble/rule/bblock/BubbleBlockRuleDriver_stats.js.hbs +++ b/bubble-server/src/main/resources/bubble/rule/bblock/BubbleBlockRuleDriver_stats.js.hbs @@ -31,13 +31,23 @@ function {{JS_PREFIX}}_show_app_details(ev) { detailsDiv.removeChild(detailsDiv.lastChild); } detailsDiv.style.display = 'block'; - detailsDiv.appendChild({{JS_PREFIX}}_app_title_span('BlockParty!')); + + const titleSpan = {{JS_PREFIX}}_app_title_span('BlockParty!'); + const topButtons = document.createElement('span'); + const topCloseButton = {{JS_PREFIX}}_create_button('web_close', 'close', function (e) { + e.stopPropagation(); + e.preventDefault(); + {{JS_PREFIX}}_hide_app_details(); + return false; + }); + topButtons.style.float = 'right'; + topButtons.appendChild(topCloseButton); + titleSpan.appendChild(topButtons); + detailsDiv.appendChild(titleSpan); detailsDiv.appendChild(document.createElement('hr')); // add rows for blocked stuff... - const adsAndTrackersLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_adsAndTrackers', 'Blocked Ads/Trackers'); - const adsAndTrackersHeader = document.createElement('strong'); - adsAndTrackersHeader.appendChild(document.createTextNode(adsAndTrackersLabel)); + const adsAndTrackersHeader = {{JS_PREFIX}}_app_header('web_adsAndTrackers', 'Blocked Ads/Trackers'); detailsDiv.appendChild(adsAndTrackersHeader); if ({{JS_PREFIX}}_last_stats.blocks.length === 0) { const emptyLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noAdsOrTrackers', '(no blocks)'); @@ -46,26 +56,25 @@ function {{JS_PREFIX}}_show_app_details(ev) { } else { for (let i = 0; i < {{JS_PREFIX}}_last_stats.blocks.length; i++) { const entry = {{JS_PREFIX}}_last_stats.blocks[i]; - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); const entryText = document.createTextNode(entry.fqdn + ': ' + entry.count); entryDiv.appendChild(entryText); detailsDiv.appendChild(entryDiv); } } detailsDiv.appendChild(document.createElement('hr')); - const fqdn = window.location.hostname; - const hideStatsButton = {{JS_PREFIX}}_create_button('web_hideStats', 'Hide BlockParty for '+fqdn, function (e) { + const domain = {{PAGE_PREFIX}}_get_fqdn_tld(); + const hideStatsButton = {{JS_PREFIX}}_create_button('web_hideStats', 'Hide BlockParty for ', function (e) { // write appdata const hide_show_stats_url = '/__bubble/api/filter/data/{{BUBBLE_DATA_ID}}/write'; const requestOptions = { method: 'POST', - body: JSON.stringify({key: 'hideStats_'+fqdn, data: 'false'}) + body: JSON.stringify({key: 'hideStats_'+domain, data: 'true'}) }; fetch(hide_show_stats_url, requestOptions) .then(() => window.location.reload()); - //.then(() => window.location.reload()); return false; - }, label => label + fqdn); + }, label => label + domain); detailsDiv.appendChild(hideStatsButton); detailsDiv.appendChild(document.createElement('hr')); diff --git a/bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs b/bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs index 13a0549b..64b6ecf0 100644 --- a/bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs +++ b/bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs @@ -330,11 +330,13 @@ function {{JS_PREFIX}}_hide_app_details() { } } -function {{JS_PREFIX}}_create_expandable_header(listsHeaderText, entryClassName, detailsDiv, stateVar) { - const listsHeaderLink = document.createElement('a'); - const listsHeader = document.createElement('strong'); +function {{JS_PREFIX}}_create_expandable_header(messageKey, defaultMessage, entryClassName, detailsDiv, stateVar) { + const listsHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, messageKey, defaultMessage); + const listsHeader = {{JS_PREFIX}}_app_header_element(); const listsExpandControl = document.createElement('span'); listsExpandControl.appendChild(document.createTextNode('[+] ')); + + const listsHeaderLink = document.createElement('a'); const listsCollapseControl = document.createElement('span'); listsCollapseControl.appendChild(document.createTextNode('[-] ')); listsHeader.appendChild({{JS_PREFIX}}_expand_state[stateVar] ? listsCollapseControl : listsExpandControl); @@ -392,7 +394,6 @@ function {{JS_PREFIX}}_show_app_details() { detailsDiv.style.scrollTop = 0; const titleSpan = {{JS_PREFIX}}_app_title_span('ShadowBan'); - const topButtons = document.createElement('span'); if ({{JS_PREFIX}}_unblocked_needs_refresh) { @@ -420,8 +421,7 @@ function {{JS_PREFIX}}_show_app_details() { detailsDiv.appendChild(document.createElement('hr')); const recentBlocksDiv = document.createElement('div'); - const recentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_recentBlocks', 'Recent Blocks'); - const recentBlocksHeader = {{JS_PREFIX}}_create_expandable_header(recentBlocksText, '{{JS_PREFIX}}_recent_entry', detailsDiv, 'recent'); + const recentBlocksHeader = {{JS_PREFIX}}_create_expandable_header('web_recentBlocks', 'Recent Blocks', '{{JS_PREFIX}}_recent_entry', detailsDiv, 'recent'); recentBlocksDiv.appendChild(recentBlocksHeader); detailsDiv.appendChild(recentBlocksDiv); @@ -432,7 +432,7 @@ function {{JS_PREFIX}}_show_app_details() { let totalBlocks = 0; if (keywordTallyKeys.length === 0 && authorTallyKeys.length === 0) { const noRecentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noRecentBlocks', '(empty)'); - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_recent_entry'; entryDiv.style.display = {{JS_PREFIX}}_expand_state['recent'] ? 'block': 'none'; entryDiv.appendChild(document.createTextNode(noRecentBlocksText)); @@ -448,7 +448,7 @@ function {{JS_PREFIX}}_show_app_details() { keywordTallyKeys.forEach(keyword => { const count = keywordTally[keyword]; totalBlocks += count; - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_recent_entry'; entryDiv.style.display = {{JS_PREFIX}}_expand_state['recent'] ? 'block': 'none'; const em = document.createElement('em'); @@ -465,7 +465,7 @@ function {{JS_PREFIX}}_show_app_details() { authorTallyKeys.forEach(author => { const count = authorTally[author]; totalBlocks += count; - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_recent_entry'; entryDiv.style.display = 'none'; entryDiv.appendChild(document.createTextNode(author + ': ' + count)); @@ -473,13 +473,14 @@ function {{JS_PREFIX}}_show_app_details() { }); } } - const summaryHeader = document.createElement('strong'); - const summaryLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_signalNoiseRatio', 'signal/noise'); + const totalAllowed = {{PAGE_PREFIX}}_allow_tally; const fullTotal = totalAllowed + totalBlocks; const ratio = fullTotal === 0 ? 0 : 100.0 * (totalAllowed / fullTotal); const eqSign = totalBlocks === 0 ? '=' : '≈'; - summaryHeader.appendChild(document.createTextNode(summaryLabel + ': ' + totalAllowed + '/' + totalBlocks + ' '+eqSign+' ' + ratio.toLocaleString('{{ACCOUNT_LOCALE_HYPHEN}}', { maximumSignificantDigits: 3 }) + '%')); + const summaryHeader = {{JS_PREFIX}}_app_header('web_signalNoiseRatio', 'signal/noise', + label => label + ': ' + totalAllowed + '/' + totalBlocks + ' '+eqSign+' ' + ratio.toLocaleString('{{ACCOUNT_LOCALE_HYPHEN}}', { maximumSignificantDigits: 3 }) + '%'); + recentBlocksDiv.appendChild(summaryHeader); detailsDiv.appendChild(document.createElement('hr')); @@ -497,12 +498,11 @@ function {{JS_PREFIX}}_show_app_details() { return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'}); }); } - const usersHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedUsers', 'Block Users'); - const usersHeader = {{JS_PREFIX}}_create_expandable_header(usersHeaderText, '{{JS_PREFIX}}_user_entry', detailsDiv, 'users'); + const usersHeader = {{JS_PREFIX}}_create_expandable_header('web_blockedUsers', 'Block Users', '{{JS_PREFIX}}_user_entry', detailsDiv, 'users'); detailsDiv.appendChild(usersHeader); if (blocks === null || blocks.length === 0) { const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noUsersBlocked', '(empty)'); - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_user_entry'; entryDiv.style.display = {{JS_PREFIX}}_expand_state['users'] ? 'block': 'none'; const entryText = document.createTextNode(emptyMessage); @@ -511,7 +511,7 @@ function {{JS_PREFIX}}_show_app_details() { } else { for (let i = 0; i < blocks.length; i++) { const entry = blocks[i]; - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_user_entry'; entryDiv.style.display = {{JS_PREFIX}}_expand_state['users'] ? 'block': 'none'; const entryText = document.createTextNode(entry); @@ -523,12 +523,11 @@ function {{JS_PREFIX}}_show_app_details() { detailsDiv.appendChild(document.createElement('hr')); if ({{JS_PREFIX}}_supports_keywords) { - const keywordsHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedKeywords', 'Block Keywords'); - const keywordsHeader = {{JS_PREFIX}}_create_expandable_header(keywordsHeaderText, '{{JS_PREFIX}}_keyword_entry', detailsDiv, 'keywords'); + const keywordsHeader = {{JS_PREFIX}}_create_expandable_header('web_blockedKeywords', 'Block Keywords', '{{JS_PREFIX}}_keyword_entry', detailsDiv, 'keywords'); detailsDiv.appendChild(keywordsHeader); if (keywords === null || keywords.length === 0) { const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noKeywordsBlocked', '(empty)'); - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_keyword_entry'; entryDiv.style.display = {{JS_PREFIX}}_expand_state['keyword'] ? 'block': 'none'; const entryText = document.createTextNode(emptyMessage); @@ -537,7 +536,7 @@ function {{JS_PREFIX}}_show_app_details() { } else { for (let i = 0; i < keywords.length; i++) { const entry = keywords[i]; - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_keyword_entry'; entryDiv.style.display = {{JS_PREFIX}}_expand_state['keyword'] ? 'block': 'none'; const entryText = document.createTextNode(entry); @@ -571,8 +570,7 @@ function {{JS_PREFIX}}_show_app_details() { const listKeys = Object.keys({{JS_PREFIX}}_messages).filter(k => k.startsWith('web_kwlist_') && !k.endsWith('_url')); if (listKeys.length > 0) { - const listsHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedKeywordLists', 'Keyword Lists'); - const listsHeader = {{JS_PREFIX}}_create_expandable_header(listsHeaderText, '{{JS_PREFIX}}_keylist_entry', detailsDiv, 'lists'); + const listsHeader = {{JS_PREFIX}}_create_expandable_header('web_blockedKeywordLists', 'Keyword Lists', '{{JS_PREFIX}}_keylist_entry', detailsDiv, 'lists'); detailsDiv.appendChild(listsHeader); listKeys.forEach(listKey => { @@ -582,7 +580,7 @@ function {{JS_PREFIX}}_show_app_details() { console.log('URL not defined for list: '+listKey); return; } - const entryDiv = document.createElement('div'); + const entryDiv = {{JS_PREFIX}}_app_entry_div(); entryDiv.className = '{{JS_PREFIX}}_keylist_entry'; const entryLink = document.createElement('span'); entryLink.onclick = function () {