Consolidate error handling
Do or do not; there is no try. With some exceptions.
This commit is contained in:
		
							parent
							
								
									8c824fe9e6
								
							
						
					
					
						commit
						a5df435931
					
				
					 5 changed files with 106 additions and 159 deletions
				
			
		| 
						 | 
				
			
			@ -81,6 +81,15 @@ def create_app(test_config=None):
 | 
			
		|||
    def unauth_handler():
 | 
			
		||||
        return flask.redirect(flask.url_for('auth.login', next=flask.request.endpoint))
 | 
			
		||||
 | 
			
		||||
    @app.errorhandler(TimeoutError)
 | 
			
		||||
    def timeout_error(e):
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
 | 
			
		||||
    @app.errorhandler(Exception)
 | 
			
		||||
    def internal_server_error(e):
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=500, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
    @app.route('/')
 | 
			
		||||
    @flask_login.login_required
 | 
			
		||||
    def home():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,43 +11,29 @@ blueprint = flask.Blueprint('config', __name__, url_prefix='/config')
 | 
			
		|||
@blueprint.route('/', methods=('GET', 'POST'))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
def index():
 | 
			
		||||
    try:
 | 
			
		||||
        if not flask_login.current_user.is_admin:
 | 
			
		||||
            return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
    if not flask_login.current_user.is_admin:
 | 
			
		||||
        return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
        with db.locked():
 | 
			
		||||
            settings = db.read('settings')
 | 
			
		||||
 | 
			
		||||
            if flask.request.method == 'POST':
 | 
			
		||||
                form = flask.request.form
 | 
			
		||||
                for name, value in form.items():
 | 
			
		||||
                    if name in settings:
 | 
			
		||||
                        settings[name] = value
 | 
			
		||||
                    db.write('settings', settings)
 | 
			
		||||
                system.run(system.save_config)
 | 
			
		||||
                return flask.redirect(flask.url_for('config.index'))
 | 
			
		||||
 | 
			
		||||
            return flask.render_template('config/index.html', settings=settings)
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
    with db.locked():
 | 
			
		||||
        settings = db.read('settings')
 | 
			
		||||
        if flask.request.method == 'POST':
 | 
			
		||||
            form = flask.request.form
 | 
			
		||||
            for name, value in form.items():
 | 
			
		||||
                if name in settings:
 | 
			
		||||
                    settings[name] = value
 | 
			
		||||
                db.write('settings', settings)
 | 
			
		||||
            system.run(system.save_config)
 | 
			
		||||
            return flask.redirect(flask.url_for('config.index'))
 | 
			
		||||
        return flask.render_template('config/index.html', settings=settings)
 | 
			
		||||
 | 
			
		||||
@blueprint.route('/edit/<name>', methods=('GET', 'POST'))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
def edit(name):
 | 
			
		||||
    try:
 | 
			
		||||
        if not flask_login.current_user.is_admin:
 | 
			
		||||
            return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
        if flask.request.method == 'POST':
 | 
			
		||||
            form = flask.request.form
 | 
			
		||||
            db.save(name, json.loads(form.get('text').replace('\r\n', '\n')))
 | 
			
		||||
            system.run(system.save_config)
 | 
			
		||||
        content = json.dumps(db.load(name), indent=2)
 | 
			
		||||
        return flask.render_template('config/edit.html', **locals())
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
    if not flask_login.current_user.is_admin:
 | 
			
		||||
        return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
    if flask.request.method == 'POST':
 | 
			
		||||
        form = flask.request.form
 | 
			
		||||
        db.save(name, json.loads(form.get('text').replace('\r\n', '\n')))
 | 
			
		||||
        system.run(system.save_config)
 | 
			
		||||
    content = json.dumps(db.load(name), indent=2)
 | 
			
		||||
    return flask.render_template('config/edit.html', **locals())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										37
									
								
								web/nat.py
									
										
									
									
									
								
							
							
						
						
									
										37
									
								
								web/nat.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -9,27 +9,18 @@ blueprint = flask.Blueprint('nat', __name__, url_prefix='/nat')
 | 
			
		|||
