Sunil Mohan Adapa 8858774b35
wireguard: Fix issues with QR codes and config download
- Previous changes introduced a regression where QR codes and configuration
download stopped working. This is because of removal of private key from
session. Fix this.

- Provide the QR code and configuration download inline so that private key does
not have to stored in session.

- Provide separate QR code and configuration for each of the available
endpoints.

- Move the manual parameters into a separate table for each of the endpoints.
Make it look like this is only needed as the last option.

- Immediately add the client when the page is presented instead of additional
step (there is no use input needed).

- Tone down the warnings into simple text.

- Provide additional parameters in the manual configuration parameters tables to
make it easy to configure (based on the parameters configured on the WireGuard
app for Android). Use terminology that is used in app so that the fields can be
mapped easily by the user.

Tests:

- Functional tests work more reliably now.

- Clicking on the 'Add Client Automatically' creates the client successfully. A
new message is shown with client public key. The description looks good.

- Clicking on 'Download config file', 'Show QR code' and 'Show config
parameters' works. Downloaded configuration file works. QR code works too and
fills in expected parameters. Shown manual parameters are correct.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-10-06 06:59:46 -04:00

265 lines
11 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Functional, browser based tests for wireguard app.
"""
import urllib
import pytest
from freedombox.tests import functional
pytestmark = [pytest.mark.apps, pytest.mark.wireguard]
class TestWireguardApp(functional.BaseAppTests):
app_name = 'wireguard'
has_web = False
_client_public_key1 = '8dlHfuAsU0WsnpQkNd8fguwmBtFX5/CkO0xYKbNBt24='
_client_public_key2 = 'xM/vyXbYtMf9bDTZY3W2x7Moxw4m1ckpnZvVSd4DLho='
_server = [
{
'peer_endpoint': 'wg1.example.org:1234',
'peer_public_key': 'HBCqZk4B93N6q19zNleJkAVs+PEfWAPgPpKnrhL/CVw=',
'ip_address_and_network': '10.0.0.2/32',
'private_key': '',
'preshared_key': ''
},
{
'peer_endpoint': 'wg2.example.org:5678',
'peer_public_key': 'Z/iHo0vaeSN8Ykk5KwhQ819MMU5nyzD7y7xFFthlxXI=',
'ip_address_and_network': '192.168.0.2/24',
'private_key': 'QC2xEZMn3bgNsSVFrU51+ALSUiUaWg6gRWigh3EeVm0=',
'preshared_key': 'AHxZ4Rr8Ij4L1aq+ceusSIgBfluqiI9Vb5I2UtQFanI='
},
]
def test_backup_restore(self, session_browser):
"""Skip the test."""
pytest.skip('Does not implement backup/restore.')
@staticmethod
def _get_client_href(key):
"""Return the href for client show page."""
key = urllib.parse.quote(urllib.parse.quote(key, safe=''))
return f'/freedombox/apps/wireguard/client/{key}/show/'
def _client_exists(self, browser, key):
"""Check whether a client key exists."""
functional.nav_to_module(browser, 'wireguard')
href = browser.links.find_by_href(self._get_client_href(key))
return bool(href)
def _assert_client_details(self, browser, key):
"""Check that the client's details are correct."""
def get_value(key):
return browser.find_by_css(f'tr.{key} > td').first.text
functional.nav_to_module(browser, 'wireguard')
href = browser.links.find_by_href(self._get_client_href(key))
href.first.click()
assert get_value('client-public-key') == key
@staticmethod
def _add_client(browser, key):
"""Add a client."""
functional.nav_to_module(browser, 'wireguard')
# Start the server on FreedomBox, if needed.
start_server_button = browser.find_by_css('.btn-start-server')
if start_server_button:
with functional.wait_for_page_update(browser):
start_server_button.first.click()
with functional.wait_for_page_update(browser):
browser.find_by_css('.btn-add-client').first.click()
browser.find_by_id('id_public_key').fill(key)
functional.submit(browser, form_class='form-add-client')
def _edit_client(self, browser, key1, key2):
"""Edit a client"""
functional.nav_to_module(browser, 'wireguard')
with functional.wait_for_page_update(browser):
browser.links.find_by_href(
self._get_client_href(key1)).first.click()
with functional.wait_for_page_update(browser):
browser.find_by_css('.btn-edit-client').first.click()
browser.find_by_id('id_public_key').fill(key2)
functional.submit(browser, form_class='form-edit-client')
def _delete_client(self, browser, key):
"""Delete a client"""
functional.nav_to_module(browser, 'wireguard')
with functional.wait_for_page_update(browser):
browser.links.find_by_href(
self._get_client_href(key)).first.click()
with functional.wait_for_page_update(browser):
browser.find_by_css('.btn-delete-client').first.click()
functional.submit(browser, form_class='form-delete-client')
def test_add_edit_delete_client(self, session_browser):
"""Test adding, editing and deleting a WireGuard client."""
if self._client_exists(session_browser, self._client_public_key1):
self._delete_client(session_browser, self._client_public_key1)
if self._client_exists(session_browser, self._client_public_key2):
self._delete_client(session_browser, self._client_public_key2)
self._add_client(session_browser, self._client_public_key1)
assert self._client_exists(session_browser, self._client_public_key1)
self._edit_client(session_browser, self._client_public_key1,
self._client_public_key2)
assert not self._client_exists(session_browser,
self._client_public_key1)
assert self._client_exists(session_browser, self._client_public_key2)
self._delete_client(session_browser, self._client_public_key2)
assert not self._client_exists(session_browser,
self._client_public_key2)
def test_auto_add_client(self, session_browser):
"""Test the automatic client generation and addition flow."""
functional.nav_to_module(session_browser, 'wireguard')
# Start server if needed (reuse existing logic)
# Extract to reusable method
start_server_button = session_browser.find_by_css('.btn-start-server')
if start_server_button:
with functional.wait_for_page_update(session_browser):
start_server_button.first.click()
with functional.wait_for_page_update(session_browser):
session_browser.find_by_css('.btn-auto-add-client').first.click()
# Verify that a new client was added
assert session_browser.find_by_css('.alert-client-added')
# Read public key
session_browser.find_by_css(
'.btn-show-config-parameters').first.click()
client_pubkey = session_browser.find_by_css(
'.client-public-key').first.text.strip()
assert client_pubkey
# Verify private key is shown
assert session_browser.find_by_css('.client-private-key').first.text
# Assert that at least one of each buttons are present
assert session_browser.find_by_css('.btn-download-config-file')
assert session_browser.find_by_css('.btn-show-qr-code')
assert session_browser.find_by_css('.btn-show-config-parameters')
# Clean up
self._delete_client(session_browser, client_pubkey)
assert not self._client_exists(session_browser, client_pubkey)
@staticmethod
def _get_server_href(browser, key):
"""Return the href for server show page."""
return browser.find_by_css(
f'.peer-public-key[data-public-key="{key}"] a')
def _server_exists(self, browser, config):
"""Check whether a server key exists."""
functional.nav_to_module(browser, 'wireguard')
href = self._get_server_href(browser, config['peer_public_key'])
return bool(href)
def _assert_server_details(self, browser, config):
"""Check that the server's details are correct."""
def get_value(key):
return browser.find_by_css(f'tr.{key} > td').first.text
functional.nav_to_module(browser, 'wireguard')
with functional.wait_for_page_update(browser):
href = self._get_server_href(browser, config['peer_public_key'])
href.first.click()
assert get_value('peer-endpoint') == config['peer_endpoint']
assert get_value('peer-public-key') == config['peer_public_key']
assert get_value('server-ip-address-and-network'
) == config['ip_address_and_network']
assert get_value('peer-preshared-key') == (config['preshared_key']
or 'None')
@staticmethod
def _add_server(browser, config):
"""Add a server."""
functional.nav_to_module(browser, 'wireguard')
with functional.wait_for_page_update(browser):
browser.find_by_css('.btn-add-server').first.click()
browser.find_by_id('id_peer_endpoint').fill(config['peer_endpoint'])
browser.find_by_id('id_peer_public_key').fill(
config['peer_public_key'])
browser.find_by_id('id_ip_address_and_network').fill(
config['ip_address_and_network'])
browser.find_by_id('id_private_key').fill(config['private_key'])
browser.find_by_id('id_preshared_key').fill(config['preshared_key'])
functional.submit(browser, form_class='form-add-server')
def _edit_server(self, browser, config1, config2):
"""Edit a server."""
functional.nav_to_module(browser, 'wireguard')
with functional.wait_for_page_update(browser):
href = self._get_server_href(browser, config1['peer_public_key'])
href.first.click()
with functional.wait_for_page_update(browser):
browser.find_by_css('.btn-edit-server').first.click()
browser.find_by_id('id_peer_endpoint').fill(config2['peer_endpoint'])
browser.find_by_id('id_peer_public_key').fill(
config2['peer_public_key'])
browser.find_by_id('id_ip_address_and_network').fill(
config2['ip_address_and_network'])
browser.find_by_id('id_private_key').fill(config2['private_key'])
browser.find_by_id('id_preshared_key').fill(config2['preshared_key'])
functional.submit(browser, form_class='form-edit-server')
def _delete_server(self, browser, config):
"""Delete a server"""
functional.nav_to_module(browser, 'wireguard')
with functional.wait_for_page_update(browser):
href = self._get_server_href(browser, config['peer_public_key'])
href.first.click()
with functional.wait_for_page_update(browser):
browser.find_by_css('.btn-delete-server').first.click()
functional.submit(browser, form_class='form-delete-server')
def test_add_edit_delete_server(self, session_browser):
"""Test adding, editing and deleting a WireGuard server."""
if self._server_exists(session_browser, self._server[0]):
self._delete_server(session_browser, self._server[0])
if self._server_exists(session_browser, self._server[1]):
self._delete_server(session_browser, self._server[1])
self._add_server(session_browser, self._server[0])
self._assert_server_details(session_browser, self._server[0])
# XXX: Editing the connection is very flaky. Fails, incorrect and
# crashes. Fix it.
# self._edit_server(session_browser, self._server[0],
# self._server[1]) assert not self._server_exists(session_browser,
# self._server[0]) self._assert_server_details(session_browser,
# self._server[1])
# self._delete_server(session_browser, self._server[1])
# assert not self._server_exists(session_browser, self._server[1])
self._delete_server(session_browser, self._server[0])
assert not self._server_exists(session_browser, self._server[0])