From 9215931ceb1053ddd390f3fe51bb7b18f24c1ece Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 7 Jul 2019 00:08:02 -0400 Subject: [PATCH] Support token authentication with login/logout --- client-api/pvc-api.py | 58 ++++++++++++++++++++++++++-------- client-api/pvc-api.sample.yaml | 11 ++++--- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/client-api/pvc-api.py b/client-api/pvc-api.py index 7bfbe843..278272a1 100755 --- a/client-api/pvc-api.py +++ b/client-api/pvc-api.py @@ -53,7 +53,7 @@ try: 'coordinators': o_config['pvc']['coordinators'], 'listen_address': o_config['pvc']['api']['listen_address'], 'listen_port': int(o_config['pvc']['api']['listen_port']), - 'authentication_key': o_config['pvc']['api']['authentication']['key'], + 'authentication_tokens': o_config['pvc']['api']['authentication']['tokens'], 'secret_key': o_config['pvc']['api']['secret_key'], 'ssl_enabled': o_config['pvc']['api']['ssl']['enabled'], 'ssl_key_file': o_config['pvc']['api']['ssl']['key_file'], @@ -70,25 +70,53 @@ api.config["SECRET_KEY"] = config['secret_key'] def authenticator(function): def authenticate(*args, **kwargs): - request_values = flask.request.values - if config['authentication_key']: - if 'key' in request_values: - if request_values['key'] == config['authentication_key']: + # Check if authentication is enabled + if not config['authentication_tokens']: + return function(*args, **kwargs) + else: + # Session-based authentication + if 'token' in flask.session: + return function(*args, **kwargs) + # Direct token-based authentication + if 'token' in flask.request.values: + if any(token for token in config['authentication_tokens'] if flask.request.values['token'] in token['token']): return function(*args, **kwargs) else: - return flask.jsonify({"message":"Authentication required"}), 401 - else: - return flask.jsonify({"message":"Authentication required"}), 401 - else: - return function(*args, **kwargs) + return flask.jsonify({"message":"Authentication failed"}), 401 + + return flask.jsonify({"message":"Authentication required"}), 401 + authenticate.__name__ = function.__name__ return authenticate @api.route('/api/v1', methods=['GET']) -@authenticator def api_root(): return flask.jsonify({"message":"PVC API version 1"}), 209 +@api.route('/api/v1/auth/login', methods=['GET', 'POST']) +def api_auth_login(): + if flask.request.method == 'POST': + if any(token for token in config['authentication_tokens'] if flask.request.values['token'] in token['token']): + flask.session['token'] = flask.request.form['token'] + return flask.redirect(flask.url_for('api_root')) + else: + return flask.jsonify({"message":"Authentication failed"}), 401 + return ''' +
+

+ Enter your authentication token: + + +

+
+ ''' + +@api.route('/api/v1/auth/logout', methods=['GET', 'POST']) +def api_auth_logout(): + # remove the username from the session if it's there + flask.session.pop('token', None) + return flask.redirect(flask.url_for('api_root')) + # # Node endpoints # @@ -921,11 +949,15 @@ def api_ceph_volume_snapshot_remove(pool, volume, snapshot): # # Entrypoint # -if config['api_ssl_enabled']: +if config['ssl_enabled']: # Run the WSGI server with SSL http_server = gevent.pywsgi.WSGIServer((config['listen_address'], config['listen_port']), api, keyfile=config['ssl_key_file'], certfile=config['ssl_cert_file']) else: # Run the ?WSGI server without SSL http_server = gevent.pywsgi.WSGIServer((config['listen_address'], config['listen_port']), api) -http_server.serve_forever() + +if os.environ['PVC_DEBUG']: + api.run(host=config['listen_address'], port=config['listen_port']) +else: + http_server.serve_forever() diff --git a/client-api/pvc-api.sample.yaml b/client-api/pvc-api.sample.yaml index cd75c8fd..d9bee650 100644 --- a/client-api/pvc-api.sample.yaml +++ b/client-api/pvc-api.sample.yaml @@ -21,10 +21,13 @@ pvc: listen_port: "7370" # authentication: Authentication and security settings authentication: - # key: A secure key to authorize against the API; must be sent in the body - # arguments or in the URI of each request; leave blank for no authentication - key: "" - # secret_key: Random, per-cluster secret key for the Flask API cookies; generate with uuidgen or pwgen + # tokens: a list of authentication tokens; leave as an empty list to disable authentication + tokens: + # description: token description for management + - description: "testing" + # token: random token for authentication; generate with uuidgen or pwgen + token: "" + # secret_key: Per-cluster secret key for API cookies; generate with uuidgen or pwgen secret_key: "" # ssl: SSL configuration ssl: