From d286592732fbebee3676912387d4512733b296b3 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 23 Oct 2019 13:09:51 +0200 Subject: [HTTP API] Use a decorator for functions that require authentication. --- src/services/vyos-http-api-server | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'src/services') diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index afab9be70..63e67e855 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -27,6 +27,8 @@ import vyos.config import bottle +from functools import wraps + from vyos.configsession import ConfigSession, ConfigSessionError from vyos.config import VyOSError @@ -61,16 +63,23 @@ def success(data): resp = {"success": True, "data": data, "error": None} return json.dumps(resp) +def auth_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + key = bottle.request.forms.get("key") + api_keys = app.config['vyos_keys'] + id = check_auth(api_keys, key) + if not id: + return error(401, "Valid API key is required") + return f(*args, **kwargs) + + return decorated_function + @app.route('/configure', method='POST') +@auth_required def configure(): session = app.config['vyos_session'] config = app.config['vyos_config'] - api_keys = app.config['vyos_keys'] - - key = bottle.request.forms.get("key") - id = check_auth(api_keys, key) - if not id: - return error(401, "Valid API key is required") strict_field = bottle.request.forms.get("strict") if strict_field == "true": @@ -177,17 +186,11 @@ def configure(): return success(None) @app.route('/retrieve', method='POST') +@auth_required def get_value(): config = app.config['vyos_config'] session = app.config['vyos_session'] - api_keys = app.config['vyos_keys'] - - key = bottle.request.forms.get("key") - id = check_auth(api_keys, key) - if not id: - return error(401, "Valid API key is required") - command = bottle.request.forms.get("data") command = json.loads(command) -- cgit v1.2.3 From 3f8884587ab1291c13f633b079058879241ac3c1 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 23 Oct 2019 15:41:58 +0200 Subject: [HTTP API] Add endpoints for config file and image management. --- python/vyos/configsession.py | 18 +++++++++ src/conf_mode/https.py | 2 +- src/services/vyos-http-api-server | 77 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) (limited to 'src/services') diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 09fae78a1..ed6288939 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -25,6 +25,9 @@ COMMIT = '/opt/vyatta/sbin/my_commit' DISCARD = '/opt/vyatta/sbin/my_discard' SHOW_CONFIG = ['/bin/cli-shell-api', 'showConfig'] LOAD_CONFIG = ['/bin/cli-shell-api', 'loadFile'] +SAVE_CONFIG = ['/opt/vyatta/sbin/vyatta-save-config.pl'] +INSTALL_IMAGE = ['/opt/vyatta/sbin/install-image'] +REMOVE_IMAGE = ['/opt/vyatta/bin/vyatta-boot-image.pl', '--del'] # Default "commit via" string APP = "vyos-http-api" @@ -36,6 +39,8 @@ APP = "vyos-http-api" def inject_vyos_env(env): env['VYATTA_CFG_GROUP_NAME'] = 'vyattacfg' env['VYATTA_USER_LEVEL_DIR'] = '/opt/vyatta/etc/shell/level/admin' + env['VYATTA_PROCESS_CLIENT'] = 'gui2_rest' + env['VYOS_HEADLESS_CLIENT'] = 'vyos_http_api' env['vyatta_bindir']= '/opt/vyatta/bin' env['vyatta_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates' env['vyatta_configdir'] = '/opt/vyatta/config' @@ -160,3 +165,16 @@ class ConfigSession(object): def load_config(self, file_path): out = self.__run_command(LOAD_CONFIG + [file_path]) return out + + def save_config(self, file_path): + out = self.__run_command(SAVE_CONFIG + [file_path]) + return out + + def install_image(self, url): + out = self.__run_command(INSTALL_IMAGE + [url]) + return out + + def remove_image(self, name): + out = self.__run_command(REMOVE_IMAGE + [name]) + return out + diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index f948063e9..233c815bc 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -67,7 +67,7 @@ server { {% endif %} # proxy settings for HTTP API, if enabled; 503, if not - location ~ /(retrieve|configure) { + location ~ /(retrieve|configure|config-file|image) { {% if api %} proxy_pass http://localhost:{{ api.port }}; proxy_buffering off; diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index 63e67e855..04c44c2be 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -34,7 +34,6 @@ from vyos.config import VyOSError DEFAULT_CONFIG_FILE = '/etc/vyos/http-api.conf' - CFG_GROUP = 'vyattacfg' app = bottle.default_app() @@ -223,6 +222,82 @@ def get_value(): return success(res) +@app.route('/config-file', method='POST') +@auth_required +def config_file_op(): + config = app.config['vyos_config'] + session = app.config['vyos_session'] + + command = bottle.request.forms.get("data") + command = json.loads(command) + + try: + op = command['op'] + except KeyError: + return error(400, "Missing required field \"op\"") + + try: + if op == 'save': + try: + path = command['file'] + except KeyError: + path = '/config/config.boot' + res = session.save_config(path) + elif op == 'load': + try: + path = command['file'] + except KeyError: + return error(400, "Missing required field \"file\"") + res = session.load_config(path) + res = session.commit() + else: + return error(400, "\"{0}\" is not a valid operation".format(op)) + except VyOSError as e: + return error(400, str(e)) + except Exception as e: + print(traceback.format_exc(), file=sys.stderr) + return error(500, "An internal error occured. Check the logs for details.") + + return success(res) + +@app.route('/image', method='POST') +@auth_required +def config_file_op(): + config = app.config['vyos_config'] + session = app.config['vyos_session'] + + command = bottle.request.forms.get("data") + command = json.loads(command) + + try: + op = command['op'] + except KeyError: + return error(400, "Missing required field \"op\"") + + try: + if op == 'add': + try: + url = command['url'] + except KeyError: + return error(400, "Missing required field \"url\"") + res = session.install_image(url) + elif op == 'delete': + try: + name = command['name'] + except KeyError: + return error(400, "Missing required field \"name\"") + res = session.remove_image(name) + else: + return error(400, "\"{0}\" is not a valid operation".format(op)) + except VyOSError as e: + return error(400, str(e)) + except Exception as e: + print(traceback.format_exc(), file=sys.stderr) + return error(500, "An internal error occured. Check the logs for details.") + + return success(res) + + if __name__ == '__main__': # systemd's user and group options don't work, do it by hand here, # else no one else will be able to commit -- cgit v1.2.3