Development

This commit is contained in:
Matthew Grotke 2026-05-27 22:04:04 -04:00
parent eed1d295dc
commit d9f3bd8289
45 changed files with 635 additions and 666 deletions

View file

@ -1,3 +1,4 @@
from pathlib import Path
import copy
import re
from flask import Blueprint, request, redirect, flash
@ -6,9 +7,9 @@ from config_utils import load_config, save_config_with_snapshot, verify_config_h
import sanitize
import validation as validate
bp = Blueprint('dnsblocking', __name__)
_PAGE = Path(__file__).parent.name
VIEW = '/view/view_dnsblocking'
bp = Blueprint(_PAGE, __name__)
_VALID_FORMATS_STR = ', '.join(sorted(validate.VALID_BLOCKLIST_FORMATS))
@ -52,22 +53,22 @@ def _parse_fields():
return {'name': name, 'description': description, 'format': fmt, 'url': url}, None
@bp.route('/action/dnsblocking_tableblocklists_rowdelete', methods=['POST'])
@bp.route('/action/dnsblocking/blocklists_delete', methods=['POST'])
@require_level('administrator')
def dnsblocking_tableblocklists_rowdelete():
def blocklists_delete():
idx = _row_index()
if idx is None:
flash('Invalid request.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
if not _hash_ok():
return redirect(VIEW)
return redirect(f'/{_PAGE}')
cfg = load_config()
items = cfg.get('dns_blocking', {}).get('blocklists', [])
if idx < 0 or idx >= len(items):
flash('Entry not found.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
before = copy.deepcopy(items[idx])
name = before.get('name', str(idx))
@ -76,36 +77,36 @@ def dnsblocking_tableblocklists_rowdelete():
if errors:
for msg in errors:
flash(msg, 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
flash(save_config_with_snapshot(
cfg, path='dns_blocking', key=name, operation='delete',
before=before, after=None,
description=f'Deleted blocklist: {name}',
queue=False,
), 'success')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
@bp.route('/action/dnsblocking_tableblocklists_rowedit', methods=['POST'])
@bp.route('/action/dnsblocking/blocklists_edit', methods=['POST'])
@require_level('administrator')
def dnsblocking_tableblocklists_rowedit():
def blocklists_edit():
idx = _row_index()
if idx is None:
flash('Invalid request.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
fields, err = _parse_fields()
if err:
return redirect(VIEW)
return redirect(f'/{_PAGE}')
if not _hash_ok():
return redirect(VIEW)
return redirect(f'/{_PAGE}')
cfg = load_config()
items = cfg.get('dns_blocking', {}).get('blocklists', [])
if idx < 0 or idx >= len(items):
flash('Entry not found.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
before = copy.deepcopy(items[idx])
items[idx].update({
@ -118,32 +119,32 @@ def dnsblocking_tableblocklists_rowedit():
if errors:
for msg in errors:
flash(msg, 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
flash(save_config_with_snapshot(
cfg, path='dns_blocking', key=fields['name'], operation='edit',
before=before, after=copy.deepcopy(items[idx]),
description=f'Edited blocklist: {fields["name"]}',
queue=False,
), 'success')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
@bp.route('/action/dnsblocking_cardaddblocklist_add', methods=['POST'])
@bp.route('/action/dnsblocking/addblocklist_add', methods=['POST'])
@require_level('administrator')
def dnsblocking_cardaddblocklist_add():
def addblocklist_add():
fields, err = _parse_fields()
if err:
return redirect(VIEW)
return redirect(f'/{_PAGE}')
if not _hash_ok():
return redirect(VIEW)
return redirect(f'/{_PAGE}')
cfg = load_config()
blocklists = cfg.setdefault('dns_blocking', {}).setdefault('blocklists', [])
if any(b.get('name', '').lower() == fields['name'].lower() for b in blocklists):
flash('The configuration has not been saved because a blocklist with that name already exists.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
entry = {
'name': fields['name'],
@ -157,28 +158,28 @@ def dnsblocking_cardaddblocklist_add():
if errors:
for msg in errors:
flash(msg, 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
flash(save_config_with_snapshot(
cfg, path='dns_blocking', key=fields['name'], operation='add',
before=None, after=copy.deepcopy(entry),
description=f'Added blocklist: {fields["name"]}',
queue=False,
), 'success')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
@bp.route('/action/dnsblocking_cardblocklistrefresh_save', methods=['POST'])
@bp.route('/action/dnsblocking/blocklistrefresh_save', methods=['POST'])
@require_level('administrator')
def dnsblocking_cardblocklistrefresh_save():
def blocklistrefresh_save():
daily_execute_time = validate.time_24h(sanitize.time_24h(request.form.get('daily_execute_time_24hr_local', '')))
if not daily_execute_time:
flash('Daily Refresh Time must be a valid 24-hour time (e.g. 02:30).', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
if not verify_config_hash(request.form.get('config_hash', '')):
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
cfg = load_config()
before = copy.deepcopy(cfg.get('dns_blocking', {}).get('general', {}))
@ -189,30 +190,30 @@ def dnsblocking_cardblocklistrefresh_save():
description='Updated daily blocklist refresh time',
cmd='core apply',
), 'success')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
@bp.route('/action/dnsblocking_cardblocklistrefresh_refreshnow', methods=['POST'])
@bp.route('/action/dnsblocking/blocklistrefresh_refresh', methods=['POST'])
@require_level('administrator')
def dnsblocking_cardblocklistrefresh_refreshnow():
def blocklistrefresh_refresh():
flash(queued_msg('core update-blocklists', action_label='Blocklist refresh queued'), 'success')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
@bp.route('/action/dnsblocking_cardlogging_save', methods=['POST'])
@bp.route('/action/dnsblocking/logging_save', methods=['POST'])
@require_level('administrator')
def dnsblocking_cardlogging_save():
def logging_save():
log_max_kb_raw = request.form.get('log_max_kb', '').strip()
log_errors_only = 'log_errors_only' in request.form
log_max_kb = validate.int_range(log_max_kb_raw, 64, None)
if log_max_kb is None:
flash('Max Log Size must be a number >= 64.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
if not verify_config_hash(request.form.get('config_hash', '')):
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
cfg = load_config()
before = copy.deepcopy(cfg.get('dns_blocking', {}).get('general', {}))
@ -224,11 +225,11 @@ def dnsblocking_cardlogging_save():
if errors:
for msg in errors:
flash(msg, 'error')
return redirect(VIEW)
return redirect(f'/{_PAGE}')
flash(save_config_with_snapshot(
cfg, path='dns_blocking', key='general', operation='edit',
before=before, after=copy.deepcopy(cfg['dns_blocking']['general']),
description='Updated DNS blocking log settings',
queue=False,
), 'success')
return redirect(VIEW)
return redirect(f'/{_PAGE}')

View file

@ -1,5 +1,4 @@
{
"id": "view_dnsblocking",
"client_requirement": "client_is_viewer+",
"items": [
{
@ -42,7 +41,7 @@
"row_actions": [
{
"client_requirement": "client_is_administrator+",
"action": "/action/dnsblocking_tableblocklists_rowedit",
"action": "/action/dnsblocking/blocklists_edit",
"method": "inline_edit",
"text": "Edit",
"class": "btn-ghost btn-sm",
@ -70,7 +69,7 @@
},
{
"client_requirement": "client_is_administrator+",
"action": "/action/dnsblocking_tableblocklists_rowdelete",
"action": "/action/dnsblocking/blocklists_delete",
"method": "post",
"text": "Delete",
"class": "btn-danger btn-sm"
@ -85,7 +84,7 @@
"items": [
{
"type": "form",
"action": "/action/dnsblocking_cardaddblocklist_add",
"action": "/action/dnsblocking/addblocklist_add",
"method": "post",
"items": [
{
@ -123,7 +122,7 @@
"items": [
{
"type": "button_primary",
"action": "/action/dnsblocking_cardaddblocklist_add",
"action": "/action/dnsblocking/addblocklist_add",
"method": "post",
"text": "Add Blocklist"
},
@ -154,7 +153,7 @@
"items": [
{
"type": "button_secondary",
"action": "/action/dnsblocking_cardblocklistrefresh_refreshnow",
"action": "/action/dnsblocking/blocklistrefresh_refresh",
"method": "post",
"text": "Refresh All Now"
}
@ -165,7 +164,7 @@
},
{
"type": "form",
"action": "/action/dnsblocking_cardblocklistrefresh_save",
"action": "/action/dnsblocking/blocklistrefresh_save",
"method": "post",
"items": [
{
@ -183,7 +182,7 @@
"items": [
{
"type": "button_primary",
"action": "/action/dnsblocking_cardblocklistrefresh_save",
"action": "/action/dnsblocking/blocklistrefresh_save",
"method": "post",
"text": "Save"
},
@ -204,7 +203,7 @@
"items": [
{
"type": "form",
"action": "/action/dnsblocking_cardlogging_save",
"action": "/action/dnsblocking/logging_save",
"method": "post",
"items": [
{
@ -229,7 +228,7 @@
"items": [
{
"type": "button_primary",
"action": "/action/dnsblocking_cardlogging_save",
"action": "/action/dnsblocking/logging_save",
"method": "post",
"text": "Save"
},