#!/usr/bin/python3 """Make a token with the admin claim for the router's web service, with vpnrouter_auth. The token is signed with the router's key and is valid for the lifetime given, without a maximum. It may call every protected path, and it is the only token that may change the admins group. The answer is the token on standard output, and a failure is a message on standard error. With --json the answer is {"token", "expires"} on standard output, and a failure is {"error": ...} there too. The exit status is 2 for an invalid request and 1 for any other failure. -h or --help shows the options. vpn-router-token [--json] (--minutes N | --hours N | --days N) """ import argparse import json import sys sys.path.insert(0, '/usr/lib/vpn-router') import vpnrouter_auth #: The seconds of each unit of the lifetime. UNITS = {'minutes': 60, 'hours': 3600, 'days': 86400} class Failure(Exception): """A request that cannot be done; status is 2 invalid, 1 other.""" def __init__(self, message, status=1): super().__init__(message) self.status = status class Parser(argparse.ArgumentParser): """An argument parser whose errors are Failures with status 2, so that --json answers them as JSON.""" def error(self, message): raise Failure(f'{message}; see {self.prog} --help', 2) def positive(value): try: number = int(value) except ValueError: number = 0 if number < 1: raise argparse.ArgumentTypeError(f'{value!r} is not a positive whole number') return number def main(argv): # A request that cannot be parsed is answered in JSON when --json is among the arguments. as_json = '--json' in argv try: parser = Parser(prog='vpn-router-token', description='Make a token with the admin claim for the web service.') parser.add_argument('--json', action='store_true', help='answer in JSON') lifetime = parser.add_mutually_exclusive_group(required=True) for unit in UNITS: lifetime.add_argument(f'--{unit}', type=positive, metavar='N', help=f'valid for N {unit}') options = parser.parse_args(argv) as_json = options.json seconds = next(getattr(options, unit) * factor for unit, factor in UNITS.items() if getattr(options, unit)) answer = vpnrouter_auth.admin_token(seconds) except Failure as failure: message, status = str(failure), failure.status except OSError as exc: message, status = str(exc), 1 else: print(json.dumps(answer) if as_json else answer['token']) return 0 if as_json: print(json.dumps({'error': message})) else: print(f'vpn-router-token: {message}', file=sys.stderr) return status if __name__ == '__main__': sys.exit(main(sys.argv[1:]))