mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
751 lines
25 KiB
Python
751 lines
25 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import contextlib
|
|
import os
|
|
import random
|
|
import shutil
|
|
import string
|
|
import subprocess
|
|
import tempfile
|
|
import urllib
|
|
from time import sleep
|
|
|
|
import requests
|
|
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',
|
|
'openvpn': 'id_openvpn-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 = 'app-toggle-input'
|
|
if app_name in app_checkbox_id:
|
|
checkbox_id = app_checkbox_id[app_name]
|
|
return checkbox_id
|
|
|
|
|
|
def _find_install_button(browser, app_name):
|
|
interface.nav_to_module(browser, get_app_module(app_name))
|
|
return browser.find_by_css('.form-install input[type=submit]')
|
|
|
|
|
|
def install(browser, app_name):
|
|
install_button = _find_install_button(browser, app_name)
|
|
|
|
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_button:
|
|
install_button.click()
|
|
wait_for_install()
|
|
# sleep(2) # XXX This shouldn't be required.
|
|
|
|
|
|
def is_installed(browser, app_name):
|
|
install_button = _find_install_button(browser, app_name)
|
|
return not bool(install_button)
|
|
|
|
|
|
def _change_app_status(browser, app_name, change_status_to='enabled'):
|
|
"""Enable or disable application."""
|
|
button = browser.find_by_id('app-toggle-button')
|
|
checkbox_id = get_app_checkbox_id(app_name)
|
|
checkbox = browser.find_by_id(checkbox_id)
|
|
if button:
|
|
if checkbox.checked and change_status_to == 'disabled' or (
|
|
not checkbox.checked and change_status_to == 'enabled'):
|
|
interface.submit(browser, element=button)
|
|
else:
|
|
_change_status(browser, app_name, checkbox_id, change_status_to)
|
|
|
|
if app_name in apps_with_loaders:
|
|
wait_for_config_update(browser, app_name)
|
|
|
|
|
|
def _change_status(browser, app_name, checkbox_id, change_status_to='enabled'):
|
|
"""Change checkbox status."""
|
|
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):
|
|
interface.nav_to_module(browser, get_app_module(app_name))
|
|
_change_app_status(browser, app_name, 'enabled')
|
|
|
|
|
|
def disable(browser, app_name):
|
|
interface.nav_to_module(browser, get_app_module(app_name))
|
|
_change_app_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 _download_file(browser, url):
|
|
"""Return file contents after downloading a URL."""
|
|
cookies = browser.cookies.all()
|
|
response = requests.get(url, cookies=cookies, verify=False)
|
|
if response.status_code != 200:
|
|
raise Exception('URL download failed')
|
|
|
|
return response.content
|
|
|
|
|
|
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 make_share_public(browser, name):
|
|
"""Make share publicly accessible."""
|
|
row = get_share(browser, name)
|
|
with wait_for_page_update(browser):
|
|
row.find_by_css('.share-edit')[0].click()
|
|
browser.find_by_id('id_sharing-is_public').check()
|
|
interface.submit(browser)
|
|
|
|
|
|
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', 'id_enable_public_registrations',
|
|
'enabled')
|
|
|
|
|
|
def disable_mediawiki_public_registrations(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'id_enable_public_registrations',
|
|
'disabled')
|
|
|
|
|
|
def enable_mediawiki_private_mode(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'id_enable_private_mode', 'enabled')
|
|
|
|
|
|
def disable_mediawiki_private_mode(browser):
|
|
"""Enable public registrations in MediaWiki."""
|
|
interface.nav_to_module(browser, 'mediawiki')
|
|
_change_status(browser, 'mediawiki', 'id_enable_private_mode', 'disabled')
|
|
|
|
|
|
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', 'id_MAM_enabled', 'enabled')
|
|
|
|
|
|
def disable_ejabberd_message_archive_management(browser):
|
|
"""Enable Message Archive Management in Ejabberd."""
|
|
interface.nav_to_module(browser, 'ejabberd')
|
|
_change_status(browser, 'ejabberd', 'id_MAM_enabled', 'disabled')
|
|
|
|
|
|
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 gitweb_create_repo(browser, repo, access=None, ok_if_exists=False):
|
|
"""Create repository."""
|
|
if not gitweb_repo_exists(browser, repo, access):
|
|
gitweb_delete_repo(browser, repo, ignore_missing=True)
|
|
browser.find_link_by_href('/plinth/apps/gitweb/create/').first.click()
|
|
browser.find_by_id('id_gitweb-name').fill(repo)
|
|
if access == 'private':
|
|
browser.find_by_id('id_gitweb-is_private').check()
|
|
elif access == 'public':
|
|
browser.find_by_id('id_gitweb-is_private').uncheck()
|
|
submit(browser)
|
|
elif not ok_if_exists:
|
|
assert False, 'Repo already exists.'
|
|
|
|
|
|
def gitweb_delete_repo(browser, repo, ignore_missing=False):
|
|
"""Delete repository."""
|
|
interface.nav_to_module(browser, 'gitweb')
|
|
delete_link = browser.find_link_by_href(
|
|
'/plinth/apps/gitweb/{}/delete/'.format(repo))
|
|
if delete_link or not ignore_missing:
|
|
delete_link.first.click()
|
|
submit(browser)
|
|
|
|
|
|
def gitweb_edit_repo_metadata(browser, repo, metadata):
|
|
"""Set repository metadata."""
|
|
interface.nav_to_module(browser, 'gitweb')
|
|
browser.find_link_by_href(
|
|
'/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click()
|
|
if 'name' in metadata:
|
|
browser.find_by_id('id_gitweb-name').fill(metadata['name'])
|
|
if 'description' in metadata:
|
|
browser.find_by_id('id_gitweb-description').fill(
|
|
metadata['description'])
|
|
if 'owner' in metadata:
|
|
browser.find_by_id('id_gitweb-owner').fill(metadata['owner'])
|
|
if 'access' in metadata:
|
|
if metadata['access'] == 'private':
|
|
browser.find_by_id('id_gitweb-is_private').check()
|
|
else:
|
|
browser.find_by_id('id_gitweb-is_private').uncheck()
|
|
submit(browser)
|
|
|
|
|
|
def gitweb_get_repo_metadata(browser, repo):
|
|
"""Get repository metadata."""
|
|
interface.nav_to_module(browser, 'gitweb')
|
|
browser.find_link_by_href(
|
|
'/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click()
|
|
metadata = {}
|
|
for item in ['name', 'description', 'owner']:
|
|
metadata[item] = browser.find_by_id('id_gitweb-' + item).value
|
|
if browser.find_by_id('id_gitweb-is_private').value:
|
|
metadata['access'] = 'private'
|
|
else:
|
|
metadata['access'] = 'public'
|
|
return metadata
|
|
|
|
|
|
def _gitweb_get_repo_url(repo, with_auth):
|
|
""""Get repository URL"""
|
|
scheme = 'http'
|
|
if default_url.startswith('https://'):
|
|
scheme = 'https'
|
|
url = default_url.split('://')[1] if '://' in default_url else default_url
|
|
password = 'gitweb_wrong_password'
|
|
if with_auth:
|
|
password = config['DEFAULT']['password']
|
|
|
|
return '{0}://{1}:{2}@{3}/gitweb/{4}'.format(scheme,
|
|
config['DEFAULT']['username'],
|
|
password, url, repo)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def _gitweb_temp_directory():
|
|
"""Create temporary directory"""
|
|
name = tempfile.mkdtemp(prefix='plinth_test_gitweb_')
|
|
yield name
|
|
shutil.rmtree(name)
|
|
|
|
|
|
def _gitweb_git_command_is_successful(command, cwd):
|
|
"""Check if a command runs successfully or gives authentication error"""
|
|
process = subprocess.run(command, capture_output=True, cwd=cwd)
|
|
if process.returncode != 0:
|
|
if 'Authentication failed' in process.stderr.decode():
|
|
return False
|
|
print(process.stdout.decode())
|
|
# raise exception
|
|
process.check_returncode()
|
|
return True
|
|
|
|
|
|
def gitweb_repo_exists(browser, repo, access=None):
|
|
"""Check whether the repository exists."""
|
|
interface.nav_to_module(browser, 'gitweb')
|
|
links_found = browser.find_link_by_href('/gitweb/{}.git'.format(repo))
|
|
access_matches = True
|
|
if links_found and access:
|
|
parent = links_found.first.find_by_xpath('..').first
|
|
private_icon = parent.find_by_css('.repo-private-icon')
|
|
if access == 'private':
|
|
access_matches = True if private_icon else False
|
|
if access == 'public':
|
|
access_matches = True if not private_icon else False
|
|
return bool(links_found) and access_matches
|
|
|
|
|
|
def gitweb_repo_is_readable(repo, with_auth=False, url_git_extension=False):
|
|
"""Check if a git repo is readable with git client."""
|
|
url = _gitweb_get_repo_url(repo, with_auth)
|
|
if url_git_extension:
|
|
url = url + '.git'
|
|
git_command = ['git', 'clone', '-c', 'http.sslverify=false', url]
|
|
with _gitweb_temp_directory() as cwd:
|
|
return _gitweb_git_command_is_successful(git_command, cwd)
|
|
|
|
|
|
def gitweb_repo_is_writable(repo, with_auth=False, url_git_extension=False):
|
|
"""Check if a git repo is writable with git client."""
|
|
url = _gitweb_get_repo_url(repo, with_auth)
|
|
if url_git_extension:
|
|
url = url + '.git'
|
|
|
|
with _gitweb_temp_directory() as cwd:
|
|
subprocess.run(['mkdir', 'test-project'], check=True, cwd=cwd)
|
|
cwd = os.path.join(cwd, 'test-project')
|
|
prepare_git_repo_commands = [
|
|
'git init -q', 'git config http.sslVerify false',
|
|
'git -c "user.name=Tester" -c "user.email=tester" '
|
|
'commit -q --allow-empty -m "test"'
|
|
]
|
|
for command in prepare_git_repo_commands:
|
|
subprocess.run(command, shell=True, check=True, cwd=cwd)
|
|
git_push_command = ['git', 'push', '-qf', url, 'master']
|
|
|
|
return _gitweb_git_command_is_successful(git_push_command, cwd)
|
|
|
|
|
|
def gitweb_set_repo_access(browser, repo, access):
|
|
"""Set repository as public or private."""
|
|
interface.nav_to_module(browser, 'gitweb')
|
|
browser.find_link_by_href(
|
|
'/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click()
|
|
if access == 'private':
|
|
browser.find_by_id('id_gitweb-is_private').check()
|
|
else:
|
|
browser.find_by_id('id_gitweb-is_private').uncheck()
|
|
submit(browser)
|
|
|
|
|
|
def gitweb_set_all_repos_private(browser):
|
|
"""Set all repositories private"""
|
|
interface.nav_to_module(browser, 'gitweb')
|
|
public_repos = []
|
|
for element in browser.find_by_css('#gitweb-repo-list .list-group-item'):
|
|
if not element.find_by_css('.repo-private-icon'):
|
|
repo = element.find_by_css('.repo-label').first.text
|
|
public_repos.append(repo)
|
|
for repo in public_repos:
|
|
gitweb_set_repo_access(browser, repo, 'private')
|
|
|
|
|
|
def gitweb_site_repo_exists(browser, repo):
|
|
"""Check whether the repository exists on Gitweb site."""
|
|
browser.visit('{}/gitweb'.format(default_url))
|
|
return browser.find_by_css('a[href="/gitweb/{0}.git"]'.format(repo))
|
|
|
|
|
|
def ikiwiki_create_wiki_if_needed(browser):
|
|
"""Create wiki if it does not exist."""
|
|
interface.nav_to_module(browser, 'ikiwiki')
|
|
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/wiki/delete/').first.click()
|
|
submit(browser)
|
|
|
|
|
|
def ikiwiki_wiki_exists(browser):
|
|
"""Check whether the wiki exists."""
|
|
interface.nav_to_module(browser, 'ikiwiki')
|
|
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'))
|
|
|
|
|
|
def radicale_get_access_rights(browser):
|
|
access_rights_types = ['owner_only', 'owner_write', 'authenticated']
|
|
interface.nav_to_module(browser, 'radicale')
|
|
for access_rights_type in access_rights_types:
|
|
if browser.find_by_value(access_rights_type).checked:
|
|
return access_rights_type
|
|
|
|
|
|
def radicale_set_access_rights(browser, access_rights_type):
|
|
interface.nav_to_module(browser, 'radicale')
|
|
browser.choose('access_rights', access_rights_type)
|
|
interface.submit(browser, form_class='form-configuration')
|
|
|
|
|
|
def openvpn_setup(browser):
|
|
"""Setup the OpenVPN application after installation."""
|
|
interface.nav_to_module(browser, 'openvpn')
|
|
setup_form = browser.find_by_css('.form-setup')
|
|
if not setup_form:
|
|
return
|
|
|
|
submit(browser, form_class='form-setup')
|
|
wait_for_config_update(browser, 'openvpn')
|
|
|
|
|
|
def openvpn_download_profile(browser):
|
|
"""Download the current user's profile into a file and return path."""
|
|
interface.nav_to_module(browser, 'openvpn')
|
|
url = browser.find_by_css('.form-profile')['action']
|
|
return _download_file(browser, url)
|
|
|
|
|
|
def samba_set_share(browser, share_type, status='enabled'):
|
|
"""Enable or disable samba share."""
|
|
disk_name = 'disk'
|
|
share_type_name = '{0}_share'.format(share_type)
|
|
interface.nav_to_module(browser, 'samba')
|
|
for elem in browser.find_by_tag('td'):
|
|
if elem.text == disk_name:
|
|
share_form = elem.find_by_xpath('(..//*)[2]/form').first
|
|
share_btn = share_form.find_by_name(share_type_name).first
|
|
if status == 'enabled' and share_btn['value'] == 'enable':
|
|
share_btn.click()
|
|
elif status == 'disabled' and share_btn['value'] == 'disable':
|
|
share_btn.click()
|
|
break
|
|
|
|
|
|
def _samba_write_to_share(share_type, as_guest=False):
|
|
"""Write to the samba share, return output messages as string."""
|
|
disk_name = 'disk'
|
|
if share_type == 'open':
|
|
share_name = disk_name
|
|
else:
|
|
share_name = '{0}_{1}'.format(disk_name, share_type)
|
|
hostname = urllib.parse.urlparse(default_url).hostname
|
|
servicename = '\\\\{0}\\{1}'.format(hostname, share_name)
|
|
directory = '_plinth-test_{0}'.format(''.join(
|
|
random.SystemRandom().choices(string.ascii_letters, k=8)))
|
|
port = config['DEFAULT']['samba_port']
|
|
|
|
smb_command = ['smbclient', '-W', 'WORKGROUP', '-p', port]
|
|
if as_guest:
|
|
smb_command += ['-N']
|
|
else:
|
|
smb_command += [
|
|
'-U', '{0}%{1}'.format(config['DEFAULT']['username'],
|
|
config['DEFAULT']['password'])
|
|
]
|
|
smb_command += [
|
|
servicename, '-c', 'mkdir {0}; rmdir {0}'.format(directory)
|
|
]
|
|
|
|
return subprocess.check_output(smb_command).decode()
|
|
|
|
|
|
def samba_assert_share_is_writable(share_type, as_guest=False):
|
|
"""Assert that samba share is writable."""
|
|
output = _samba_write_to_share(share_type, as_guest=False)
|
|
|
|
assert not output, output
|
|
|
|
|
|
def samba_assert_share_is_not_accessible(share_type, as_guest=False):
|
|
"""Assert that samba share is not accessible."""
|
|
try:
|
|
_samba_write_to_share(share_type, as_guest)
|
|
except subprocess.CalledProcessError as err:
|
|
err_output = err.output.decode()
|
|
assert 'NT_STATUS_ACCESS_DENIED' in err_output, err_output
|
|
else:
|
|
assert False, 'Can access the share.'
|
|
|
|
|
|
def samba_assert_share_is_not_available(share_type):
|
|
"""Assert that samba share is not accessible."""
|
|
try:
|
|
_samba_write_to_share(share_type)
|
|
except subprocess.CalledProcessError as err:
|
|
err_output = err.output.decode()
|
|
assert 'NT_STATUS_BAD_NETWORK_NAME' in err_output, err_output
|
|
else:
|
|
assert False, 'Can access the share.'
|
|
|
|
|
|
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
|