@@ -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) | |||
@@ -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<BlockSpec> 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<AppData, AppData> 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); | |||
@@ -1 +1 @@ | |||
bubble.version=Adventure 1.0.6 | |||
bubble.version=Adventure 1.0.7 |
@@ -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 = { | |||
@@ -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')); | |||
@@ -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 () { | |||