searx: Use privileged decorator for actions

Tests:

- Functional tests work
- Initial setup works
  - UWSGI configuration is created and daemon is running.
- Enabling and disabling public access works. Public access file is
  created/removed. App page shows current value. If exception is raised, error
  is shown properly.
- Setting safe search to all three values works. Configuration file is updated
  properly. App page shows current value properly. If exception is raised, error
  is shown properly.

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 10:19:08 -07:00 committed by James Valleroy
parent 8db063b59b
commit 52f42a4f74
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
4 changed files with 33 additions and 86 deletions

View File

@ -1,13 +1,10 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """FreedomBox app to configure Searx."""
FreedomBox app to configure Searx.
"""
import os import os
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 frontpage, menu from plinth import frontpage, menu
from plinth.modules.apache.components import Uwsgi, Webserver from plinth.modules.apache.components import Uwsgi, Webserver
@ -16,7 +13,7 @@ from plinth.modules.firewall.components import Firewall
from plinth.modules.users.components import UsersAndGroups from plinth.modules.users.components import UsersAndGroups
from plinth.package import Packages from plinth.package import Packages
from . import manifest from . import manifest, privileged
_description = [ _description = [
_('Searx is a privacy-respecting Internet metasearch engine. ' _('Searx is a privacy-respecting Internet metasearch engine. '
@ -95,9 +92,9 @@ class SearxApp(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('searx', ['setup']) privileged.setup()
if not old_version or old_version < 3: if not old_version or old_version < 3:
actions.superuser_run('searx', ['disable-public-access']) privileged.disable_public_access()
self.enable() self.enable()
self.set_shortcut_login_required(True) self.set_shortcut_login_required(True)
@ -115,12 +112,6 @@ class SearxWebserverAuth(Webserver):
super().enable() super().enable()
def get_safe_search_setting():
"""Get the current value of the safe search setting for Searx."""
value = actions.superuser_run('searx', ['get-safe-search'])
return int(value.strip())
def is_public_access_enabled(): def is_public_access_enabled():
"""Check whether public access is enabled for Searx.""" """Check whether public access is enabled for Searx."""
return os.path.exists(manifest.PUBLIC_ACCESS_SETTING_FILE) return os.path.exists(manifest.PUBLIC_ACCESS_SETTING_FILE)
@ -128,7 +119,7 @@ def is_public_access_enabled():
def enable_public_access(): def enable_public_access():
"""Allow Searx app to be accessed by anyone with access.""" """Allow Searx app to be accessed by anyone with access."""
actions.superuser_run('searx', ['enable-public-access']) privileged.enable_public_access()
app = app_module.App.get('searx') app = app_module.App.get('searx')
app.get_component('webserver-searx-auth').disable() app.get_component('webserver-searx-auth').disable()
app.set_shortcut_login_required(False) app.set_shortcut_login_required(False)
@ -136,7 +127,7 @@ def enable_public_access():
def disable_public_access(): def disable_public_access():
"""Allow Searx app to be accessed by logged-in users only.""" """Allow Searx app to be accessed by logged-in users only."""
actions.superuser_run('searx', ['disable-public-access']) privileged.disable_public_access()
app = app_module.App.get('searx') app = app_module.App.get('searx')
app.get_component('webserver-searx-auth').enable() app.get_component('webserver-searx-auth').enable()
app.set_shortcut_login_required(True) app.set_shortcut_login_required(True)

View File

@ -1,7 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """Django form for configuring Searx."""
Django form for configuring Searx.
"""
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -9,6 +7,7 @@ from django.utils.translation import gettext_lazy as _
class SearxForm(forms.Form): class SearxForm(forms.Form):
"""Searx configuration form.""" """Searx configuration form."""
safe_search = forms.ChoiceField( safe_search = forms.ChoiceField(
label=_('Safe Search'), help_text=_( label=_('Safe Search'), help_text=_(
'Select the default family filter to apply to your search results.' 'Select the default family filter to apply to your search results.'

76
actions/searx → plinth/modules/searx/privileged.py Executable file → Normal file
View File

@ -1,10 +1,6 @@
#!/usr/bin/python3
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """Configure searx."""
Configuration helper for searx.
"""
import argparse
import gzip import gzip
import os import os
import pathlib import pathlib
@ -15,6 +11,7 @@ import augeas
import yaml import yaml
from plinth import action_utils from plinth import action_utils
from plinth.actions import privileged
from plinth.modules.searx.manifest import PUBLIC_ACCESS_SETTING_FILE from plinth.modules.searx.manifest import PUBLIC_ACCESS_SETTING_FILE
from plinth.utils import gunzip from plinth.utils import gunzip
@ -23,34 +20,6 @@ SETTINGS_FILE = '/etc/searx/settings.yml'
UWSGI_FILE = '/etc/uwsgi/apps-available/searx.ini' UWSGI_FILE = '/etc/uwsgi/apps-available/searx.ini'
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 post-installation operations for Searx')
subparsers.add_parser('enable-public-access',
help='Enable public access to the Searx application')
subparsers.add_parser(
'disable-public-access',
help='Disable public access to the Searx application')
safe_search = subparsers.add_parser(
'set-safe-search',
help='Set the default filter for safe search on Searx')
safe_search.add_argument(
'filter', type=int,
help='Filter results. 0: None, 1: Moderate, 2: Strict')
subparsers.add_parser('get-safe-search',
help='Print the value of the safe search setting.')
subparsers.required = True
return parser.parse_args()
def _copy_uwsgi_configuration(): def _copy_uwsgi_configuration():
"""Copy example uwsgi configuration """Copy example uwsgi configuration
@ -100,21 +69,22 @@ def _set_safe_search(settings):
settings['search']['safe_search'] = 1 settings['search']['safe_search'] = 1
def subcommand_set_safe_search(arguments): @privileged
def set_safe_search(filter_: int):
"""Set safe search filter for search results.""" """Set safe search filter for search results."""
value = arguments.filter
settings = read_settings() settings = read_settings()
settings['search']['safe_search'] = value settings['search']['safe_search'] = filter_
write_settings(settings) write_settings(settings)
def subcommand_get_safe_search(_): @privileged
"""Print the value of the safe search setting.""" def get_safe_search() -> int:
"""Return the value of the safe search setting."""
if os.path.exists(SETTINGS_FILE): if os.path.exists(SETTINGS_FILE):
settings = read_settings() settings = read_settings()
print(settings['search']['safe_search']) return int(settings['search']['safe_search'])
else: else:
print(0) return 0
def read_settings(): def read_settings():
@ -138,15 +108,16 @@ def _get_example_settings_file():
def _update_search_engines(settings): def _update_search_engines(settings):
"""Updates settings with the latest supported search engines.""" """Update settings with the latest supported search engines."""
example_settings_file = _get_example_settings_file() example_settings_file = _get_example_settings_file()
open_func = gzip.open if example_settings_file.suffix == '.gz' else open open_func = gzip.open if example_settings_file.suffix == '.gz' else open
with open_func(example_settings_file, 'rb') as example_settings: with open_func(example_settings_file, 'rb') as example_settings:
settings['engines'] = yaml.safe_load(example_settings)['engines'] settings['engines'] = yaml.safe_load(example_settings)['engines']
def subcommand_setup(_): @privileged
"""Post installation actions for Searx""" def setup():
"""Post installation actions for Searx."""
_copy_uwsgi_configuration() _copy_uwsgi_configuration()
_update_uwsgi_configuration() _update_uwsgi_configuration()
@ -169,25 +140,14 @@ def subcommand_setup(_):
action_utils.service_restart('uwsgi') action_utils.service_restart('uwsgi')
def subcommand_enable_public_access(_): @privileged
def enable_public_access():
"""Enable public access to the SearX application.""" """Enable public access to the SearX application."""
open(PUBLIC_ACCESS_SETTING_FILE, 'w', encoding='utf-8').close() open(PUBLIC_ACCESS_SETTING_FILE, 'w', encoding='utf-8').close()
def subcommand_disable_public_access(_): @privileged
def disable_public_access():
"""Disable public access to the SearX application.""" """Disable public access to the SearX application."""
if os.path.exists(PUBLIC_ACCESS_SETTING_FILE): if os.path.exists(PUBLIC_ACCESS_SETTING_FILE):
os.remove(PUBLIC_ACCESS_SETTING_FILE) os.remove(PUBLIC_ACCESS_SETTING_FILE)
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,29 +1,27 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
""" """Django views for Searx."""
Django views for Searx.
"""
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import views from plinth import views
from plinth.errors import ActionError
from plinth.modules import searx from plinth.modules import searx
from . import privileged
from .forms import SearxForm from .forms import SearxForm
class SearxAppView(views.AppView): class SearxAppView(views.AppView):
"""Serve configuration page.""" """Serve configuration page."""
app_id = 'searx' app_id = 'searx'
form_class = SearxForm form_class = SearxForm
def get_initial(self): def get_initial(self):
"""Return the status of the service to fill in the form.""" """Return the status of the service to fill in the form."""
initial = super().get_initial() initial = super().get_initial()
initial['safe_search'] = searx.get_safe_search_setting() initial['safe_search'] = privileged.get_safe_search()
initial['public_access'] = searx.is_public_access_enabled() and \ initial['public_access'] = searx.is_public_access_enabled() and \
app_module.App.get('searx').is_enabled() app_module.App.get('searx').is_enabled()
return initial return initial
@ -35,10 +33,9 @@ class SearxAppView(views.AppView):
if str(old_data['safe_search']) != form_data['safe_search']: if str(old_data['safe_search']) != form_data['safe_search']:
try: try:
actions.superuser_run( privileged.set_safe_search(int(form_data['safe_search']))
'searx', ['set-safe-search', form_data['safe_search']])
messages.success(self.request, _('Configuration updated.')) messages.success(self.request, _('Configuration updated.'))
except ActionError: except Exception:
messages.error(self.request, messages.error(self.request,
_('An error occurred during configuration.')) _('An error occurred during configuration.'))
@ -49,7 +46,7 @@ class SearxAppView(views.AppView):
else: else:
searx.disable_public_access() searx.disable_public_access()
messages.success(self.request, _('Configuration updated.')) messages.success(self.request, _('Configuration updated.'))
except ActionError: except Exception:
messages.error(self.request, messages.error(self.request,
_('An error occurred during configuration.')) _('An error occurred during configuration.'))