import ipaddress
from flask import Blueprint, request, redirect
import config_utils
bp = Blueprint('portal', __name__)
PORTAL_HTML = """\
{title}
{title}
{splash_html}
{error_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