From 502cfa49537537b83a81a8be328ab825be38a997 Mon Sep 17 00:00:00 2001 From: fliu <10025-fliu@users.noreply.salsa.debian.org> Date: Wed, 21 Jul 2021 01:47:07 +0000 Subject: [PATCH] email: Add templates for TLS and domains --- plinth/modules/email_server/forms.py | 12 ++ .../email_server/templates/domains.html | 46 ++++++ .../email_server/templates/security.html | 27 ++++ .../email_server/templates/tls_form.html | 69 +++++++++ plinth/modules/email_server/urls.py | 1 + plinth/modules/email_server/views.py | 145 ++++++++++-------- 6 files changed, 240 insertions(+), 60 deletions(-) create mode 100644 plinth/modules/email_server/templates/domains.html create mode 100644 plinth/modules/email_server/templates/security.html create mode 100644 plinth/modules/email_server/templates/tls_form.html diff --git a/plinth/modules/email_server/forms.py b/plinth/modules/email_server/forms.py index 9567d1be1..1c121f862 100644 --- a/plinth/modules/email_server/forms.py +++ b/plinth/modules/email_server/forms.py @@ -13,3 +13,15 @@ class EmailServerForm(forms.Form): class AliasCreationForm(forms.Form): email_name = forms.CharField(label=_('New alias (without @domain)'), max_length=50) + + +class MailnameForm(forms.Form): + mailname = forms.CharField(label=_('New value'), max_length=256) + + +class MydomainForm(forms.Form): + mydomain = forms.CharField(label=_('New value'), max_length=256) + + +class MydestinationForm(forms.Form): + mydestination = forms.CharField(label=_('New value'), max_length=4000) diff --git a/plinth/modules/email_server/templates/domains.html b/plinth/modules/email_server/templates/domains.html new file mode 100644 index 000000000..4f5af63b3 --- /dev/null +++ b/plinth/modules/email_server/templates/domains.html @@ -0,0 +1,46 @@ +{# SPDX-License-Identifier: AGPL-3.0-or-later #} +{% extends "form_base.html" %} +{% load bootstrap %} +{% load i18n %} + +{% block content %} + + {{ block.super }} + +
+ /etc/mailname + = {{ mailname }} +
+ {% csrf_token %} + {{ mailname_form|bootstrap }} + + +
+
+ +
+ $mydomain + = {{ mydomain }} +
+ {% csrf_token %} + {{ mydomain_form|bootstrap }} + + +
+
+ +
+ $mydestination + = {{ mydestination }} +
+ {% csrf_token %} + {{ mydestination_form|bootstrap }} + + +
+
+ +{% endblock %} diff --git a/plinth/modules/email_server/templates/security.html b/plinth/modules/email_server/templates/security.html new file mode 100644 index 000000000..a84bed457 --- /dev/null +++ b/plinth/modules/email_server/templates/security.html @@ -0,0 +1,27 @@ +{# SPDX-License-Identifier: AGPL-3.0-or-later #} +{% extends "form_base.html" %} + +{% load i18n %} + +{% block content %} + + {{ block.super }} + +
+ {% csrf_token %} +
+ {% trans "Postfix TLS" %} + {% include "tls_form.html" with prefix="postfix" proto="smtp" %} +
+ +
+ {% trans "Dovecot TLS" %} + {% include "tls_form.html" with prefix="dovecot" proto="imap" %} +
+ + +
+ +{% endblock %} + diff --git a/plinth/modules/email_server/templates/tls_form.html b/plinth/modules/email_server/templates/tls_form.html new file mode 100644 index 000000000..544f6e55b --- /dev/null +++ b/plinth/modules/email_server/templates/tls_form.html @@ -0,0 +1,69 @@ +{# SPDX-License-Identifier: AGPL-3.0-or-later #} + +{% load i18n %} + + +
+ + +
+ + +
+ + + +
+ +
+ +
+
+
+ + +
+ + + +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ + +
+ + +
diff --git a/plinth/modules/email_server/urls.py b/plinth/modules/email_server/urls.py index 0a5e208d8..be8e8cc2e 100644 --- a/plinth/modules/email_server/urls.py +++ b/plinth/modules/email_server/urls.py @@ -7,6 +7,7 @@ from . import views urlpatterns = [ path('apps/email_server/', views.EmailServerView.as_view(), name='index'), path('apps/email_server/security', views.TLSView.as_view()), + path('apps/email_server/domains', views.DomainView.as_view()), path('apps/email_server/my_mail', non_admin_view(views.MyMailView.as_view()), name='my_mail'), diff --git a/plinth/modules/email_server/views.py b/plinth/modules/email_server/views.py index 7f3ab837d..4a21d02b6 100644 --- a/plinth/modules/email_server/views.py +++ b/plinth/modules/email_server/views.py @@ -4,12 +4,12 @@ import itertools import pwd import plinth.utils -import plinth.views from django.core.exceptions import ValidationError from django.utils.html import escape from django.utils.translation import ugettext_lazy as _ -from django.views.generic.base import TemplateView +from django.views.generic.base import TemplateView, View +from plinth.views import AppView from . import aliases from . import audit @@ -18,7 +18,9 @@ from . import forms admin_tabs = [ ('', _('Home')), ('my_mail', _('My Mail')), - ('my_aliases', _('My Aliases')) + ('my_aliases', _('My Aliases')), + ('security', _('Security')), + ('domains', _('Domains')) ] user_tabs = [ @@ -27,23 +29,74 @@ user_tabs = [ ] -class EmailServerView(plinth.views.AppView): +class TabMixin(View): + def get_context_data(self, *args, **kwargs): + # Retrieve context data from the next method in the MRO + context = super().get_context_data(*args, **kwargs) + # Populate context with customized data + context['tabs'] = self.render_tabs() + return context + + def render_tabs(self): + if plinth.utils.is_user_admin(self.request): + return self.__render_tabs(self.request.path, admin_tabs) + else: + return self.__render_tabs(self.request.path, user_tabs) + + @staticmethod + def __render_tabs(path, tab_data): + sb = io.StringIO() + sb.write('') + return sb.getvalue() + + def render_validation_error(self, validation_error, status=400): + context = self.get_context_data() + context['error'] = validation_error + return self.render_to_response(context, status=status) + + def find_button(self, post): + key_filter = (k for k in post.keys() if k.startswith('btn_')) + lst = list(itertools.islice(key_filter, 2)) + if len(lst) != 1: + raise ValidationError('Bad post data') + if not isinstance(lst[0], str): + raise ValidationError('Bad post data') + return lst[0][len('btn_'):] + + def find_form(self, post): + form_name = post.get('form') + for cls in self.form_classes: + if cls.__name__ == form_name: + return cls(post) + raise ValidationError('Form was unspecified') + + +class EmailServerView(TabMixin, AppView): """Server configuration page""" app_id = 'email_server' template_name = 'email_server.html' - def get_context_data(self, *args, **kwargs): - context = super().get_context_data(*args, **kwargs) - context['tabs'] = render_tabs(self.request) - return context - -class MyMailView(TemplateView): +class MyMailView(TabMixin, TemplateView): template_name = 'my_mail.html' def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) - context['tabs'] = render_tabs(self.request) nam = self.request.user.username context['has_homedir'] = audit.home.exists_nam(nam) @@ -54,22 +107,20 @@ class MyMailView(TemplateView): try: return self._post(request) except ValidationError as validation_error: - context = self.get_context_data() - context['error'] = validation_error - return self.render_to_response(context, status=400) + return self.render_validation_error(validation_error) except RuntimeError as runtime_error: context = self.get_context_data() context['error'] = [str(runtime_error)] return self.render_to_response(context, status=500) def _post(self, request): - if not 'btn_mkhome' in request.POST: + if 'btn_mkhome' not in request.POST: raise ValidationError('Bad post data') audit.home.put_nam(request.user.username) return self.render_to_response(self.get_context_data()) -class AliasView(TemplateView): +class AliasView(TabMixin, TemplateView): class Checkboxes: def __init__(self, post=None, initial=None): self.models = initial @@ -133,7 +184,6 @@ class AliasView(TemplateView): def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context['form'] = forms.AliasCreationForm() - context['tabs'] = render_tabs(self.request) uid = pwd.getpwnam(self.request.user.username).pw_uid models = aliases.get(uid) @@ -144,29 +194,11 @@ class AliasView(TemplateView): context['no_alias'] = True return context - def find_form(self, post): - form_name = post.get('form') - for cls in self.form_classes: - if cls.__name__ == form_name: - return cls(post) - raise ValidationError('Form was unspecified') - - def find_button(self, post): - key_filter = (k for k in post.keys() if k.startswith('btn_')) - lst = list(itertools.islice(key_filter, 2)) - if len(lst) != 1: - raise ValidationError('Bad post data') - if not isinstance(lst[0], str): - raise ValidationError('Bad post data') - return lst[0][len('btn_'):] - def post(self, request): try: return self._post(request) except ValidationError as e: - context = self.get_context_data() - context['error'] = e - return self.render_to_response(context, status=400) + return self.render_validation_error(e) def _post(self, request): form = self.find_form(request.POST) @@ -203,30 +235,23 @@ class AliasView(TemplateView): return self.render_to_response(self.get_context_data()) -def render_tabs(request): - if plinth.utils.is_user_admin(request): - return _render_tabs(request, admin_tabs) - else: - return _render_tabs(request, user_tabs) +class TLSView(TabMixin, TemplateView): + template_name = 'security.html' -def _render_tabs(request, tab_data): - sb = io.StringIO() - sb.write('') - return sb.getvalue() + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context['mailname'] = 'placeholder' + context['mydomain'] = 'placeholder.exmaple.com' + context['mydestination'] = '$mydomain, placeholder.example' + + context['mailname_form'] = forms.MailnameForm() + context['mydomain_form'] = forms.MydomainForm() + context['mydestination_form'] = forms.MydestinationForm() + + return context