Commit 33ce6378 authored by ale's avatar ale

support optional authentication to protect the certificate generation

parent b46b7066
......@@ -71,3 +71,15 @@ VPN_CA_SUBJECT
In this latter case, the autoca web application will be available
under the /ca/ URL prefix.
The VPN web application supports authentication (to control who has
access to the certificate generation). To enable it, define the
following variables:
AUTH_ENABLE
Set this to True to enable authentication
AUTH_FUNCTION
Set this to a function that should accept two arguments (username
and password) and return a True/Fals result.
......@@ -57,3 +57,12 @@ a:hover {
.footer a {
text-decoration: none;
}
input {
font-size: 1.2em;
}
.center {
text-align: center;
}
......@@ -15,7 +15,7 @@
<div style="display:none;">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
</div>
<p style="text-align:center;">
<p class="center">
<input type="submit" value=" Generate new SSL certificate ">
</p>
</form>
......
{% extends "_base.html" %}
{% block content %}
<p>
Enter your authentication credentials:
</p>
{% if error %}
<p class="center" style="color:red;">{{ error }}</p>
{% endif %}
<form action="{{ url_for('vpn_admin.login') }}" method="post">
<div style="display:none;">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
</div>
<p class="center">
<input type="text" size="20" placeholder="Email"
name="username" value="{{ username }}"><br>
<input type="password" size="20" placeholder="Password"
name="password"><br>
<input type="submit" value="Login">
</p>
</form>
<p>
<b>NOTE:</b> the credentials are only used to limit access
to this application. They will not be saved, nor they will
ever be associated with the VPN connection.
</p>
{% endblock %}
......@@ -10,7 +10,7 @@ from autoca import ca
from autoca import ca_app
from autoca import ca_stub
from flask import Blueprint, Flask, abort, redirect, request, make_response, \
render_template, session, g, current_app
render_template, session, g, current_app, url_for
vpn_admin = Blueprint('vpn_admin', __name__)
log = logging.getLogger(__name__)
......@@ -118,15 +118,28 @@ Further info:
'''
def csrf(fn):
def csrf(methods=('POST',)):
def _csrf(fn):
@functools.wraps(fn)
def _csrf_wrapper(*args, **kwargs):
if request.method in methods:
query_args = (request.method == 'POST') and request.form or request.args
token = session.pop('_csrf', None)
if not token or token != query_args.get('_csrf'):
abort(400)
return fn(*args, **kwargs)
return _csrf_wrapper
return _csrf
def auth(fn):
@functools.wraps(fn)
def _csrf_wrapper(*args, **kwargs):
query_args = (request.method == 'POST') and request.form or request.args
token = session.pop('_csrf', None)
if not token or token != query_args.get('_csrf'):
abort(400)
def _auth_wrapper(*args, **kwargs):
if current_app.config.get('AUTH_ENABLE'):
if not session.get('logged_in'):
return redirect(url_for('vpn_admin.login'))
return fn(*args, **kwargs)
return _csrf_wrapper
return _auth_wrapper
def generate_csrf_token():
......@@ -140,29 +153,47 @@ def set_ca_wrapper():
g.ca = current_app.ca
@vpn_admin.before_request
def check_userlogin():
g.username = None
if 'username' in session:
g.username = session['username']
@vpn_admin.route('/')
def index():
return render_template('index.html')
@vpn_admin.route('/newcert', methods=['POST'])
@csrf
@vpn_admin.route('/login', methods=['GET', 'POST'])
@csrf()
def login():
error = None
username = ''
if request.method == 'POST':
username = request.form.get('username', '')
password = request.form.get('password', '')
if current_app.config['AUTH_FUNCTION'](username, password):
session['logged_in'] = True
return redirect(url_for('vpn_admin.new_cert',
_csrf=generate_csrf_token()))
else:
error = 'Authentication failed'
return render_template('login.html', error=error, username=username)
@vpn_admin.route('/logout')
def logout():
session.pop('logged_in', None)
return redirect(url_for('vpn_admin.index'))
@vpn_admin.route('/newcert', methods=['GET', 'POST'])
@auth
@csrf(methods=['GET', 'POST'])
def new_cert():
session['dl_ok'] = 'y'
session['dl_ok'] = True
return render_template('download.html')
@vpn_admin.route('/newcertdl')
@csrf
@auth
@csrf(methods=['GET'])
def new_cert_dl():
if session.pop('dl_ok', None) != 'y':
if not session.pop('dl_ok', None):
return render_template('download_retry.html')
# Create and sign a certificate.
......@@ -257,6 +288,8 @@ if __name__ == '__main__':
built by <a href="http://www.autistici.org/">autistici.org</a>
</p>
''',
'AUTH_ENABLE': True,
'AUTH_FUNCTION': lambda x, y: (x and y and x == y),
}).run(port=4000)
finally:
shutil.rmtree(ca_dir)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment