diff --git a/actions/searx b/actions/searx index a41ad11cb..3398b40d6 100755 --- a/actions/searx +++ b/actions/searx @@ -20,11 +20,11 @@ Configuration helper for searx. """ import argparse -import augeas import os import secrets import shutil +import augeas import yaml from plinth import action_utils @@ -45,6 +45,12 @@ def parse_arguments(): 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') @@ -81,7 +87,8 @@ def _update_uwsgi_configuration(): aug.set('/augeas/load/inifile/lens', 'Puppet.lns') aug.set('/augeas/load/inifile/incl[last() + 1]', UWSGI_FILE) aug.load() - aug.set('/files/etc/uwsgi/apps-available/searx.ini/uwsgi/autoload', 'false') + aug.set('/files/etc/uwsgi/apps-available/searx.ini/uwsgi/autoload', + 'false') aug.save() @@ -153,14 +160,26 @@ def subcommand_enable(_): """Enable web configuration and reload.""" action_utils.uwsgi_enable('searx') action_utils.webserver_enable('searx-freedombox') + action_utils.webserver_enable('searx-freedombox-auth') def subcommand_disable(_): """Disable web configuration and reload.""" action_utils.webserver_disable('searx-freedombox') + action_utils.webserver_disable('searx-freedombox-auth') action_utils.uwsgi_disable('searx') +def subcommand_enable_public_access(_): + """Enable public access to the SearX application.""" + action_utils.webserver_disable('searx-freedombox-auth') + + +def subcommand_disable_public_access(_): + """Disable public access to the SearX application.""" + action_utils.webserver_enable('searx-freedombox-auth') + + def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() diff --git a/functional_tests/features/searx.feature b/functional_tests/features/searx.feature index 72e507d46..266607720 100644 --- a/functional_tests/features/searx.feature +++ b/functional_tests/features/searx.feature @@ -38,3 +38,18 @@ Scenario: Disable searx application Given the searx application is enabled When I disable the searx application Then the searx site should not be available + +Scenario: Enable public access + Given the searx application is enabled + And I enable public access in searx + And I'm a logged out user + Then searx app should be visible on the front page + And the searx site should be available + +Scenario: Disable public access + Given the searx application is enabled + And I disable public access in searx + And I'm a logged out user + Then searx app should not be visible on the front page + And the searx site should not be available + diff --git a/functional_tests/step_definitions/application.py b/functional_tests/step_definitions/application.py index f64dd87a7..cb0cd4fab 100644 --- a/functional_tests/step_definitions/application.py +++ b/functional_tests/step_definitions/application.py @@ -423,3 +423,26 @@ def openvpn_profile_downloadable(browser): def openvpn_profile_download_compare(browser, openvpn_download_profile): new_profile = application.openvpn_download_profile(browser) assert openvpn_download_profile == new_profile + + +@given('I enable public access in searx') +def searx_enable_public_access(browser): + application.searx_enable_public_access(browser) + + +@given('I disable public access in searx') +def searx_disable_public_access(browser): + application.searx_disable_public_access(browser) + + +@then(parsers.parse('{app_name:w} app should be visible on the front page')) +def app_visible_on_front_page(browser, app_name): + shortcuts = application.find_on_front_page(browser, app_name) + assert len(shortcuts) == 1 + + +@then( + parsers.parse('{app_name:w} app should not be visible on the front page')) +def app_visible_on_front_page(browser, app_name): + shortcuts = application.find_on_front_page(browser, app_name) + assert len(shortcuts) == 0 diff --git a/functional_tests/support/application.py b/functional_tests/support/application.py index 72bf7a34a..f14dc205a 100644 --- a/functional_tests/support/application.py +++ b/functional_tests/support/application.py @@ -469,3 +469,23 @@ def openvpn_download_profile(browser): interface.nav_to_module(browser, 'openvpn') url = browser.find_by_css('.form-profile')['action'] return _download_file(browser, url) + + +def searx_enable_public_access(browser): + """Enable Public Access in SearX""" + interface.nav_to_module(browser, 'searx') + browser.find_by_id('id_public_access').check() + interface.submit(browser, form_class='form-configuration') + + +def searx_disable_public_access(browser): + """Enable Public Access in SearX""" + interface.nav_to_module(browser, 'searx') + browser.find_by_id('id_public_access').uncheck() + interface.submit(browser, form_class='form-configuration') + + +def find_on_front_page(browser, app_name): + browser.visit(default_url) + shortcuts = browser.find_link_by_href(f'/{app_name}/') + return shortcuts diff --git a/functional_tests/support/site.py b/functional_tests/support/site.py index a0f1ec027..123866575 100644 --- a/functional_tests/support/site.py +++ b/functional_tests/support/site.py @@ -21,7 +21,6 @@ import pathlib import time import requests - from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys @@ -45,10 +44,14 @@ def get_site_url(site_name): def is_available(browser, site_name): - browser.visit(config['DEFAULT']['url'] + get_site_url(site_name)) + url_to_visit = config['DEFAULT']['url'] + get_site_url(site_name) + browser.visit(url_to_visit) time.sleep(3) browser.reload() - return '404' not in browser.title + not_404 = '404' not in browser.title + # A trailing slash might be appended by Apache redirect rules + no_redirect = url_to_visit.strip('/') == browser.url.strip('/') + return not_404 and no_redirect def access_url(browser, site_name): diff --git a/plinth/modules/searx/__init__.py b/plinth/modules/searx/__init__.py index d997b2c94..3d5200cc1 100644 --- a/plinth/modules/searx/__init__.py +++ b/plinth/modules/searx/__init__.py @@ -30,7 +30,7 @@ from .manifest import backup, clients clients = clients -version = 2 +version = 3 managed_services = ['searx'] @@ -68,10 +68,17 @@ class SearxApp(app_module.App): shortcut = frontpage.Shortcut( 'shortcut-searx', name, short_description=short_description, - icon='searx', url='/searx/', clients=clients, login_required=True, + icon='searx', url='/searx/', clients=clients, + login_required=(not is_public_access_enabled()), allowed_groups=[group[0]]) self.add(shortcut) + def set_shortcut_login_required(self, login_required): + """Change the login_required property of shortcut.""" + shortcut = self.remove('shortcut-searx') + shortcut.login_required = login_required + self.add(shortcut) + def init(): """Intialize the module.""" @@ -95,8 +102,12 @@ def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'searx', ['setup']) - if not old_version: + if not old_version or old_version < 3: helper.call('post', actions.superuser_run, 'searx', ['enable']) + helper.call('post', actions.superuser_run, 'searx', + ['disable-public-access']) + app.set_shortcut_login_required(True) + app.enable() global service if service is None: @@ -108,11 +119,16 @@ def setup(helper, old_version=None): def get_safe_search_setting(): - """Get the current value of the safe search setting for Seax.""" + """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(): + """Check whether public access is enabled for Searx.""" + return not action_utils.webserver_is_enabled('searx-freedombox-auth') + + def is_enabled(): """Return whether the module is enabled.""" return (action_utils.webserver_is_enabled('searx-freedombox') diff --git a/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox-auth.conf b/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox-auth.conf new file mode 100644 index 000000000..86c4e727b --- /dev/null +++ b/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox-auth.conf @@ -0,0 +1,9 @@ + + Include includes/freedombox-single-sign-on.conf + + + TKTAuthToken "web-search" "admin" + + + ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/ + diff --git a/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox.conf b/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox.conf index 2efe558f6..ceaa0b176 100644 --- a/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox.conf +++ b/plinth/modules/searx/data/etc/apache2/conf-available/searx-freedombox.conf @@ -9,12 +9,6 @@ - Include includes/freedombox-single-sign-on.conf - - - TKTAuthToken "web-search" "admin" - - ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/ diff --git a/plinth/modules/searx/forms.py b/plinth/modules/searx/forms.py index 89f95ddf5..22203cd4f 100644 --- a/plinth/modules/searx/forms.py +++ b/plinth/modules/searx/forms.py @@ -26,8 +26,11 @@ from plinth.forms import ServiceForm class SearxForm(ServiceForm): """Searx configuration form.""" - safe_search = forms.ChoiceField(label=_('Safe Search'), help_text=_( - 'Select the default family filter to apply to your search results.'), - choices=((0, _('None')), - (1, _('Moderate')), - (2, _('Strict')))) + safe_search = forms.ChoiceField( + label=_('Safe Search'), help_text=_( + 'Select the default family filter to apply to your search results.' + ), choices=((0, _('None')), (1, _('Moderate')), (2, _('Strict')))) + public_access = forms.BooleanField( + label=_('Public Access'), help_text=_( + 'Allow this application to be used by anyone who can reach it.'), + required=False) diff --git a/plinth/modules/searx/views.py b/plinth/modules/searx/views.py index 9e285477d..1c9e3421a 100644 --- a/plinth/modules/searx/views.py +++ b/plinth/modules/searx/views.py @@ -23,8 +23,9 @@ from django.utils.translation import ugettext as _ from plinth import actions, views from plinth.errors import ActionError -from plinth.modules.searx import (clients, description, - get_safe_search_setting, manual_page) +from plinth.modules.searx import (add_shortcut, clients, description, + get_safe_search_setting, + is_public_access_enabled, manual_page) from .forms import SearxForm @@ -43,6 +44,7 @@ class SearxServiceView(views.ServiceView): """Return the status of the service to fill in the form.""" initial = super().get_initial() initial['safe_search'] = get_safe_search_setting() + initial['public_access'] = is_public_access_enabled() return initial def form_valid(self, form): @@ -59,4 +61,16 @@ class SearxServiceView(views.ServiceView): messages.error(self.request, _('An error occurred during configuration.')) + if old_data['public_access'] != form_data['public_access']: + try: + if form_data['public_access']: + actions.superuser_run('searx', ['enable-public-access']) + else: + actions.superuser_run('searx', ['disable-public-access']) + add_shortcut() + messages.success(self.request, _('Configuration updated.')) + except ActionError as e: + messages.error(self.request, + _('An error occurred during configuration.')) + return super().form_valid(form)