searx: Add option to allow public access to the application

[jvalleroy] Resolved merge conflict to use shortcut component.

Fixes #1590

Signed-off-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Joseph Nuthalapati 2019-05-27 19:14:01 +05:30 committed by James Valleroy
parent 6a9133c305
commit 2d85b61199
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
10 changed files with 138 additions and 22 deletions

View File

@ -20,11 +20,11 @@ Configuration helper for searx.
""" """
import argparse import argparse
import augeas
import os import os
import secrets import secrets
import shutil import shutil
import augeas
import yaml import yaml
from plinth import action_utils from plinth import action_utils
@ -45,6 +45,12 @@ def parse_arguments():
subparsers.add_parser( subparsers.add_parser(
'setup', help='Perform post-installation operations for Searx') '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( safe_search = subparsers.add_parser(
'set-safe-search', 'set-safe-search',
help='Set the default filter for safe search on Searx') 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/lens', 'Puppet.lns')
aug.set('/augeas/load/inifile/incl[last() + 1]', UWSGI_FILE) aug.set('/augeas/load/inifile/incl[last() + 1]', UWSGI_FILE)
aug.load() 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() aug.save()
@ -153,14 +160,26 @@ def subcommand_enable(_):
"""Enable web configuration and reload.""" """Enable web configuration and reload."""
action_utils.uwsgi_enable('searx') action_utils.uwsgi_enable('searx')
action_utils.webserver_enable('searx-freedombox') action_utils.webserver_enable('searx-freedombox')
action_utils.webserver_enable('searx-freedombox-auth')
def subcommand_disable(_): def subcommand_disable(_):
"""Disable web configuration and reload.""" """Disable web configuration and reload."""
action_utils.webserver_disable('searx-freedombox') action_utils.webserver_disable('searx-freedombox')
action_utils.webserver_disable('searx-freedombox-auth')
action_utils.uwsgi_disable('searx') 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(): def main():
"""Parse arguments and perform all duties.""" """Parse arguments and perform all duties."""
arguments = parse_arguments() arguments = parse_arguments()

View File

@ -38,3 +38,18 @@ Scenario: Disable searx application
Given the searx application is enabled Given the searx application is enabled
When I disable the searx application When I disable the searx application
Then the searx site should not be available 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

View File

@ -423,3 +423,26 @@ def openvpn_profile_downloadable(browser):
def openvpn_profile_download_compare(browser, openvpn_download_profile): def openvpn_profile_download_compare(browser, openvpn_download_profile):
new_profile = application.openvpn_download_profile(browser) new_profile = application.openvpn_download_profile(browser)
assert openvpn_download_profile == new_profile 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

View File

@ -469,3 +469,23 @@ def openvpn_download_profile(browser):
interface.nav_to_module(browser, 'openvpn') interface.nav_to_module(browser, 'openvpn')
url = browser.find_by_css('.form-profile')['action'] url = browser.find_by_css('.form-profile')['action']
return _download_file(browser, url) 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

View File

@ -21,7 +21,6 @@ import pathlib
import time import time
import requests import requests
from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
@ -45,10 +44,14 @@ def get_site_url(site_name):
def is_available(browser, 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) time.sleep(3)
browser.reload() 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): def access_url(browser, site_name):

View File

@ -30,7 +30,7 @@ from .manifest import backup, clients
clients = clients clients = clients
version = 2 version = 3
managed_services = ['searx'] managed_services = ['searx']
@ -68,10 +68,17 @@ class SearxApp(app_module.App):
shortcut = frontpage.Shortcut( shortcut = frontpage.Shortcut(
'shortcut-searx', name, short_description=short_description, '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]]) allowed_groups=[group[0]])
self.add(shortcut) 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(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -95,8 +102,12 @@ def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""
helper.install(managed_packages) helper.install(managed_packages)
helper.call('post', actions.superuser_run, 'searx', ['setup']) 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', ['enable'])
helper.call('post', actions.superuser_run, 'searx',
['disable-public-access'])
app.set_shortcut_login_required(True)
app.enable()
global service global service
if service is None: if service is None:
@ -108,11 +119,16 @@ def setup(helper, old_version=None):
def get_safe_search_setting(): 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']) value = actions.superuser_run('searx', ['get-safe-search'])
return int(value.strip()) 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(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return (action_utils.webserver_is_enabled('searx-freedombox') return (action_utils.webserver_is_enabled('searx-freedombox')

View File

@ -0,0 +1,9 @@
<Location /searx/>
Include includes/freedombox-single-sign-on.conf
<IfModule mod_auth_pubtkt.c>
TKTAuthToken "web-search" "admin"
</IfModule>
ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
</Location>

View File

@ -9,12 +9,6 @@
<Location /searx/> <Location /searx/>
Include includes/freedombox-single-sign-on.conf
<IfModule mod_auth_pubtkt.c>
TKTAuthToken "web-search" "admin"
</IfModule>
ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/ ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
</Location> </Location>

View File

@ -26,8 +26,11 @@ from plinth.forms import ServiceForm
class SearxForm(ServiceForm): class SearxForm(ServiceForm):
"""Searx configuration form.""" """Searx configuration form."""
safe_search = forms.ChoiceField(label=_('Safe Search'), help_text=_( safe_search = forms.ChoiceField(
'Select the default family filter to apply to your search results.'), label=_('Safe Search'), help_text=_(
choices=((0, _('None')), 'Select the default family filter to apply to your search results.'
(1, _('Moderate')), ), choices=((0, _('None')), (1, _('Moderate')), (2, _('Strict'))))
(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)

View File

@ -23,8 +23,9 @@ from django.utils.translation import ugettext as _
from plinth import actions, views from plinth import actions, views
from plinth.errors import ActionError from plinth.errors import ActionError
from plinth.modules.searx import (clients, description, from plinth.modules.searx import (add_shortcut, clients, description,
get_safe_search_setting, manual_page) get_safe_search_setting,
is_public_access_enabled, manual_page)
from .forms import SearxForm from .forms import SearxForm
@ -43,6 +44,7 @@ class SearxServiceView(views.ServiceView):
"""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'] = get_safe_search_setting() initial['safe_search'] = get_safe_search_setting()
initial['public_access'] = is_public_access_enabled()
return initial return initial
def form_valid(self, form): def form_valid(self, form):
@ -59,4 +61,16 @@ class SearxServiceView(views.ServiceView):
messages.error(self.request, messages.error(self.request,
_('An error occurred during configuration.')) _('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) return super().form_valid(form)