From 6e7b31a3cf9962412816adcdb0ac744aae5f7688 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Fri, 26 Aug 2022 10:45:43 -0700 Subject: [PATCH] shadowsocks: Use privileged decorator for actions Tests: - Functional tests work - Initial setup works - Setting configuration works, correct configuration is updated in the configuration files and app shows the values correctly. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/shadowsocks/__init__.py | 9 +-- plinth/modules/shadowsocks/forms.py | 11 ++-- .../modules/shadowsocks/privileged.py | 57 +++++-------------- plinth/modules/shadowsocks/views.py | 20 +++---- 4 files changed, 28 insertions(+), 69 deletions(-) rename actions/shadowsocks => plinth/modules/shadowsocks/privileged.py (70%) mode change 100755 => 100644 diff --git a/plinth/modules/shadowsocks/__init__.py b/plinth/modules/shadowsocks/__init__.py index b91cb6e47..c0d45df36 100644 --- a/plinth/modules/shadowsocks/__init__.py +++ b/plinth/modules/shadowsocks/__init__.py @@ -1,12 +1,9 @@ # SPDX-License-Identifier: AGPL-3.0-or-later -""" -FreedomBox app to configure Shadowsocks. -""" +"""FreedomBox app to configure Shadowsocks.""" from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ -from plinth import actions from plinth import app as app_module from plinth import cfg, frontpage, menu from plinth.daemon import Daemon @@ -15,7 +12,7 @@ from plinth.modules.firewall.components import Firewall from plinth.package import Packages from plinth.utils import format_lazy -from . import manifest +from . import manifest, privileged _description = [ _('Shadowsocks is a lightweight and secure SOCKS5 proxy, designed to ' @@ -85,5 +82,5 @@ class ShadowsocksApp(app_module.App): def setup(self, old_version): """Install and configure the app.""" super().setup(old_version) - actions.superuser_run('shadowsocks', ['setup']) + privileged.setup() self.enable() diff --git a/plinth/modules/shadowsocks/forms.py b/plinth/modules/shadowsocks/forms.py index 188e4caf5..102b77baf 100644 --- a/plinth/modules/shadowsocks/forms.py +++ b/plinth/modules/shadowsocks/forms.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-or-later -""" -FreedomBox app for configuring Shadowsocks. -""" +"""FreedomBox app for configuring Shadowsocks.""" from django import forms from django.utils.translation import gettext_lazy as _ @@ -22,10 +20,10 @@ METHODS = [('chacha20-ietf-poly1305', class TrimmedCharField(forms.CharField): - """Trim the contents of a CharField""" + """Trim the contents of a CharField.""" def clean(self, value): - """Clean and validate the field value""" + """Clean and validate the field value.""" if value: value = value.strip() @@ -33,7 +31,8 @@ class TrimmedCharField(forms.CharField): class ShadowsocksForm(forms.Form): - """Shadowsocks configuration form""" + """Shadowsocks configuration form.""" + server = TrimmedCharField(label=_('Server'), help_text=_('Server hostname or IP address')) diff --git a/actions/shadowsocks b/plinth/modules/shadowsocks/privileged.py old mode 100755 new mode 100644 similarity index 70% rename from actions/shadowsocks rename to plinth/modules/shadowsocks/privileged.py index 1407dadf6..39cdbe6b4 --- a/actions/shadowsocks +++ b/plinth/modules/shadowsocks/privileged.py @@ -1,42 +1,24 @@ -#!/usr/bin/python3 # SPDX-License-Identifier: AGPL-3.0-or-later -""" -Helper script for configuring Shadowsocks. -""" +"""Configure Shadowsocks.""" -import argparse import json import os import pathlib import random import string -import sys from shutil import move +from typing import Union from plinth import action_utils -from plinth.modules.shadowsocks import ShadowsocksApp +from plinth.actions import privileged SHADOWSOCKS_CONFIG_SYMLINK = '/etc/shadowsocks-libev/freedombox.json' SHADOWSOCKS_CONFIG_ACTUAL = \ '/var/lib/private/shadowsocks-libev/freedombox/freedombox.json' -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='Perform initial setup steps') - subparsers.add_parser('get-config', - help='Read and print JSON config to stdout') - subparsers.add_parser('merge-config', - help='Merge JSON config from stdin with existing') - - subparsers.required = True - return parser.parse_args() - - -def subcommand_setup(_): +@privileged +def setup(): """Perform initial setup steps.""" # Only client socks5 proxy is supported for now. Disable the # server component. @@ -76,16 +58,16 @@ def subcommand_setup(_): if not wrong_state_dir.is_symlink() and wrong_state_dir.is_dir(): wrong_state_dir.rmdir() + from plinth.modules.shadowsocks import ShadowsocksApp if action_utils.service_is_enabled(ShadowsocksApp.DAEMON): action_utils.service_restart(ShadowsocksApp.DAEMON) -def subcommand_get_config(_): +@privileged +def get_config() -> dict[str, Union[int, str]]: """Read and print Shadowsocks configuration.""" - try: - print(open(SHADOWSOCKS_CONFIG_SYMLINK, 'r', encoding='utf-8').read()) - except Exception: - sys.exit(1) + config = open(SHADOWSOCKS_CONFIG_SYMLINK, 'r', encoding='utf-8').read() + return json.loads(config) def _merge_config(config): @@ -103,26 +85,13 @@ def _merge_config(config): open(SHADOWSOCKS_CONFIG_SYMLINK, 'w', encoding='utf-8').write(new_config) -def subcommand_merge_config(_): +@privileged +def merge_config(config: dict[str, Union[int, str]]): """Configure Shadowsocks.""" - config = sys.stdin.read() - config = json.loads(config) _merge_config(config) # Don't try_restart because initial configuration may not be valid so # shadowsocks will not be running even when enabled. + from . import ShadowsocksApp if action_utils.service_is_enabled(ShadowsocksApp.DAEMON): action_utils.service_restart(ShadowsocksApp.DAEMON) - - -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() diff --git a/plinth/modules/shadowsocks/views.py b/plinth/modules/shadowsocks/views.py index b1d8b74fd..ad2deda1b 100644 --- a/plinth/modules/shadowsocks/views.py +++ b/plinth/modules/shadowsocks/views.py @@ -1,21 +1,18 @@ # SPDX-License-Identifier: AGPL-3.0-or-later -""" -FreedomBox app for configuring Shadowsocks. -""" - -import json +"""FreedomBox app for configuring Shadowsocks.""" from django.contrib import messages from django.utils.translation import gettext_lazy as _ -from plinth import actions, views -from plinth.errors import ActionError +from plinth import views +from . import privileged from .forms import ShadowsocksForm class ShadowsocksAppView(views.AppView): """Configuration view for Shadowsocks local socks5 proxy.""" + app_id = 'shadowsocks' form_class = ShadowsocksForm @@ -23,10 +20,8 @@ class ShadowsocksAppView(views.AppView): """Get initial values for form.""" status = super().get_initial() try: - configuration = actions.superuser_run('shadowsocks', - ['get-config']) - status.update(json.loads(configuration)) - except ActionError: + status.update(privileged.get_config()) + except Exception: status.update({ 'server': '', 'server_port': 8388, @@ -54,8 +49,7 @@ class ShadowsocksAppView(views.AppView): 'method': new_status['method'], } - actions.superuser_run('shadowsocks', ['merge-config'], - input=json.dumps(new_config).encode()) + privileged.merge_config(new_config) messages.success(self.request, _('Configuration updated')) return super().form_valid(form)