Added support for allowlists.

Updated docstring for merge_blocklists()
This commit is contained in:
Justin Warren 2023-01-14 10:44:08 +11:00
parent 894b133fbb
commit 7a31c3380e
No known key found for this signature in database
1 changed files with 33 additions and 6 deletions

View File

@ -35,6 +35,9 @@ API_CALL_DELAY = 5 * 60 / 300 # 300 calls per 5 minutes
# We always import the domain and the severity # We always import the domain and the severity
IMPORT_FIELDS = ['domain', 'severity'] IMPORT_FIELDS = ['domain', 'severity']
# Allowlists always import these fields
ALLOWLIST_IMPORT_FIELDS = ['domain', 'severity', 'public_comment', 'private_comment', 'reject_media', 'reject_reports', 'obfuscate']
# We always export the domain and the severity # We always export the domain and the severity
EXPORT_FIELDS = ['domain', 'severity'] EXPORT_FIELDS = ['domain', 'severity']
@ -69,6 +72,22 @@ def sync_blocklists(conf: dict):
# Merge blocklists into an update dict # Merge blocklists into an update dict
merged = merge_blocklists(blocklists, conf.mergeplan) merged = merge_blocklists(blocklists, conf.mergeplan)
# Apply allows specified on the commandline
for domain in conf.allow_domains:
log.info(f"Allowing domain '{domain}' specified on commandline.")
merged[domain] = DomainBlock(domain, 'noop')
# Apply allows from URLs lists
if conf.allowlist_url_sources:
log.info("Adding allows from URL lists...")
allowlists = fetch_from_urls({}, conf.allowlist_url_sources, ALLOWLIST_IMPORT_FIELDS)
for key, alist in allowlists.items():
log.debug(f"Processing allows from '{key}'...")
for allowed in alist:
merged[allowed.domain] = allowed
log.debug(f"Allowed domain '{allowed.domain}' from allowlist: {allowed}")
if conf.blocklist_savefile: if conf.blocklist_savefile:
log.info(f"Saving merged blocklist to {conf.blocklist_savefile}") log.info(f"Saving merged blocklist to {conf.blocklist_savefile}")
save_blocklist_to_file(merged.values(), conf.blocklist_savefile, export_fields) save_blocklist_to_file(merged.values(), conf.blocklist_savefile, export_fields)
@ -140,9 +159,12 @@ def fetch_from_instances(blocklists: dict, sources: dict,
def merge_blocklists(blocklists: dict, mergeplan: str='max') -> dict: def merge_blocklists(blocklists: dict, mergeplan: str='max') -> dict:
"""Merge fetched remote blocklists into a bulk update """Merge fetched remote blocklists into a bulk update
@param blocklists: A dict of lists of DomainBlocks, keyed by source.
Each value is a list of DomainBlocks
@param mergeplan: An optional method of merging overlapping block definitions @param mergeplan: An optional method of merging overlapping block definitions
'max' (the default) uses the highest severity block found 'max' (the default) uses the highest severity block found
'min' uses the lowest severity block found 'min' uses the lowest severity block found
@param returns: A dict of DomainBlocks keyed by domain
""" """
merged = {} merged = {}
@ -433,7 +455,7 @@ def update_known_block(token: str, host: str, block: DomainBlock):
response = requests.put(url, response = requests.put(url,
headers=requests_headers(token), headers=requests_headers(token),
data=blockdata, json=blockdata._asdict(),
timeout=REQUEST_TIMEOUT timeout=REQUEST_TIMEOUT
) )
if response.status_code != 200: if response.status_code != 200:
@ -442,14 +464,14 @@ def update_known_block(token: str, host: str, block: DomainBlock):
def add_block(token: str, host: str, blockdata: DomainBlock): def add_block(token: str, host: str, blockdata: DomainBlock):
"""Block a domain on Mastodon host """Block a domain on Mastodon host
""" """
log.debug(f"Blocking domain {blockdata.domain} at {host}...") log.debug(f"Adding block entry for {blockdata.domain} at {host}...")
api_path = "/api/v1/admin/domain_blocks" api_path = "/api/v1/admin/domain_blocks"
url = f"https://{host}{api_path}" url = f"https://{host}{api_path}"
response = requests.post(url, response = requests.post(url,
headers=requests_headers(token), headers=requests_headers(token),
data=blockdata._asdict(), json=blockdata._asdict(),
timeout=REQUEST_TIMEOUT timeout=REQUEST_TIMEOUT
) )
if response.status_code == 422: if response.status_code == 422:
@ -515,6 +537,8 @@ def push_blocklist(token: str, host: str, blocklist: list[dict],
log.info(f"Pushing new block definition: {newblock}") log.info(f"Pushing new block definition: {newblock}")
blockdata = oldblock.copy() blockdata = oldblock.copy()
blockdata.update(newblock) blockdata.update(newblock)
log.debug(f"Block as dict: {blockdata._asdict()}")
if not dryrun: if not dryrun:
update_known_block(token, host, blockdata) update_known_block(token, host, blockdata)
# add a pause here so we don't melt the instance # add a pause here so we don't melt the instance
@ -530,6 +554,7 @@ def push_blocklist(token: str, host: str, blocklist: list[dict],
# This is a new block for the target instance, so we # This is a new block for the target instance, so we
# need to add a block rather than update an existing one # need to add a block rather than update an existing one
log.info(f"Adding new block: {newblock}...") log.info(f"Adding new block: {newblock}...")
log.debug(f"Block as dict: {newblock._asdict()}")
# Make sure the new block doesn't clobber a domain with followers # Make sure the new block doesn't clobber a domain with followers
newblock.severity = check_followed_severity(host, token, newblock.domain, newblock.severity, max_followed_severity) newblock.severity = check_followed_severity(host, token, newblock.domain, newblock.severity, max_followed_severity)
@ -615,9 +640,10 @@ def augment_args(args):
if not args.import_fields: if not args.import_fields:
args.import_fields = conf.get('import_fields', []) args.import_fields = conf.get('import_fields', [])
args.blocklist_url_sources = conf.get('blocklist_url_sources') args.blocklist_url_sources = conf.get('blocklist_url_sources', None)
args.blocklist_instance_sources = conf.get('blocklist_instance_sources') args.blocklist_instance_sources = conf.get('blocklist_instance_sources', None)
args.blocklist_instance_destinations = conf.get('blocklist_instance_destinations') args.allowlist_url_sources = conf.get('allowlist_url_sources', None)
args.blocklist_instance_destinations = conf.get('blocklist_instance_destinations', None)
return args return args
@ -637,6 +663,7 @@ def main():
ap.add_argument('-I', '--import-field', dest='import_fields', action='append', help="Extra blocklist fields to import.") ap.add_argument('-I', '--import-field', dest='import_fields', action='append', help="Extra blocklist fields to import.")
ap.add_argument('-E', '--export-field', dest='export_fields', action='append', help="Extra blocklist fields to export.") ap.add_argument('-E', '--export-field', dest='export_fields', action='append', help="Extra blocklist fields to export.")
ap.add_argument('-A', '--allow', dest="allow_domains", action='append', default=[], help="Override any blocks to allow this domain.")
ap.add_argument('--no-fetch-url', dest='no_fetch_url', action='store_true', help="Don't fetch from URLs, even if configured.") ap.add_argument('--no-fetch-url', dest='no_fetch_url', action='store_true', help="Don't fetch from URLs, even if configured.")
ap.add_argument('--no-fetch-instance', dest='no_fetch_instance', action='store_true', help="Don't fetch from instances, even if configured.") ap.add_argument('--no-fetch-instance', dest='no_fetch_instance', action='store_true', help="Don't fetch from instances, even if configured.")