"""LAN access control and local network address discovery.""" import ipaddress import json import os from pathlib import Path import secrets import subprocess class LanAccess: def __init__(self, config, enabled, port): self.enabled, self.port = enabled, port self.key = '' self.urls = [] if enabled: path = Path(config).with_name('lan-key') path.parent.mkdir(parents=True, exist_ok=True) try: fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) except FileExistsError: self.key = path.read_text().strip() if len(self.key) < 20: raise ValueError('Ungültiger LAN-Zugangscode in ' + str(path)) else: self.key = secrets.token_urlsafe(24) with os.fdopen(fd, 'w') as file: file.write(self.key) try: result = subprocess.run(['ip', '-j', '-4', 'route', 'get', '1.1.1.1'], capture_output=True, text=True, timeout=3) addresses = {entry['prefsrc'] for entry in json.loads(result.stdout) if entry.get('prefsrc')} self.urls = [f'http://{address}:{port}' for address in sorted(addresses)] except (OSError, ValueError, subprocess.TimeoutExpired): pass @staticmethod def local(address): try: return ipaddress.ip_address(address).is_loopback except ValueError: return False def allowed(self, address, supplied): return self.local(address) or (self.enabled and bool(supplied) and secrets.compare_digest(self.key.encode(), supplied.encode())) def info(self, address): return {'enabled': self.enabled, 'urls': self.urls, 'key': self.key if self.local(address) else None}