Fix FLEXIAPI-212 Add CoTURN credentials support in the provisioning

This commit is contained in:
Timothée Jaussoin 2024-09-19 17:19:39 +02:00
parent 23e61fdc38
commit 12ef6d472e
6 changed files with 134 additions and 11 deletions

View file

@ -7,6 +7,7 @@ v1.6
- Fix FLEXIAPI-203 Implement domain based Linphone configuration, add documentation, complete API endpoints, complete provisioning XML
- Fix FLEXIAPI-208 Add SMS templates documentation
- Fix FLEXIAPI-211 Add a JSON validation middleware + test
- Fix FLEXIAPI-212 Add CoTURN credentials support in the provisioning
v1.5
---

View file

@ -106,6 +106,13 @@ MAIL_VERIFY_PEER=true
MAIL_VERIFY_PEER_NAME=true
MAIL_SIGNATURE="The Example Team"
# CoTURN
COTURN_SERVER_HOST= # IP or domain name
COTURN_SESSION_TTL_MINUTES=1440 # 60 * 24
COTURN_STATIC_AUTH_SECRET= # static-auth-secret in the coturn configuration
COTURN_REALM= # realm in the coturn configuration, empty by default
# OVH SMS API variables
OVH_APP_KEY=
OVH_APP_SECRET=

View file

@ -50,7 +50,9 @@ class ProvisioningController extends Controller
})
->firstOrFail();
if ($account->activationExpired()) abort(404);
if ($account->activationExpired()) {
abort(404);
}
$params = ['provisioning_token' => $provisioningToken];
@ -235,6 +237,7 @@ class ProvisioningController extends Controller
if ($section == null) {
$section = $dom->createElement('section');
$section->setAttribute('name', 'proxy_0');
$config->appendChild($section);
}
$entry = $dom->createElement('entry', $account->fullIdentifier);
@ -246,17 +249,89 @@ class ProvisioningController extends Controller
provisioningProxyHook($section, $request, $account);
}
$config->appendChild($section);
$passwords = $account->passwords()->get();
$authInfoIndex = 0;
// CoTURN
if (config('app.coturn_session_ttl_minutes') > 0
&& !empty(config('app.coturn_server_host'))
&& !empty(config('app.coturn_static_auth_secret'))) {
$user = 'foo';
$secret = config('app.coturn_static_auth_secret');
$ttl = config('app.coturn_session_ttl_minutes') * 60;
$time = time() + $ttl;
$username = $time . ':' . Str::random(16);
$password = base64_encode(hash_hmac('sha1', $username, $secret, true));
// net
$section = $xpath->query("//section[@name='net']")->item(0);
if ($section == null) {
$section = $dom->createElement('section');
$section->setAttribute('name', 'net');
$config->appendChild($section);
}
$ref = Str::random(8);
$entry = $dom->createElement('entry', $ref);
$entry->setAttribute('name', 'nat_policy_ref');
$section->appendChild($entry);
// nat_policy_0
$section = $dom->createElement('section');
$section->setAttribute('name', 'nat_policy_0');
$config->appendChild($section);
$entry = $dom->createElement('entry', $ref);
$entry->setAttribute('name', 'ref');
$section->appendChild($entry);
$entry = $dom->createElement('entry', config('app.coturn_server_host'));
$entry->setAttribute('name', 'stun_server');
$section->appendChild($entry);
$entry = $dom->createElement('entry', $username);
$entry->setAttribute('name', 'stun_server_username');
$section->appendChild($entry);
$entry = $dom->createElement('entry', 'turn,ice');
$entry->setAttribute('name', 'protocols');
$section->appendChild($entry);
// auth_info_x
$section = $xpath->query("//section[@name='auth_info_" . $authInfoIndex . "']")->item(0);
if ($section == null) {
$section = $dom->createElement('section');
$section->setAttribute('name', 'auth_info_' . $authInfoIndex);
$config->appendChild($section);
$authInfoIndex++;
}
$entry = $dom->createElement('entry', $username);
$entry->setAttribute('name', 'username');
$section->appendChild($entry);
$entry = $dom->createElement('entry', $password);
$entry->setAttribute('name', 'passwd');
$section->appendChild($entry);
if (!empty(config('app.coturn_realm'))) {
$entry = $dom->createElement('entry', config('app.coturn_realm'));
$entry->setAttribute('name', 'realm');
$section->appendChild($entry);
}
}
foreach ($passwords as $password) {
$section = $xpath->query("//section[@name='auth_info_" . $authInfoIndex . "']")->item(0);
if ($section == null) {
$section = $dom->createElement('section');
$section->setAttribute('name', 'auth_info_' . $authInfoIndex);
$config->appendChild($section);
}
$entry = $dom->createElement('entry', $account->username);
@ -284,8 +359,6 @@ class ProvisioningController extends Controller
provisioningAuthHook($section, $request, $password);
}
$config->appendChild($section);
$authInfoIndex++;
}
}

View file

@ -50,6 +50,14 @@ return [
*/
'account_creation_token_retry_minutes' => env('APP_API_ACCOUNT_CREATION_TOKEN_RETRY_MINUTES', 60),
/**
* CoTURN authentication in the provisioning
*/
'coturn_server_host' => env('COTURN_SERVER_HOST', null),
'coturn_session_ttl_minutes' => (int)env('COTURN_SESSION_TTL_MINUTES', 60 * 24),
'coturn_static_auth_secret' => env('COTURN_STATIC_AUTH_SECRET', null),
'coturn_realm' => env('COTURN_REALM', null),
/**
* External interfaces
*/

View file

@ -1,5 +0,0 @@
[auth_info_0]
test=foobar
[auth_info_1]
blabla=gnap

View file

@ -95,7 +95,7 @@ class AccountProvisioningTest extends TestCase
])->get($this->accountRoute)->assertStatus(302);
}
public function testAuthenticatedProvisioning()
public function testAuthenticatedWithPasswordProvisioning()
{
$password = Password::factory()->create();
$password->account->generateApiKey();
@ -342,4 +342,43 @@ class AccountProvisioningTest extends TestCase
->get($this->route . '/' . $account->provisioning_token)
->assertStatus(410);
}
public function testCoTURN()
{
$account = Account::factory()->create();
$account->generateApiKey();
$host = 'coturn.tld';
$realm = 'realm.tld';
config()->set('app.coturn_server_host', $host);
config()->set('app.coturn_static_auth_secret', 'secret');
$response = $this->withHeaders([
'x-linphone-provisioning' => true,
])
->keyAuthenticated($account)
->get($this->accountRoute)
->assertStatus(200)
->assertHeader('Content-Type', 'application/xml')
->assertSee($host)
->assertSee('nat_policy_ref')
->assertSee('stun_server_username')
->assertSee('nat_policy_0')
->assertDontSee('realm')
->assertDontSee($realm);
config()->set('app.coturn_realm', $realm);
$response = $this->withHeaders([
'x-linphone-provisioning' => true,
])
->keyAuthenticated($account)
->get($this->accountRoute)
->assertStatus(200)
->assertHeader('Content-Type', 'application/xml')
->assertSee($host)
->assertSee('realm')
->assertSee($realm);
}
}