diff --git a/lib/Config.php b/lib/Config.php index c3c807e1ad6..f2ddec2fefb 100644 --- a/lib/Config.php +++ b/lib/Config.php @@ -251,7 +251,7 @@ public function getTurnServers(): array { } /** - * Generates a username and password for the TURN server + * Prepares a list of TURN servers with username and password * * @return array */ @@ -259,20 +259,7 @@ public function getTurnSettings(): array { $servers = $this->getTurnServers(); if (empty($servers)) { - return [ - 'schemes' => '', - 'server' => '', - 'username' => '', - 'password' => '', - 'protocols' => '', - ]; - } - - // For now we use a random server from the list - try { - $server = $servers[random_int(0, count($servers) - 1)]; - } catch (\Exception $e) { - $server = $servers[0]; + return []; } // Credentials are valid for 24h @@ -280,15 +267,20 @@ public function getTurnSettings(): array { $timestamp = $this->timeFactory->getTime() + 86400; $rnd = $this->secureRandom->generate(16); $username = $timestamp . ':' . $rnd; - $password = base64_encode(hash_hmac('sha1', $username, $server['secret'], true)); - - return [ - 'schemes' => $server['schemes'], - 'server' => $server['server'], - 'username' => $username, - 'password' => $password, - 'protocols' => $server['protocols'], - ]; + + foreach ($servers as $server) { + $password = base64_encode(hash_hmac('sha1', $username, $server['secret'], true)); + + $turnSettings[] = [ + 'schemes' => $server['schemes'], + 'server' => $server['server'], + 'username' => $username, + 'password' => $password, + 'protocols' => $server['protocols'], + ]; + } + + return $turnSettings; } public function getSignalingMode(bool $cleanExternalSignaling = true): string { diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php index 7f0f41b5795..56bc7a3bd6f 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -146,16 +146,16 @@ public function getSettings(string $apiVersion, string $token = ''): DataRespons $turn = []; $turnSettings = $this->talkConfig->getTurnSettings(); - if (!empty($turnSettings['server'])) { - $schemes = explode(',', $turnSettings['schemes']); - $protocols = explode(',', $turnSettings['protocols']); + foreach ($turnSettings as $turnServer) { + $schemes = explode(',', $turnServer['schemes']); + $protocols = explode(',', $turnServer['protocols']); foreach ($schemes as $scheme) { foreach ($protocols as $proto) { $turn[] = [ - 'url' => [$scheme . ':' . $turnSettings['server'] . '?transport=' . $proto], - 'urls' => [$scheme . ':' . $turnSettings['server'] . '?transport=' . $proto], - 'username' => $turnSettings['username'], - 'credential' => $turnSettings['password'], + 'url' => [$scheme . ':' . $turnServer['server'] . '?transport=' . $proto], + 'urls' => [$scheme . ':' . $turnServer['server'] . '?transport=' . $proto], + 'username' => $turnServer['username'], + 'credential' => $turnServer['password'], ]; } } diff --git a/tests/php/ConfigTest.php b/tests/php/ConfigTest.php index c533e36f283..cc1c7a95b89 100644 --- a/tests/php/ConfigTest.php +++ b/tests/php/ConfigTest.php @@ -117,13 +117,19 @@ public function testGenerateTurnSettings() { ->willReturn(json_encode([ [ // No scheme explicitly given - 'server' => 'turn.example.org', + 'server' => 'turn.example.org:3478', 'secret' => 'thisisasupersecretsecret', 'protocols' => 'udp,tcp', ], [ 'schemes' => 'turn,turns', - 'server' => 'turn2.example.com', + 'server' => 'turn2.example.com:5349', + 'secret' => 'ThisIsAlsoSuperSecret', + 'protocols' => 'udp', + ], + [ + 'schemes' => 'turns', + 'server' => 'turn-tls.example.com:443', 'secret' => 'ThisIsAlsoSuperSecret', 'protocols' => 'tcp', ], @@ -149,24 +155,53 @@ public function testGenerateTurnSettings() { $helper = new Config($config, $secureRandom, $groupManager, $timeFactory); // - $server = $helper->getTurnSettings(); - if ($server['server'] === 'turn.example.org') { - $this->assertSame([ - 'schemes' => 'turn', - 'server' => 'turn.example.org', - 'username' => '1479829425:abcdefghijklmnop', - 'password' => '4VJLVbihLzuxgMfDrm5C3zy8kLQ=', - 'protocols' => 'udp,tcp', - ], $server); - } else { - $this->assertSame([ - 'schemes' => 'turn,turns', - 'server' => 'turn2.example.com', - 'username' => '1479829425:abcdefghijklmnop', - 'password' => 'Ol9DEqnvyN4g+IAM+vFnqhfWUTE=', - 'protocols' => 'tcp', - ], $server); - } + $settings = $helper->getTurnSettings(); + $this->assertEquals(3, count($settings)); + $this->assertSame([ + 'schemes' => 'turn', + 'server' => 'turn.example.org:3478', + 'username' => '1479829425:abcdefghijklmnop', + 'password' => '4VJLVbihLzuxgMfDrm5C3zy8kLQ=', + 'protocols' => 'udp,tcp', + ], $settings[0]); + $this->assertSame([ + 'schemes' => 'turn,turns', + 'server' => 'turn2.example.com:5349', + 'username' => '1479829425:abcdefghijklmnop', + 'password' => 'Ol9DEqnvyN4g+IAM+vFnqhfWUTE=', + 'protocols' => 'udp', + ], $settings[1]); + $this->assertSame([ + 'schemes' => 'turns', + 'server' => 'turn-tls.example.com:443', + 'username' => '1479829425:abcdefghijklmnop', + 'password' => 'Ol9DEqnvyN4g+IAM+vFnqhfWUTE=', + 'protocols' => 'tcp', + ], $settings[2]); + } + + public function testGenerateTurnSettingsEmpty() { + /** @var MockObject|IConfig $config */ + $config = $this->createMock(IConfig::class); + $config + ->expects($this->once()) + ->method('getAppValue') + ->with('spreed', 'turn_servers', '') + ->willReturn(json_encode([])); + + /** @var MockObject|ITimeFactory $timeFactory */ + $timeFactory = $this->createMock(ITimeFactory::class); + + /** @var MockObject|IGroupManager $secureRandom */ + $groupManager = $this->createMock(IGroupManager::class); + + /** @var MockObject|ISecureRandom $secureRandom */ + $secureRandom = $this->createMock(ISecureRandom::class); + + $helper = new Config($config, $secureRandom, $groupManager, $timeFactory); + + $settings = $helper->getTurnSettings(); + $this->assertEquals(0, count($settings)); } public function dataGetWebSocketDomainForSignalingServer() {