Support token authentication with login/logout

This commit is contained in:
Joshua Boniface 2019-07-07 00:08:02 -04:00
parent 8aedd78879
commit 9215931ceb
2 changed files with 52 additions and 17 deletions

View File

@ -53,7 +53,7 @@ try:
'coordinators': o_config['pvc']['coordinators'], 'coordinators': o_config['pvc']['coordinators'],
'listen_address': o_config['pvc']['api']['listen_address'], 'listen_address': o_config['pvc']['api']['listen_address'],
'listen_port': int(o_config['pvc']['api']['listen_port']), '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'], 'secret_key': o_config['pvc']['api']['secret_key'],
'ssl_enabled': o_config['pvc']['api']['ssl']['enabled'], 'ssl_enabled': o_config['pvc']['api']['ssl']['enabled'],
'ssl_key_file': o_config['pvc']['api']['ssl']['key_file'], '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 authenticator(function):
def authenticate(*args, **kwargs): def authenticate(*args, **kwargs):
request_values = flask.request.values # Check if authentication is enabled
if config['authentication_key']: if not config['authentication_tokens']:
if 'key' in request_values:
if request_values['key'] == config['authentication_key']:
return function(*args, **kwargs) return function(*args, **kwargs)
else: else:
return flask.jsonify({"message":"Authentication required"}), 401 # Session-based authentication
else: if 'token' in flask.session:
return flask.jsonify({"message":"Authentication required"}), 401
else:
return function(*args, **kwargs) 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 failed"}), 401
return flask.jsonify({"message":"Authentication required"}), 401
authenticate.__name__ = function.__name__ authenticate.__name__ = function.__name__
return authenticate return authenticate
@api.route('/api/v1', methods=['GET']) @api.route('/api/v1', methods=['GET'])
@authenticator
def api_root(): def api_root():
return flask.jsonify({"message":"PVC API version 1"}), 209 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 '''
<form method="post">
<p>
Enter your authentication token:
<input type=text name=token style='width:24em'>
<input type=submit value=Login>
</p>
</form>
'''
@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 # Node endpoints
# #
@ -921,11 +949,15 @@ def api_ceph_volume_snapshot_remove(pool, volume, snapshot):
# #
# Entrypoint # Entrypoint
# #
if config['api_ssl_enabled']: if config['ssl_enabled']:
# Run the WSGI server with SSL # Run the WSGI server with SSL
http_server = gevent.pywsgi.WSGIServer((config['listen_address'], config['listen_port']), api, http_server = gevent.pywsgi.WSGIServer((config['listen_address'], config['listen_port']), api,
keyfile=config['ssl_key_file'], certfile=config['ssl_cert_file']) keyfile=config['ssl_key_file'], certfile=config['ssl_cert_file'])
else: else:
# Run the ?WSGI server without SSL # Run the ?WSGI server without SSL
http_server = gevent.pywsgi.WSGIServer((config['listen_address'], config['listen_port']), api) http_server = gevent.pywsgi.WSGIServer((config['listen_address'], config['listen_port']), api)
if os.environ['PVC_DEBUG']:
api.run(host=config['listen_address'], port=config['listen_port'])
else:
http_server.serve_forever() http_server.serve_forever()

View File

@ -21,10 +21,13 @@ pvc:
listen_port: "7370" listen_port: "7370"
# authentication: Authentication and security settings # authentication: Authentication and security settings
authentication: authentication:
# key: A secure key to authorize against the API; must be sent in the body # tokens: a list of authentication tokens; leave as an empty list to disable authentication
# arguments or in the URI of each request; leave blank for no authentication tokens:
key: "" # description: token description for management
# secret_key: Random, per-cluster secret key for the Flask API cookies; generate with uuidgen or pwgen - 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: "" secret_key: ""
# ssl: SSL configuration # ssl: SSL configuration
ssl: ssl: