FreedomBox/plinth/modules/coturn/tests/test_components.py
Sunil Mohan Adapa e6e6896d0d
coturn: Fix incorrectly passing transport argument to STUN URIs
Closes: #2362.

Passing ?transport= parameter in STUN URIs is invalid. It always uses UDP.
Chrome and perhaps Firefox has recently started enforcing the correct syntax
leading to failures using the Coturn server URIs we set in Janus. This also
likely effects matrix-syanpse and ejabberd clients.

Links:

1) https://www.rfc-editor.org/rfc/rfc7064#section-3.1
2) https://bugs.chromium.org/p/chromium/issues/detail?id=1385735

Tests:

- Install Coturn. Observe that STUN URIs shown don't contain the 'transport'
parameter.

- Install Janus and launch the meeting room. Notice that the STUN URIs in the
room page don't have 'transport' parameter.

- Install ejabberd and notice that the auto-configured STUN URIs don't have
'transport' parameter.

- Install matrix-synapse and notice that the auto-configured STUN URIs don't
have 'transport' parameter.

- Install ejabberd and matrix-synapse. Ensure that STUN URIs manually. They are
not allowed to 'transport' parameter for the STUN URIs but must have transport
parameter for TURN URIs.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2023-11-05 06:43:54 -05:00

136 lines
5.0 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Tests for the Coturn app component.
"""
import json
from unittest.mock import call, patch
import pytest
from plinth.utils import random_string
from .. import notify_configuration_change
from ..components import (TurnConfiguration, TurnConsumer,
TurnTimeLimitedConsumer, UserTurnConfiguration)
@pytest.fixture(name='turn_configuration')
def fixture_turn_configuration():
"""Return test Coturn configuration."""
return TurnConfiguration('test-domain.example', [], random_string(64))
@pytest.fixture(name='empty_component_list', autouse=True)
def fixture_empty_component_list():
"""Remove all entries in component list before starting a test."""
TurnConsumer._all = {}
def test_turn_configuration_init():
"""Test creating configuration object."""
config = TurnConfiguration('test-domain.example', [], 'test-shared-secret')
assert config.domain == 'test-domain.example'
assert config.shared_secret == 'test-shared-secret'
assert config.uris == [
'stun:test-domain.example:3478',
'turn:test-domain.example:3478?transport=tcp',
'turn:test-domain.example:3478?transport=udp',
]
config = TurnConfiguration(None, ['test-uri1', 'test-uri2'],
'test-shared-secret')
assert config.domain is None
assert config.uris == ['test-uri1', 'test-uri2']
config = TurnConfiguration('test-domain.example',
['test-uri1', 'test-uri2'],
'test-shared-secret')
assert config.domain == 'test-domain.example'
assert config.uris == ['test-uri1', 'test-uri2']
config = UserTurnConfiguration('test-domain.example',
['test-uri1', 'test-uri2'], None,
'test-username', 'test-credential')
assert config.domain == 'test-domain.example'
assert config.uris == ['test-uri1', 'test-uri2']
assert config.shared_secret is None
assert config.username == 'test-username'
assert config.credential == 'test-credential'
def test_turn_configuration_to_json():
"""Test exporting configuration to JSON."""
config = TurnConfiguration('test-domain.example', [], 'test-shared-secret')
assert json.loads(config.to_json()) == {
'domain': 'test-domain.example',
'uris': [
'stun:test-domain.example:3478',
'turn:test-domain.example:3478?transport=tcp',
'turn:test-domain.example:3478?transport=udp',
],
'shared_secret': 'test-shared-secret'
}
def test_turn_configuration_validate_turn_uris():
"""Test validation method to check for STUN/TURN URIs."""
valid_uris = [
'stun:test-domain.example:3478',
'turn:test-domain.example:3478?transport=tcp',
'turn:test-domain.example:3478?transport=udp',
]
invalid_uris = [
'x-invalid-uri',
'stun:',
'stun:domain-port-missing.example',
'stun:testing.example:1234invalid-append',
'turn:testing.example:1234',
'turn:testing.example:1234?invalid-param=value',
'turn:testing.example:1234?transport=invalid-value',
'turn:testing.example:1234?transport=tcp-invalid-append',
]
assert TurnConfiguration().validate_turn_uris(valid_uris)
for uri in invalid_uris:
assert not TurnConfiguration().validate_turn_uris([uri])
def test_component_init_and_list():
"""Test initializing and listing all the components."""
component1 = TurnConsumer('component1')
component2 = TurnConsumer('component2')
component3 = TurnTimeLimitedConsumer('component3')
assert component1.component_id == 'component1'
assert [component1, component2, component3] == list(TurnConsumer.list())
@patch('plinth.modules.coturn.get_config')
def test_notify_on_configuration_changed(get_config, turn_configuration):
"""Test configuration change notifications."""
component = TurnConsumer('component')
get_config.return_value = turn_configuration
with patch.object(component, 'on_config_change') as mock_method:
notify_configuration_change()
mock_method.assert_has_calls([call(turn_configuration)])
@patch('plinth.modules.coturn.get_config')
def test_get_configuration(get_config, turn_configuration):
"""Test coturn configuration retrieval using component."""
get_config.return_value = turn_configuration
component = TurnConsumer('component')
assert component.get_configuration() == turn_configuration
@patch('plinth.modules.coturn.get_config')
def test_get_user_configuration(get_config, turn_configuration):
"""Test coturn user configuration retrieval using component."""
get_config.return_value = turn_configuration
component = TurnTimeLimitedConsumer('component')
user_config = component.get_configuration()
assert user_config.domain == turn_configuration.domain
assert user_config.uris == turn_configuration.uris
assert user_config.shared_secret is None
assert user_config.username is not None
assert user_config.credential is not None