from flask import Blueprint, request, redirect, flash from auth import require_level from config_utils import load_core, save_core, verify_core_hash, queued_msg import sanitize import validation as validate bp = Blueprint('action_apply_banned_ips', __name__) VIEW = '/view/view_banned_ips' def _row_index(): try: return int(request.form.get('row_index', '')) except (ValueError, TypeError): return None def _hash_ok(): if not verify_core_hash(request.form.get('config_hash', '')): flash('Configuration was modified by another session. Please refresh and try again.', 'error') return False return True def _parse_ip(): """Return validated IP string, or None after flashing an error.""" raw = request.form.get('ip', '').strip() if not raw: flash('The configuration has not been saved because an IP address, CIDR, or wildcard pattern is required.', 'error') return None ip = validate.banned_ip(raw) if not ip: flash(f'The configuration has not been saved because "{raw}" is not a valid IP address, CIDR, or wildcard pattern.', 'error') return None return ip @bp.route('/action/add_banned_ip', methods=['POST']) @require_level('administrator') def add_banned_ip(): description = sanitize.text(request.form.get('description', '')) ip = _parse_ip() if ip is None: return redirect(VIEW) if not _hash_ok(): return redirect(VIEW) core = load_core() core.setdefault('banned_ips', []).append({ 'description': description, 'ip': ip, 'enabled': True, }) errors = validate.validate_config(core) if errors: for msg in errors: flash(msg, 'error') return redirect(VIEW) save_core(core) flash(queued_msg('core apply'), 'success') return redirect(VIEW) @bp.route('/action/toggle_banned_ip', methods=['POST']) @require_level('administrator') def toggle_banned_ip(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(VIEW) if not _hash_ok(): return redirect(VIEW) core = load_core() items = core.get('banned_ips', []) if idx < 0 or idx >= len(items): flash('Entry not found.', 'error') return redirect(VIEW) items[idx]['enabled'] = not items[idx].get('enabled', True) errors = validate.validate_config(core) if errors: for msg in errors: flash(msg, 'error') return redirect(VIEW) save_core(core) flash(queued_msg('core apply'), 'success') return redirect(VIEW) @bp.route('/action/edit_banned_ip', methods=['POST']) @require_level('administrator') def edit_banned_ip(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(VIEW) description = sanitize.text(request.form.get('description', '')) ip = _parse_ip() if ip is None: return redirect(VIEW) enabled = request.form.get('enabled') == 'on' if not _hash_ok(): return redirect(VIEW) core = load_core() items = core.get('banned_ips', []) if idx < 0 or idx >= len(items): flash('Entry not found.', 'error') return redirect(VIEW) items[idx].update({'description': description, 'ip': ip, 'enabled': enabled}) errors = validate.validate_config(core) if errors: for msg in errors: flash(msg, 'error') return redirect(VIEW) save_core(core) flash(queued_msg('core apply'), 'success') return redirect(VIEW) @bp.route('/action/delete_banned_ip', methods=['POST']) @require_level('administrator') def delete_banned_ip(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(VIEW) if not _hash_ok(): return redirect(VIEW) core = load_core() items = core.get('banned_ips', []) if idx < 0 or idx >= len(items): flash('Entry not found.', 'error') return redirect(VIEW) removed = items.pop(idx) errors = validate.validate_config(core) if errors: for msg in errors: flash(msg, 'error') return redirect(VIEW) save_core(core) flash(queued_msg('core apply'), 'success') return redirect(VIEW)