80 lines
2.7 KiB
Python
Executable File
80 lines
2.7 KiB
Python
Executable File
#!/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:]))
|