coturn: Use privileged decorator for actions

Tests:

- Initial setup of coturn succeeds
  - Configuration file is created and required configuration is set.
  - Coturn is restarted
- Coturn configuration is shown on app page.
- Changing the domain succeeds and coturn configuration reflects the new domain.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2022-08-26 16:21:20 -07:00 committed by James Valleroy
parent 7ff050511c
commit 013caa28bc
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
3 changed files with 18 additions and 54 deletions

View File

@ -1,16 +1,12 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """FreedomBox app to configure Coturn server."""
FreedomBox app to configure Coturn server.
"""
import json
import logging import logging
import pathlib import pathlib
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import menu from plinth import menu
from plinth.daemon import Daemon from plinth.daemon import Daemon
@ -23,7 +19,7 @@ from plinth.modules.users.components import UsersAndGroups
from plinth.package import Packages from plinth.package import Packages
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import manifest from . import manifest, privileged
_description = [ _description = [
_('Coturn is a server to facilitate audio/video calls and conferences by ' _('Coturn is a server to facilitate audio/video calls and conferences by '
@ -109,7 +105,7 @@ class CoturnApp(app_module.App):
def setup(self, old_version): def setup(self, old_version):
"""Install and configure the app.""" """Install and configure the app."""
super().setup(old_version) super().setup(old_version)
actions.superuser_run('coturn', ['setup']) privileged.setup()
if old_version == 0: if old_version == 0:
self.enable() self.enable()
@ -151,14 +147,13 @@ def get_domains():
def set_domain(domain): def set_domain(domain):
"""Set the TLS domain by writing a file to data directory.""" """Set the TLS domain by writing a file to data directory."""
if domain: if domain:
actions.superuser_run('coturn', ['set-domain', domain]) privileged.set_domain(domain)
notify_configuration_change() notify_configuration_change()
def get_config(): def get_config():
"""Return the coturn server configuration.""" """Return the coturn server configuration."""
output = actions.superuser_run('coturn', ['get-config']) config = privileged.get_config()
config = json.loads(output)
return TurnConfiguration(config['realm'], [], config['static_auth_secret']) return TurnConfiguration(config['realm'], [], config['static_auth_secret'])

52
actions/coturn → plinth/modules/coturn/privileged.py Executable file → Normal file
View File

@ -1,11 +1,6 @@
#!/usr/bin/python3
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """Configuration helper for Coturn daemon."""
Configuration helper for Coturn daemon.
"""
import argparse
import json
import pathlib import pathlib
import random import random
import shutil import shutil
@ -14,31 +9,18 @@ import string
import augeas import augeas
from plinth import action_utils from plinth import action_utils
from plinth.actions import privileged
CONFIG_FILE = pathlib.Path('/etc/coturn/freedombox.conf') CONFIG_FILE = pathlib.Path('/etc/coturn/freedombox.conf')
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('setup', help='Setup Coturn server')
subparsers.add_parser('get-config',
help='Return the current configuration')
subparser = subparsers.add_parser('set-domain', help='Set the TLS domain')
subparser.add_argument('domain_name', help='TLS domain name to set')
subparsers.required = True
return parser.parse_args()
def _key_path(key): def _key_path(key):
"""Return the augeas path for a key.""" """Return the augeas path for a key."""
return '/files' + str(CONFIG_FILE) + '/' + key return '/files' + str(CONFIG_FILE) + '/' + key
def subcommand_setup(_): @privileged
def setup():
"""Setup Coturn server.""" """Setup Coturn server."""
CONFIG_FILE.parent.mkdir(exist_ok=True) CONFIG_FILE.parent.mkdir(exist_ok=True)
if not CONFIG_FILE.exists(): if not CONFIG_FILE.exists():
@ -75,25 +57,26 @@ def subcommand_setup(_):
action_utils.service_try_restart('coturn') action_utils.service_try_restart('coturn')
def subcommand_get_config(_): @privileged
"""Return the current configuration in JSON format.""" def get_config() -> dict[str, str]:
"""Return the current configuration."""
aug = augeas_load() aug = augeas_load()
config = { config = {
'static_auth_secret': aug.get(_key_path('static-auth-secret')), 'static_auth_secret': aug.get(_key_path('static-auth-secret')),
'realm': aug.get(_key_path('realm')), 'realm': aug.get(_key_path('realm')),
} }
print(json.dumps(config)) return config
def subcommand_set_domain(arguments): @privileged
def set_domain(domain_name: str):
"""Set the TLS domain. """Set the TLS domain.
This value is usually not stored. So, set realm value even though it is not This value is usually not stored. So, set realm value even though it is not
needed to set realm for REST API based authentication. needed to set realm for REST API based authentication.
""" """
aug = augeas_load() aug = augeas_load()
aug.set(_key_path('realm'), arguments.domain_name) aug.set(_key_path('realm'), domain_name)
aug.save() aug.save()
@ -106,16 +89,3 @@ def augeas_load():
aug.load() aug.load()
return aug return aug
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()

View File

@ -1,7 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """Views for Coturn app."""
Views for Coturn app.
"""
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -15,6 +13,7 @@ from . import forms
class CoturnAppView(views.AppView): class CoturnAppView(views.AppView):
"""Serve configuration page.""" """Serve configuration page."""
app_id = 'coturn' app_id = 'coturn'
template_name = 'coturn.html' template_name = 'coturn.html'
form_class = forms.CoturnForm form_class = forms.CoturnForm