import ipaddress from flask import Blueprint, request, redirect import config_utils bp = Blueprint('portal', __name__) PORTAL_HTML = """\ {title}

{title}

{splash_html} {error_html}
{terms_html}
""" def _vlan_for_ip(client_ip): cfg = config_utils.load_config() try: addr = ipaddress.ip_address(client_ip) except ValueError: return None for vlan in cfg.get('vlans', []): if vlan.get('restricted_vlan') != 'c': continue try: net = ipaddress.ip_network(f"{vlan['ip']}/{vlan['subnet']}", strict=False) if addr in net: return vlan except (KeyError, ValueError): continue return None def _render(vlan, error=None, next_url=''): terms = vlan.get('portal_terms', []) terms_html = ''.join( f'' for i, t in enumerate(terms) ) or '

No terms required.

' return PORTAL_HTML.format( title=vlan.get('portal_splash_title', 'Guest Portal'), splash_html=f'

{vlan["portal_splash_text"]}

' if vlan.get('portal_splash_text') else '', error_html=f'

{error}

' if error else '', terms_html=terms_html, next_url=next_url, ) @bp.route('/', defaults={'path': ''}, methods=['GET', 'POST']) @bp.route('/', methods=['GET', 'POST']) def portal(path): vlan = _vlan_for_ip(request.remote_addr) if vlan is None: return 'Portal unavailable.', 404 if request.method == 'POST': terms = vlan.get('portal_terms', []) for i in range(len(terms)): if not request.form.get(f'term_{i}'): return _render(vlan, error='You must accept all terms to continue.', next_url=request.form.get('next', '')), 200 try: with open(config_utils.CAPTIVE_QUEUE, 'a') as f: f.write(f'allow {request.remote_addr}\n') except OSError: pass return redirect(request.form.get('next') or 'http://routlin.local/', 302) return _render(vlan, next_url=request.args.get('next', '')), 200