mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-28 08:03:36 +00:00
428 lines
14 KiB
Python
428 lines
14 KiB
Python
#
|
|
# This file is part of FreedomBox.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
from time import sleep
|
|
|
|
import splinter
|
|
|
|
from support import config, interface, site
|
|
from support.interface import submit
|
|
from support.service import eventually, wait_for_page_update
|
|
|
|
# unlisted apps just use the app_name as module name
|
|
app_module = {
|
|
'ntp': 'datetime',
|
|
'wiki': 'ikiwiki',
|
|
'tt-rss': 'ttrss',
|
|
}
|
|
|
|
app_checkbox_id = {
|
|
'tor': 'id_tor-enabled',
|
|
}
|
|
|
|
default_url = config['DEFAULT']['url']
|
|
|
|
apps_with_loaders = ['tor']
|
|
|
|
|
|
def get_app_module(app_name):
|
|
module = app_name
|
|
if app_name in app_module:
|
|
module = app_module[app_name]
|
|
return module
|
|
|
|
|
|
def get_app_checkbox_id(app_name):
|
|
checkbox_id = 'id_is_enabled'
|
|
if app_name in app_checkbox_id:
|
|
checkbox_id = app_checkbox_id[app_name]
|
|
return checkbox_id
|
|
|
|
|
|
def install(browser, app_name):
|
|
interface.nav_to_module(browser, get_app_module(app_name))
|
|
install = browser.find_by_css('.form-install input[type=submit]')
|
|
|
|
def install_in_progress():
|
|
selectors = [
|
|
'.install-state-' + state
|
|
for state in ['pre', 'post', 'installing']
|
|
]
|
|
return any(
|
|
browser.is_element_present_by_css(selector)
|
|
for selector in selectors)
|
|
|
|
def is_server_restarting():
|
|
return browser.is_element_present_by_css('.neterror')
|
|
|
|
def wait_for_install():
|
|
if install_in_progress():
|
|
sleep(1)
|
|
elif is_server_restarting():
|
|
sleep(1)
|
|
browser.visit(browser.url)
|
|
else:
|
|
return
|
|
wait_for_install()
|
|
|
|
if install:
|
|
install.click()
|
|
wait_for_install()
|
|
sleep(2) # XXX This shouldn't be required.
|
|
|
|
|
|
def is_installed(browser, app_name):
|
|
interface.nav_to_module(browser, get_app_module(app_name))
|
|
install_button = browser.find_by_css('.form-install input[type=submit]')
|
|
return not bool(install_button)
|
|
|
|
|
|
def _change_status(browser, app_name, change_status_to='enabled',
|
|
checkbox_id=None):
|
|
interface.nav_to_module(browser, get_app_module(app_name))
|
|
checkbox_id = checkbox_id or get_app_checkbox_id(app_name)
|
|
checkbox = browser.find_by_id(checkbox_id)
|
|
checkbox.check() if change_status_to == 'enabled' else checkbox.uncheck()
|
|
interface.submit(browser, form_class='form-configuration')
|
|
if app_name in apps_with_loaders:
|
|
wait_for_config_update(browser, app_name)
|
|
|
|
|
|
def enable(browser, app_name):
|
|
_change_status(browser, app_name, 'enabled')
|
|
|
|
|
|
def disable(browser, app_name):
|
|
_change_status(browser, app_name, 'disabled')
|
|
|
|
|
|
def wait_for_config_update(browser, app_name):
|
|
while browser.is_element_present_by_css('.running-status.loading'):
|
|
sleep(0.1)
|
|
|
|
|
|
def select_domain_name(browser, app_name, domain_name):
|
|
browser.visit('{}/plinth/apps/{}/setup/'.format(default_url, app_name))
|
|
drop_down = browser.find_by_id('id_domain_name')
|
|
drop_down.select(domain_name)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
def configure_shadowsocks(browser, server, password):
|
|
"""Configure shadowsocks client with given server details."""
|
|
browser.visit('{}/plinth/apps/shadowsocks/'.format(default_url))
|
|
browser.find_by_id('id_server').fill(server)
|
|
browser.find_by_id('id_password').fill(password)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
def shadowsocks_get_configuration(browser):
|
|
"""Return the server and password currently configured in shadowsocks."""
|
|
browser.visit('{}/plinth/apps/shadowsocks/'.format(default_url))
|
|
server = browser.find_by_id('id_server').value
|
|
password = browser.find_by_id('id_password').value
|
|
return server, password
|
|
|
|
|
|
def modify_max_file_size(browser, size):
|
|
"""Change the maximum file size of coquelicot to the given value"""
|
|
browser.visit('{}/plinth/apps/coquelicot/'.format(default_url))
|
|
browser.find_by_id('id_max_file_size').fill(size)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
def get_max_file_size(browser):
|
|
"""Get the maximum file size of coquelicot"""
|
|
browser.visit('{}/plinth/apps/coquelicot/'.format(default_url))
|
|
return int(browser.find_by_id('id_max_file_size').value)
|
|
|
|
|
|
def modify_upload_password(browser, password):
|
|
"""Change the upload password for coquelicot to the given value"""
|
|
browser.visit('{}/plinth/apps/coquelicot/'.format(default_url))
|
|
browser.find_by_id('id_upload_password').fill(password)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
# Sharing app helper functions
|
|
|
|
|
|
def remove_share(browser, name):
|
|
"""Remove a share in sharing app."""
|
|
try:
|
|
share_row = get_share(browser, name)
|
|
except splinter.exceptions.ElementDoesNotExist:
|
|
pass
|
|
else:
|
|
share_row.find_by_css('.share-remove')[0].click()
|
|
|
|
|
|
def add_share(browser, name, path, group):
|
|
"""Add a share in sharing app."""
|
|
browser.visit('{}/plinth/apps/sharing/add/'.format(default_url))
|
|
browser.fill('sharing-name', name)
|
|
browser.fill('sharing-path', path)
|
|
browser.find_by_css(
|
|
'#id_sharing-groups input[value="{}"]'.format(group)).check()
|
|
submit(browser)
|
|
|
|
|
|
def edit_share(browser, old_name, new_name, path, group):
|
|
"""Edit a share in sharing app."""
|
|
row = get_share(browser, old_name)
|
|
with wait_for_page_update(browser):
|
|
row.find_by_css('.share-edit')[0].click()
|
|
browser.fill('sharing-name', new_name)
|
|
browser.fill('sharing-path', path)
|
|
browser.find_by_css('#id_sharing-groups input').uncheck()
|
|
browser.find_by_css(
|
|
'#id_sharing-groups input[value="{}"]'.format(group)).check()
|
|
submit(browser)
|
|
|
|
|
|
def get_share(browser, name):
|
|
"""Return the row for a given share."""
|
|
browser.visit('{}/plinth/apps/sharing/'.format(default_url))
|
|
return browser.find_by_id('share-{}'.format(name))[0]
|
|
|
|
|
|
def verify_share(browser, name, path, group):
|
|
"""Verfiy that a share exists in list of shares."""
|
|
href = '{}/share/{}'.format(default_url, name)
|
|
url = '/share/{}'.format(name)
|
|
row = get_share(browser, name)
|
|
assert row.find_by_css('.share-name')[0].text == name
|
|
assert row.find_by_css('.share-path')[0].text == path
|
|
assert row.find_by_css('.share-url a')[0]['href'] == href
|
|
assert row.find_by_css('.share-url a')[0].text == url
|
|
assert row.find_by_css('.share-groups')[0].text == group
|
|
|
|
|
|
def access_share(browser, name):
|
|
"""Visit a share and see if it is accessible."""
|
|
row = get_share(browser, name)
|
|
url = row.find_by_css('.share-url a')[0]['href']
|
|
browser.visit(url)
|
|
assert '/share/{}'.format(name) in browser.title
|
|
|
|
|
|
def verify_nonexistant_share(browser, name):
|
|
"""Verify that given URL for a given share name is a 404."""
|
|
url = '{}/share/{}'.format(default_url, name)
|
|
browser.visit(url)
|
|
assert '404' in browser.title
|
|
|
|
|
|
def verify_inaccessible_share(browser, name):
|
|
"""Verify that given URL for a given share name denies permission."""
|
|
url = '{}/share/{}'.format(default_url, name)
|
|
browser.visit(url)
|
|
eventually(lambda: '/plinth' in browser.url, args=[])
|
|
|
|
|
|
def enable_mediawiki_public_registrations(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'enabled',
|
|
checkbox_id='id_enable_public_registrations')
|
|
|
|
|
|
def disable_mediawiki_public_registrations(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'disabled',
|
|
checkbox_id='id_enable_public_registrations')
|
|
|
|
|
|
def enable_mediawiki_private_mode(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'enabled',
|
|
checkbox_id='id_enable_private_mode')
|
|
|
|
|
|
def disable_mediawiki_private_mode(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'disabled',
|
|
checkbox_id='id_enable_private_mode')
|
|
|
|
|
|
def set_mediawiki_admin_password(browser, password):
|
|
"""Set a password for the MediaWiki user called admin."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
browser.find_by_id('id_password').fill(password)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
def enable_ejabberd_message_archive_management(browser):
|
|
"""Enable Message Archive Management in Ejabberd."""
|
|
interface.nav_to_module(browser, 'ejabberd')
|
|
_change_status(browser, 'ejabberd', 'enabled',
|
|
checkbox_id='id_MAM_enabled')
|
|
|
|
|
|
def disable_ejabberd_message_archive_management(browser):
|
|
"""Enable Message Archive Management in Ejabberd."""
|
|
interface.nav_to_module(browser, 'ejabberd')
|
|
_change_status(browser, 'ejabberd', 'disabled',
|
|
checkbox_id='id_MAM_enabled')
|
|
|
|
|
|
def ejabberd_add_contact(browser):
|
|
"""Add a contact to Ejabberd user's roster."""
|
|
site.jsxc_add_contact(browser)
|
|
|
|
|
|
def ejabberd_delete_contact(browser):
|
|
"""Delete the contact from Ejabberd user's roster."""
|
|
site.jsxc_delete_contact(browser)
|
|
|
|
|
|
def ejabberd_has_contact(browser):
|
|
"""Check whether the contact is in Ejabberd user's roster."""
|
|
return eventually(site.jsxc_has_contact, [browser])
|
|
|
|
|
|
def ikiwiki_create_wiki_if_needed(browser):
|
|
"""Create wiki if it does not exist."""
|
|
interface.nav_to_module(browser, 'ikiwiki')
|
|
browser.find_link_by_href('/plinth/apps/ikiwiki/manage/').first.click()
|
|
wiki = browser.find_link_by_href('/ikiwiki/wiki')
|
|
if not wiki:
|
|
browser.find_link_by_href('/plinth/apps/ikiwiki/create/').first.click()
|
|
browser.find_by_id('id_ikiwiki-name').fill('wiki')
|
|
browser.find_by_id('id_ikiwiki-admin_name').fill(
|
|
config['DEFAULT']['username'])
|
|
browser.find_by_id('id_ikiwiki-admin_password').fill(
|
|
config['DEFAULT']['password'])
|
|
submit(browser)
|
|
|
|
|
|
def ikiwiki_delete_wiki(browser):
|
|
"""Delete wiki."""
|
|
interface.nav_to_module(browser, 'ikiwiki')
|
|
browser.find_link_by_href('/plinth/apps/ikiwiki/manage/').first.click()
|
|
browser.find_link_by_href(
|
|
'/plinth/apps/ikiwiki/wiki/delete/').first.click()
|
|
submit(browser)
|
|
|
|
|
|
def ikiwiki_wiki_exists(browser):
|
|
"""Check whether the wiki exists."""
|
|
interface.nav_to_module(browser, 'ikiwiki')
|
|
browser.find_link_by_href('/plinth/apps/ikiwiki/manage/').first.click()
|
|
wiki = browser.find_link_by_href('/ikiwiki/wiki')
|
|
return bool(wiki)
|
|
|
|
|
|
def time_zone_set(browser, time_zone):
|
|
"""Set the system time zone."""
|
|
interface.nav_to_module(browser, 'datetime')
|
|
browser.select('time_zone', time_zone)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
def time_zone_get(browser):
|
|
"""Set the system time zone."""
|
|
interface.nav_to_module(browser, 'datetime')
|
|
return browser.find_by_name('time_zone').first.value
|
|
|
|
|
|
_TOR_FEATURE_TO_ELEMENT = {
|
|
'relay': 'tor-relay_enabled',
|
|
'bridge-relay': 'tor-bridge_relay_enabled',
|
|
'hidden-services': 'tor-hs_enabled',
|
|
'software': 'tor-apt_transport_tor_enabled'
|
|
}
|
|
|
|
|
|
def tor_feature_enable(browser, feature, should_enable):
|
|
"""Enable/disable a Tor feature."""
|
|
if not isinstance(should_enable, bool):
|
|
should_enable = should_enable in ('enable', 'enabled')
|
|
|
|
element_name = _TOR_FEATURE_TO_ELEMENT[feature]
|
|
interface.nav_to_module(browser, 'tor')
|
|
checkbox_element = browser.find_by_name(element_name).first
|
|
if should_enable == checkbox_element.checked:
|
|
return
|
|
|
|
if should_enable:
|
|
if feature == 'bridge-relay':
|
|
browser.find_by_name('tor-relay_enabled').first.check()
|
|
|
|
checkbox_element.check()
|
|
else:
|
|
checkbox_element.uncheck()
|
|
|
|
interface.submit(browser, form_class='form-configuration')
|
|
wait_for_config_update(browser, 'tor')
|
|
|
|
|
|
def tor_assert_feature_enabled(browser, feature, enabled):
|
|
"""Assert whether Tor relay is enabled or disabled."""
|
|
if not isinstance(enabled, bool):
|
|
enabled = enabled in ('enable', 'enabled')
|
|
|
|
element_name = _TOR_FEATURE_TO_ELEMENT[feature]
|
|
interface.nav_to_module(browser, 'tor')
|
|
assert browser.find_by_name(element_name).first.checked == enabled
|
|
|
|
|
|
def tor_get_relay_ports(browser):
|
|
"""Return the list of ports shown in the relay table."""
|
|
interface.nav_to_module(browser, 'tor')
|
|
return [
|
|
port_name.text
|
|
for port_name in browser.find_by_css('.tor-relay-port-name')
|
|
]
|
|
|
|
|
|
def tor_assert_hidden_services(browser):
|
|
"""Assert that hidden service information is shown."""
|
|
interface.nav_to_module(browser, 'tor')
|
|
assert browser.find_by_css('.tor-hs .tor-hs-hostname')
|
|
|
|
|
|
def tahoe_get_introducer(browser, domain, introducer_type):
|
|
"""Return an introducer element with a given type from tahoe-lafs."""
|
|
interface.nav_to_module(browser, 'tahoe')
|
|
css_class = '.{}-introducers .introducer-furl'.format(introducer_type)
|
|
for furl in browser.find_by_css(css_class):
|
|
if domain in furl.text:
|
|
return furl.parent
|
|
|
|
return None
|
|
|
|
|
|
def tahoe_add_introducer(browser, domain):
|
|
"""Add a new introducer into tahoe-lafs."""
|
|
interface.nav_to_module(browser, 'tahoe')
|
|
|
|
furl = 'pb://ewe4zdz6kxn7xhuvc7izj2da2gpbgeir@tcp:{}:3456/' \
|
|
'fko4ivfwgqvybppwar3uehkx6spaaou7'.format(domain)
|
|
browser.fill('pet_name', 'testintroducer')
|
|
browser.fill('furl', furl)
|
|
submit(browser, form_class='form-add-introducer')
|
|
|
|
|
|
def tahoe_remove_introducer(browser, domain):
|
|
"""Remove an introducer from tahoe-lafs."""
|
|
introducer = tahoe_get_introducer(browser, domain, 'connected')
|
|
submit(browser, element=introducer.find_by_css('.form-remove'))
|