diff --git a/actions/mumble b/actions/mumble index 2e1d01283..e2ec6fba2 100755 --- a/actions/mumble +++ b/actions/mumble @@ -1,15 +1,16 @@ #!/usr/bin/python3 # SPDX-License-Identifier: AGPL-3.0-or-later - """ Configure Mumble server. """ import argparse -import augeas +import pathlib import sys -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen + +import augeas CONFIG_FILE = '/etc/mumble-server.ini' DATA_DIR = '/var/lib/mumble-server' @@ -25,6 +26,11 @@ def parse_arguments(): subparsers.add_parser('create-password', help='Setup mumble superuser password') + subparsers.add_parser('get-domain', help='Print Mumble domain') + subparser = subparsers.add_parser('set-domain', help='Setup Mumble domain') + subparser.add_argument('domain_name', help='Domain name to be allowed') + + subparsers.required = True return parser.parse_args() @@ -42,7 +48,7 @@ def read_from_stdin(): return (''.join(sys.stdin)).strip() -def subcommand_create_password(arguments): +def subcommand_create_password(_): """Save superuser password with murmurd command""" password = read_from_stdin() @@ -58,12 +64,25 @@ def subcommand_create_password(arguments): phrase = "Superuser password set on server" if phrase not in err: - print( - "Error occured while saving password: %s" % err - ) + print("Error occured while saving password: %s" % err) sys.exit(1) +def subcommand_get_domain(_): + """Print the file containing domain name or empty string.""" + domain_file = pathlib.Path('/var/lib/mumble-server/domain-freedombox') + try: + print(domain_file.read_text()) + except FileNotFoundError: + pass + + +def subcommand_set_domain(arguments): + """Write a file containing domain name.""" + domain_file = pathlib.Path('/var/lib/mumble-server/domain-freedombox') + domain_file.write_text(arguments.domain_name) + + def load_augeas(): """Initialize Augeas.""" aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + diff --git a/plinth/modules/mumble/__init__.py b/plinth/modules/mumble/__init__.py index 1e414dfe1..61fabdaba 100644 --- a/plinth/modules/mumble/__init__.py +++ b/plinth/modules/mumble/__init__.py @@ -8,16 +8,18 @@ import pathlib from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ +from plinth import actions from plinth import app as app_module from plinth import frontpage, menu from plinth.daemon import Daemon +from plinth.modules import names from plinth.modules.firewall.components import Firewall from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.modules.users.components import UsersAndGroups from .manifest import backup, clients # noqa, pylint: disable=unused-import -version = 1 +version = 2 managed_services = ['mumble-server'] @@ -67,7 +69,7 @@ class MumbleApp(app_module.App): self.add(firewall) letsencrypt = LetsEncrypt( - 'letsencrypt-mumble', domains='*', + 'letsencrypt-mumble', domains=get_domains, daemons=managed_services, should_copy_certificates=True, private_key_path='/var/lib/mumble-server/privkey.pem', certificate_path='/var/lib/mumble-server/fullchain.pem', @@ -89,4 +91,42 @@ class MumbleApp(app_module.App): def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - helper.call('post', app.enable) + helper.call('post', actions.superuser_run, 'mumble', ['setup']) + if not old_version: + helper.call('post', app.enable) + + app.get_component('letsencrypt-mumble').setup_certificates() + + +def get_available_domains(): + """Return an iterator with all domains able to have a certificate.""" + return (domain.name for domain in names.components.DomainName.list() + if domain.domain_type.can_have_certificate) + + +def set_domain(domain): + """Set the TLS domain by writing a file to data directory.""" + if domain: + actions.superuser_run('mumble', ['set-domain', domain]) + + +def get_domain(): + """Read TLS domain from config file select first available if none.""" + domain = actions.superuser_run('mumble', ['get-domain']).strip() + if not domain: + domain = next(get_available_domains(), None) + set_domain(domain) + + return domain + + +def get_domains(): + """Return a list with the configured domains.""" + if not pathlib.Path('/var/lib/mumble-server/').exists(): + return [] + + domain = get_domain() + if domain: + return [domain] + + return [] diff --git a/plinth/modules/mumble/forms.py b/plinth/modules/mumble/forms.py index fa05b25b5..a3df53a23 100644 --- a/plinth/modules/mumble/forms.py +++ b/plinth/modules/mumble/forms.py @@ -6,9 +6,25 @@ Mumble server configuration form from django import forms from django.utils.translation import ugettext_lazy as _ +from plinth.modules import mumble + + +def get_domain_choices(): + """Double domain entries for inclusion in the choice field.""" + return ((domain, domain) for domain in mumble.get_available_domains()) + class MumbleForm(forms.Form): """Mumble server configuration""" + domain = forms.ChoiceField( + choices=get_domain_choices, + label=_('TLS domain'), + help_text=_( + 'Select a domain to use TLS with. If the list is empty, please ' + 'configure at least one domain with certificates.'), + required=False, + ) + super_user_password = forms.CharField( max_length=20, label=_('Set SuperUser Password'), diff --git a/plinth/modules/mumble/views.py b/plinth/modules/mumble/views.py index a12ba76ce..4180f7b59 100644 --- a/plinth/modules/mumble/views.py +++ b/plinth/modules/mumble/views.py @@ -3,6 +3,7 @@ from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from plinth import actions +from plinth.modules import mumble from plinth.modules.mumble.forms import MumbleForm from plinth.views import AppView @@ -11,10 +12,21 @@ class MumbleAppView(AppView): app_id = 'mumble' form_class = MumbleForm + def get_initial(self): + """Return the values to fill in the form.""" + initial = super().get_initial() + initial['domain'] = mumble.get_domain() + return initial + def form_valid(self, form): """Apply new superuser password if it exists""" new_config = form.cleaned_data + if mumble.get_domain() != new_config['domain']: + mumble.set_domain(new_config['domain']) + mumble.app.get_component('letsencrypt-mumble').setup_certificates() + messages.success(self.request, _('Configuration updated')) + password = new_config.get('super_user_password') if password: actions.run_as_user(