diff --git a/docker/routlin-dash/app/pages/radius/action.py b/docker/routlin-dash/app/pages/radius/action.py index 69b4917..77bbc69 100644 --- a/docker/routlin-dash/app/pages/radius/action.py +++ b/docker/routlin-dash/app/pages/radius/action.py @@ -1,7 +1,6 @@ import copy import gzip import io -import ipaddress import os import re from pathlib import Path @@ -40,7 +39,7 @@ def regenerate(): def options_save(): mac_format = request.form.get('mac_format', 'aabbccddeeff') apply_to = request.form.get('apply_to', 'all') - ap_ips_raw = request.form.get('ap_ips', '') + ap_ips = request.form.getlist('ap_ips') if mac_format not in VALID_MAC_FORMATS: flash('Invalid MAC format.', 'error') @@ -49,15 +48,17 @@ def options_save(): flash('Invalid apply_to value.', 'error') return redirect(f'/{_PAGE}') - ap_ips = [line.strip() for line in ap_ips_raw.splitlines() if line.strip()] + cfg = load_config() + valid_ips = { + r['ip'] for r in cfg.get('dhcp_reservations', []) + if r.get('radius_client') is True + and r.get('ip') and r.get('ip') not in ('', 'dynamic') + } for ip in ap_ips: - try: - ipaddress.IPv4Address(ip) - except ValueError: - flash(f'Invalid IP address: {ip}', 'error') + if ip not in valid_ips: + flash(f'Invalid AP selection: {ip}', 'error') return redirect(f'/{_PAGE}') - cfg = load_config() before = copy.deepcopy(cfg.get('radius', {}).get('options', {})) after = {'mac_format': mac_format, 'apply_to': apply_to, 'ap_ips': ap_ips} cfg.setdefault('radius', {})['options'] = after diff --git a/docker/routlin-dash/app/pages/radius/content.json b/docker/routlin-dash/app/pages/radius/content.json index d8fab69..479ba39 100644 --- a/docker/routlin-dash/app/pages/radius/content.json +++ b/docker/routlin-dash/app/pages/radius/content.json @@ -85,13 +85,12 @@ }, { "type": "field", - "label": "Access Point IPs", + "label": "Which of the following RADIUS Clients are Wireless Access Points that you wish to add to the huntgroup?", "name": "ap_ips", - "input_type": "textarea", + "input_type": "checkbox_group", + "options": "%RADIUS_AP_IPS_OPTIONS%", "value": "%RADIUS_AP_IPS%", - "rows": 4, - "hint": "One IP address per line. Used when Apply DEFAULT Rule To is set to huntgroup.", - "placeholder": "192.168.1.10\n192.168.1.11" + "hint": "Used when Apply DEFAULT Rule To is set to huntgroup." }, { "type": "button_row", @@ -117,6 +116,9 @@ "type": "p", "text": "Unknown or unregistered devices are assigned to this VLAN. For wired switch ports, also set the fallback network in the switch configuration." }, + { + "type": "hr" + }, { "type": "form", "action": "/action/radius/default_vlan_save", @@ -153,6 +155,9 @@ "type": "p", "text": "These settings are required for MAC-based 802.1X authentication on managed switches." }, + { + "type": "hr" + }, { "type": "form", "action": "/action/radius/eap_save", diff --git a/docker/routlin-dash/app/pages/radius/view.py b/docker/routlin-dash/app/pages/radius/view.py index c7604ba..621fe3d 100644 --- a/docker/routlin-dash/app/pages/radius/view.py +++ b/docker/routlin-dash/app/pages/radius/view.py @@ -64,9 +64,19 @@ def collect_tokens(cfg): fr = cfg.get('radius', {}) fr_opts = fr.get('options', {}) fr_gen = fr.get('general', {}) - tokens['RADIUS_MAC_FORMAT'] = fr_opts.get('mac_format', 'aabbccddeeff') - tokens['RADIUS_APPLY_TO'] = fr_opts.get('apply_to', 'all') - tokens['RADIUS_AP_IPS'] = '\n'.join(fr_opts.get('ap_ips', [])) + tokens['RADIUS_MAC_FORMAT'] = fr_opts.get('mac_format', 'aabbccddeeff') + tokens['RADIUS_APPLY_TO'] = fr_opts.get('apply_to', 'all') + tokens['RADIUS_AP_IPS'] = json.dumps(fr_opts.get('ap_ips', [])) + + radius_client_reservations = [ + r for r in cfg.get('dhcp_reservations', []) + if r.get('radius_client') is True + and r.get('ip') and r.get('ip') not in ('', 'dynamic') + ] + tokens['RADIUS_AP_IPS_OPTIONS'] = json.dumps([ + {'value': r['ip'], 'label': f"{r.get('description', r['ip'])} ({r['ip']})"} + for r in radius_client_reservations + ]) tokens['RADIUS_LOGGING'] = 'true' if fr_gen.get('logging', False) else '' tokens['RADIUS_LOGGING_HINT'] = 'Unchecking will clear logs.' if fr_gen.get('logging', False) else '' tokens['RADIUS_GEN_LOG_MAX_KB'] = str(fr_gen.get('log_max_kb', 1024))