Files
linux-cloud-router/debian-package/src/usr/lib/vpn-router/vpnrouter_web/common.py
T

86 lines
3.1 KiB
Python

"""What the modules of the web service share: the paths of the configuration and of the state vpn-router-setup writes,
the limit of request bodies, the HTTP statuses of the failures of the router's modules, reading the configuration and
the state, changing a section of the configuration, the state of a unit, and the log."""
import json
import sys
import vpnrouter
import vpnrouter_auth
from vpnrouter_status import run
STATE_FILE = '/run/vpn-router/web.json'
CONFIG_FILE = '/etc/vpn-router/vpn-router.json'
#: The largest request body accepted, in bytes; a CSR or a certificate chain in PEM is a few kilobytes.
BODY_LIMIT = 65536
#: The HTTP status for each status of a vpnrouter_users.Failure; any other is 500.
USER_STATUS = {2: 400, 3: 404, 4: 409, 5: 401}
#: The HTTP status for each status of a vpnrouter_pki.Failure; any other is 500.
PKI_STATUS = {2: 400, 3: 404, 4: 409}
#: The HTTP status for each status of a vpnrouter_sites.Failure; any other is 500.
SITE_STATUS = {2: 400, 3: 404, 4: 409}
#: The bytes in a MiB, the unit of sites.upload_max_mb.
MIB = 1024 * 1024
def read_json(path):
"""The object in a JSON file, or {} when the file is missing or holds no object."""
try:
with open(path, encoding='utf-8') as handle:
data = json.load(handle)
except (OSError, ValueError):
return {}
return data if isinstance(data, dict) else {}
def section(configuration, name):
"""A section of the configuration, or {} when it is missing or not an object."""
value = configuration.get(name)
return value if isinstance(value, dict) else {}
def current():
"""What a request needs, read now: the features and facts setup resolved, and the signing key.
Before setup has run the router has no features, and without a key the protected paths and
/auth are not served.
"""
return dict(read_json(STATE_FILE), key=vpnrouter_auth.key())
def unit_state(unit):
result = run('systemctl', 'is-active', unit)
return result.stdout.strip() if result and result.stdout.strip() else 'unknown'
def section_change(name, body):
"""Change any of the settings of the configuration's section as /backup changes its own; the section, or the HTTP
status and the error document as a tuple."""
settings = {option: body[option] for option in vpnrouter.SCHEMA[name] if option in body}
try:
with vpnrouter.config_lock():
data = vpnrouter.read_settings()
current = data.get(name)
data[name] = {**(current if isinstance(current, dict) else {}), **settings}
cfg = vpnrouter.with_defaults(data)
vpnrouter.validate(cfg)
vpnrouter.save_config(data)
except vpnrouter.ConfigError as exc:
return 400, {'error': str(exc)}
except OSError as exc:
return 500, {'error': f'the configuration cannot be written: {exc}'}
except SystemExit as exc:
return 500, {'error': str(exc.code)}
run('systemctl', 'restart', '--no-block', 'vpn-router-setup.service')
return cfg[name]
def log(message):
print(f'vpn-router-web: {message}', file=sys.stderr, flush=True)