@blueprint.route('/', methods=('GET', 'POST'))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
def index():
 | 
			
		||||
    try:
 | 
			
		||||
        if not flask_login.current_user.is_admin:
 | 
			
		||||
            return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
        with db.locked():
 | 
			
		||||
            nat = { office: "" for office in db.read('networks') }
 | 
			
		||||
            nat |= db.read('nat')
 | 
			
		||||
 | 
			
		||||
            if flask.request.method == 'POST':
 | 
			
		||||
                form = flask.request.form
 | 
			
		||||
                for office, address in form.items():
 | 
			
		||||
                    if office in nat:
 | 
			
		||||
                        nat[office] = address
 | 
			
		||||
                db.write('nat', nat)
 | 
			
		||||
                system.run(system.save_config)
 | 
			
		||||
                return flask.redirect(flask.url_for('nat.index'))
 | 
			
		||||
 | 
			
		||||
            return flask.render_template('nat/index.html', nat=nat)
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
    if not flask_login.current_user.is_admin:
 | 
			
		||||
        return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
    with db.locked():
 | 
			
		||||
        nat = { office: "" for office in db.read('networks') }
 | 
			
		||||
        nat |= db.read('nat')
 | 
			
		||||
        if flask.request.method == 'POST':
 | 
			
		||||
            form = flask.request.form
 | 
			
		||||
            for office, address in form.items():
 | 
			
		||||
                if office in nat:
 | 
			
		||||
                    nat[office] = address
 | 
			
		||||
            db.write('nat', nat)
 | 
			
		||||
            system.run(system.save_config)
 | 
			
		||||
            return flask.redirect(flask.url_for('nat.index'))
 | 
			
		||||
        return flask.render_template('nat/index.html', nat=nat)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										58
									
								
								web/rules.py
									
										
									
									
									
								
							
							
						
						
									
										58
									
								
								web/rules.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -9,29 +9,23 @@ blueprint = flask.Blueprint('rules', __name__, url_prefix='/rules')
 | 
			
		|||
@blueprint.route('/', methods=('GET', 'POST'))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
def index():
 | 
			
		||||
    try:
 | 
			
		||||
        if not flask_login.current_user.is_admin:
 | 
			
		||||
            return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
    if not flask_login.current_user.is_admin:
 | 
			
		||||
        return flask.Response('forbidden', status=403, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
        if flask.request.method == 'POST':
 | 
			
		||||
            with db.locked():
 | 
			
		||||
                rules = db.read('rules')
 | 
			
		||||
                form = flask.request.form
 | 
			
		||||
                oldrules = {rule['name']: rule for rule in rules}
 | 
			
		||||
                rules = []
 | 
			
		||||
                for index, name in sorted(
 | 
			
		||||
                        zip(form.getlist('index'), form.getlist('name')), key=lambda e: int(e[0] or 0)):
 | 
			
		||||
                    if index and name:
 | 
			
		||||
                        rules.append(oldrules.get(name, {'name': name}))
 | 
			
		||||
                db.write('rules', rules)
 | 
			
		||||
            system.run(system.save_config)
 | 
			
		||||
    if flask.request.method == 'POST':
 | 
			
		||||
        with db.locked():
 | 
			
		||||
            rules = db.read('rules')
 | 
			
		||||
            form = flask.request.form
 | 
			
		||||
            oldrules = {rule['name']: rule for rule in rules}
 | 
			
		||||
            rules = []
 | 
			
		||||
            for index, name in sorted(
 | 
			
		||||
                    zip(form.getlist('index'), form.getlist('name')), key=lambda e: int(e[0] or 0)):
 | 
			
		||||
                if index and name:
 | 
			
		||||
                    rules.append(oldrules.get(name, {'name': name}))
 | 
			
		||||
            db.write('rules', rules)
 | 
			
		||||
        system.run(system.save_config)
 | 
			
		||||
 | 
			
		||||
        return flask.render_template('rules/index.html', rules=db.load('rules'))
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
    return flask.render_template('rules/index.html', rules=db.load('rules'))
 | 
			
		||||
 | 
			
		||||
@blueprint.route('/edit/<int:index>', methods=('GET', 'POST'))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
| 
						 | 
				
			
			@ -53,11 +47,6 @@ def edit(index):
 | 
			
		|||
        return flask.render_template('rules/edit.html', index=index, rule=db.load('rules')[index])
 | 
			
		||||
    except IndexError as e:
 | 
			
		||||
        return flask.Response(f'invalid rule: {index}', status=400, mimetype='text/plain')
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
def can_toggle(user, rule):
 | 
			
		||||
    return user.is_admin or not user.groups.isdisjoint(rule.get('managers', ()))
 | 
			
		||||
| 
						 | 
				
			
			@ -65,15 +54,9 @@ def can_toggle(user, rule):
 | 
			
		|||
@blueprint.route('/manage')
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
def manage():
 | 
			
		||||
    try:
 | 
			
		||||
        rules = [rule|{'index': index} for index, rule in enumerate(db.load('rules'))
 | 
			
		||||
                 if can_toggle(flask_login.current_user, rule)]
 | 
			
		||||
        return flask.render_template('rules/manage.html', rules=rules)
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
    rules = [rule|{'index': index} for index, rule in enumerate(db.load('rules'))
 | 
			
		||||
                if can_toggle(flask_login.current_user, rule)]
 | 
			
		||||
    return flask.render_template('rules/manage.html', rules=rules)
 | 
			
		||||
 | 
			
		||||
@blueprint.route('/toggle/<int:index>/<enable>')
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
| 
						 | 
				
			
			@ -89,8 +72,3 @@ def toggle(index, enable):
 | 
			
		|||
        return flask.redirect(flask.url_for('rules.manage'))
 | 
			
		||||
    except IndexError as e:
 | 
			
		||||
        return flask.Response(f'invalid rule: {index}', status=400, mimetype='text/plain')
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										105
									
								
								web/vpn.py
									
										
									
									
									
								
							
							
						
						
									
										105
									
								
								web/vpn.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -21,13 +21,8 @@ def index():
 | 
			
		|||
@blueprint.route('/list')
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
def list():
 | 
			
		||||
    try:
 | 
			
		||||
        user = flask_login.current_user.get_id()
 | 
			
		||||
        return flask.jsonify({k: v for k, v in db.load('wireguard').items() if v.get('user') == user})
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'failed: {e}', status=500, mimetype='text/plain')
 | 
			
		||||
    user = flask_login.current_user.get_id()
 | 
			
		||||
    return flask.jsonify({k: v for k, v in db.load('wireguard').items() if v.get('user') == user})
 | 
			
		||||
 | 
			
		||||
@blueprint.route('/new', methods=('POST',))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
| 
						 | 
				
			
			@ -36,52 +31,46 @@ def new():
 | 
			
		|||
    if not re.match(wgkey_regex, pubkey):
 | 
			
		||||
        return flask.Response('invalid key', status=400, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        settings = db.load('settings')
 | 
			
		||||
        server_pubkey = subprocess.run([f'wg pubkey'], input=settings.get('wg_key'),
 | 
			
		||||
                text=True, capture_output=True, shell=True).stdout.strip()
 | 
			
		||||
    settings = db.load('settings')
 | 
			
		||||
    server_pubkey = subprocess.run([f'wg pubkey'], input=settings.get('wg_key'),
 | 
			
		||||
            text=True, capture_output=True, shell=True).stdout.strip()
 | 
			
		||||
 | 
			
		||||
        host = ipaddress.ip_interface(settings.get('wg_net', '10.0.0.1/24'))
 | 
			
		||||
        with db.locked():
 | 
			
		||||
            # Find a free address for the new key.
 | 
			
		||||
            ips = db.read('wireguard')
 | 
			
		||||
            for ip in host.network.hosts():
 | 
			
		||||
                if ip != host.ip and str(ip) not in ips:
 | 
			
		||||
                    break
 | 
			
		||||
            else:
 | 
			
		||||
                return flask.Response('no more available IP addresses', status=500, mimetype='text/plain')
 | 
			
		||||
            now = datetime.datetime.utcnow()
 | 
			
		||||
            name = re.sub('[^\w ]', '', flask.request.json.get('name', ''))
 | 
			
		||||
    host = ipaddress.ip_interface(settings.get('wg_net', '10.0.0.1/24'))
 | 
			
		||||
    with db.locked():
 | 
			
		||||
        # Find a free address for the new key.
 | 
			
		||||
        ips = db.read('wireguard')
 | 
			
		||||
        for ip in host.network.hosts():
 | 
			
		||||
            if ip != host.ip and str(ip) not in ips:
 | 
			
		||||
                break
 | 
			
		||||
        else:
 | 
			
		||||
            return flask.Response('no more available IP addresses', status=500, mimetype='text/plain')
 | 
			
		||||
        now = datetime.datetime.utcnow()
 | 
			
		||||
        name = re.sub('[^\w ]', '', flask.request.json.get('name', ''))
 | 
			
		||||
 | 
			
		||||
            ips[str(ip)] = {
 | 
			
		||||
                'key': pubkey,
 | 
			
		||||
                'time': now.timestamp(),
 | 
			
		||||
                'user': flask_login.current_user.get_id(),
 | 
			
		||||
                'name': name,
 | 
			
		||||
            }
 | 
			
		||||
            db.write('wireguard', ips)
 | 
			
		||||
 | 
			
		||||
        # Generate a new config archive for firewall nodes.
 | 
			
		||||
        system.run(system.save_config)
 | 
			
		||||
 | 
			
		||||
        # Template arguments.
 | 
			
		||||
        args = {
 | 
			
		||||
            'server': f'{settings.get("wg_endpoint")}',
 | 
			
		||||
            'port': f'{settings.get("wg_port", 51820)}',
 | 
			
		||||
            'server_key': server_pubkey,
 | 
			
		||||
            'pubkey': pubkey,
 | 
			
		||||
            'ip': str(ip),
 | 
			
		||||
            'timestamp': now,
 | 
			
		||||
        ips[str(ip)] = {
 | 
			
		||||
            'key': pubkey,
 | 
			
		||||
            'time': now.timestamp(),
 | 
			
		||||
            'user': flask_login.current_user.get_id(),
 | 
			
		||||
            'name': name,
 | 
			
		||||
            'add_default': flask.request.json.get('add_default', False),
 | 
			
		||||
            'use_dns': flask.request.json.get('use_dns', True),
 | 
			
		||||
        }
 | 
			
		||||
        return flask.render_template('vpn/wg-fri.conf', **args)
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
        db.write('wireguard', ips)
 | 
			
		||||
 | 
			
		||||
    # Generate a new config archive for firewall nodes.
 | 
			
		||||
    system.run(system.save_config)
 | 
			
		||||
 | 
			
		||||
    # Template arguments.
 | 
			
		||||
    args = {
 | 
			
		||||
        'server': f'{settings.get("wg_endpoint")}',
 | 
			
		||||
        'port': f'{settings.get("wg_port", 51820)}',
 | 
			
		||||
        'server_key': server_pubkey,
 | 
			
		||||
        'pubkey': pubkey,
 | 
			
		||||
        'ip': str(ip),
 | 
			
		||||
        'timestamp': now,
 | 
			
		||||
        'name': name,
 | 
			
		||||
        'add_default': flask.request.json.get('add_default', False),
 | 
			
		||||
        'use_dns': flask.request.json.get('use_dns', True),
 | 
			
		||||
    }
 | 
			
		||||
    return flask.render_template('vpn/wg-fri.conf', **args)
 | 
			
		||||
 | 
			
		||||
@blueprint.route('/del', methods=('POST',))
 | 
			
		||||
@flask_login.login_required
 | 
			
		||||
| 
						 | 
				
			
			@ -90,17 +79,11 @@ def delete():
 | 
			
		|||
    if not wgkey_regex.match(pubkey):
 | 
			
		||||
        return flask.Response('invalid key', status=400, mimetype='text/plain')
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        with db.locked():
 | 
			
		||||
            user = flask_login.current_user.get_id()
 | 
			
		||||
            ips = {k: v for k, v in db.read('wireguard').items() if v.get('user') != user or v.get('key') != pubkey}
 | 
			
		||||
            db.write('wireguard', ips)
 | 
			
		||||
    with db.locked():
 | 
			
		||||
        user = flask_login.current_user.get_id()
 | 
			
		||||
        ips = {k: v for k, v in db.read('wireguard').items() if v.get('user') != user or v.get('key') != pubkey}
 | 
			
		||||
        db.write('wireguard', ips)
 | 
			
		||||
 | 
			
		||||
        system.run(system.save_config)
 | 
			
		||||
    system.run(system.save_config)
 | 
			
		||||
 | 
			
		||||
        return flask.Response(f'deleted key {pubkey}', status=200, mimetype='text/plain')
 | 
			
		||||
    except TimeoutError:
 | 
			
		||||
        return flask.render_template('busy.html')
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return flask.Response(f'something went catastrophically wrong: {e}',
 | 
			
		||||
                status=400, mimetype='text/plain')
 | 
			
		||||
    return flask.Response(f'deleted key {pubkey}', status=200, mimetype='text/plain')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue