openvpn: Use app toggle button and common app view

Tests performed:

- When app is not setup, app toggle button is not shown. Running status of the
app is also not present in the page. Profile download is not shown. Setup button
is shown.

- When app is being setup, app toggle button is not shown. Running status of the
app is also not present in the page. Page keeps refreshing every 3 seconds
during setup. Profile download is not shown. A progress spinner is shown that
setup is currently running.

- When app setup has completed, app toggle button is shown. Running status is
shown. When daemon is stopped, a message that daemon is not running is show.
Profile download is shown.

- Transition from being setup into setup completed is done with a single page
refresh. Message that setup is completed is shown.

- Port forwarding information is always shown (before, during and after setup).

- Run functional tests for OpenVPN.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Joseph Nuthalapati <njoseph@riseup.net>
This commit is contained in:
Sunil Mohan Adapa 2020-05-18 21:44:37 -07:00 committed by Joseph Nuthalapati
parent cce51cd9d0
commit 058702f2b8
No known key found for this signature in database
GPG Key ID: 5398F00A2FA43C35
6 changed files with 41 additions and 95 deletions

View File

@ -26,7 +26,6 @@ app_module = {
app_checkbox_id = {
'tor': 'id_tor-enabled',
'openvpn': 'id_openvpn-enabled',
}
default_url = config['DEFAULT']['url']

View File

@ -36,12 +36,19 @@ port_forwarding_info = [('UDP', 1194)]
app = None
setup_process = None
class OpenVPNApp(app_module.App):
"""FreedomBox app for OpenVPN."""
app_id = 'openvpn'
@property
def can_be_disabled(self):
"""Return whether the app can be disabled."""
return is_setup() and not setup_process
def __init__(self):
"""Create components for the app."""
super().__init__()

View File

@ -1,13 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
FreedomBox app for configuring OpenVPN.
"""
from django import forms
from django.utils.translation import ugettext_lazy as _
class OpenVpnForm(forms.Form): # pylint: disable=W0232
"""OpenVPN configuration form."""
enabled = forms.BooleanField(label=_('Enable OpenVPN server'),
required=False)

View File

@ -20,6 +20,10 @@
{% block status %}
{% if status.is_setup and not status.setup_running %}
{{ block.super }}
{% endif %}
{% if not status.is_setup and not status.setup_running %}
<h3>{% trans "Status" %}</h3>
@ -41,7 +45,7 @@
</form>
{% endif %}
{% if not status.is_setup and status.setup_running %}
{% if status.setup_running %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
@ -59,15 +63,11 @@
</p>
{% endif %}
{% if status.is_setup %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block configuration %}
{% if status.is_setup %}
{% if status.is_setup and not status.setup_running %}
<h3>{% trans "Profile" %}</h3>
@ -98,10 +98,6 @@
{% endif %}
{% if status.is_setup %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block page_js %}

View File

@ -6,10 +6,11 @@ URLs for the OpenVPN module.
from django.conf.urls import url
from plinth.utils import non_admin_view
from . import views
urlpatterns = [
url(r'^apps/openvpn/$', views.index, name='index'),
url(r'^apps/openvpn/$', views.OpenVPNAppView.as_view(), name='index'),
url(r'^apps/openvpn/setup/$', views.setup, name='setup'),
url(r'^apps/openvpn/profile/$', non_admin_view(views.profile),
name='profile'),

View File

@ -8,59 +8,45 @@ import logging
from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
from django.views.decorators.http import require_POST
from plinth import actions, daemon
from plinth import actions
from plinth.modules import config, openvpn
from .forms import OpenVpnForm
from plinth.views import AppView
logger = logging.getLogger(__name__)
setup_process = None
class OpenVPNAppView(AppView):
"""Show OpenVPN app main page."""
app_id = 'openvpn'
template_name = 'openvpn.html'
port_forwarding_info = openvpn.port_forwarding_info
def index(request):
"""Serve configuration page."""
status = get_status()
def dispatch(self, request, *args, **kwargs):
"""Collect the result of running setup process."""
if bool(openvpn.setup_process):
_collect_setup_result(request)
if status['setup_running']:
_collect_setup_result(request)
return super().dispatch(request, *args, **kwargs)
form = None
if request.method == 'POST':
form = OpenVpnForm(request.POST, prefix='openvpn')
# pylint: disable=E1101
if form.is_valid():
_apply_changes(request, status, form.cleaned_data)
status = get_status()
form = OpenVpnForm(initial=status, prefix='openvpn')
else:
form = OpenVpnForm(initial=status, prefix='openvpn')
return TemplateResponse(
request, 'openvpn.html', {
'app_id': 'openvpn',
'app_info': openvpn.app.info,
'port_forwarding_info': openvpn.port_forwarding_info,
'status': status,
'form': form,
'is_running': status['is_running'],
'has_diagnostics': True,
'is_enabled': status['enabled'],
})
def get_context_data(self, *args, **kwargs):
"""Add additional context data for template."""
context = super().get_context_data(*args, **kwargs)
context['status'] = {
'is_setup': openvpn.is_setup(),
'setup_running': bool(openvpn.setup_process),
}
return context
@require_POST
def setup(request):
"""Start the setup process."""
global setup_process
if not openvpn.is_setup() and not setup_process:
setup_process = actions.superuser_run('openvpn', ['setup'],
run_in_background=True)
if not openvpn.is_setup() and not openvpn.setup_process:
openvpn.setup_process = actions.superuser_run('openvpn', ['setup'],
run_in_background=True)
openvpn.app.enable()
@ -86,24 +72,12 @@ def profile(request):
return response
def get_status():
"""Get the current settings from OpenVPN server."""
return {
'is_setup': openvpn.is_setup(),
'setup_running': bool(setup_process),
'enabled': openvpn.app.is_enabled(),
'is_running': daemon.app_is_running(openvpn.app)
}
def _collect_setup_result(request):
"""Handle setup process is completion."""
global setup_process
if not setup_process:
if not openvpn.setup_process:
return
return_code = setup_process.poll()
return_code = openvpn.setup_process.poll()
# Setup process is not complete yet
if return_code is None:
@ -114,22 +88,4 @@ def _collect_setup_result(request):
else:
messages.info(request, _('Setup failed.'))
setup_process = None
def _apply_changes(request, old_status, new_status):
"""Apply the changes."""
modified = False
if old_status['enabled'] != new_status['enabled']:
if new_status['enabled']:
openvpn.app.enable()
else:
openvpn.app.disable()
modified = True
if modified:
messages.success(request, _('Configuration updated'))
else:
messages.info(request, _('Setting unchanged'))
openvpn.setup_process = None