From 648936514f982fe6d3e0ed00bb873bc00df17a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Jaussoin?= Date: Mon, 14 Oct 2024 09:03:22 +0000 Subject: [PATCH] Fix FLEXIAPI-213 Add TURN credentials support in the API as defined in... --- CHANGELOG.md | 1 + flexiapi/.env.example | 1 - flexiapi/app/Helpers/Utils.php | 24 +++++++++++++++++++ .../Account/ProvisioningController.php | 18 ++------------ .../Api/Account/AccountController.php | 21 ++++++++++++++++ flexiapi/config/app.php | 1 - .../api/documentation_markdown.blade.php | 5 ++++ flexiapi/routes/api.php | 2 ++ .../tests/Feature/AccountProvisioningTest.php | 24 ++++++++----------- 9 files changed, 65 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f56d6d6..8eccca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ v1.6 - 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 +- Fix FLEXIAPI-213 Add TURN credentials support in the API as defined in draft-uberti-behave-turn-rest-00 v1.5 --- diff --git a/flexiapi/.env.example b/flexiapi/.env.example index 28be017..dbab144 100644 --- a/flexiapi/.env.example +++ b/flexiapi/.env.example @@ -111,7 +111,6 @@ MAIL_SIGNATURE="The Example Team" 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= diff --git a/flexiapi/app/Helpers/Utils.php b/flexiapi/app/Helpers/Utils.php index 640eb6c..95962a8 100644 --- a/flexiapi/app/Helpers/Utils.php +++ b/flexiapi/app/Helpers/Utils.php @@ -98,6 +98,30 @@ function markdownDocumentationView(string $view): string ); } +function hasCoTURNConfigured(): bool +{ + return config('app.coturn_session_ttl_minutes') > 0 + && !empty(config('app.coturn_server_host')) + && !empty(config('app.coturn_static_auth_secret')); +} + +function getCoTURNCredentials(): array +{ + $user = Str::random(8); + $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)); + + return [ + 'username' => $username, + 'password' => $password, + ]; +} + function parseSIP(string $sipAdress): array { return explode('@', \substr($sipAdress, 4)); diff --git a/flexiapi/app/Http/Controllers/Account/ProvisioningController.php b/flexiapi/app/Http/Controllers/Account/ProvisioningController.php index 113adcf..7694fa3 100644 --- a/flexiapi/app/Http/Controllers/Account/ProvisioningController.php +++ b/flexiapi/app/Http/Controllers/Account/ProvisioningController.php @@ -253,16 +253,8 @@ class ProvisioningController extends Controller $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)); + if (hasCoTURNConfigured()) { + list($username, $password) = array_values(getCoTURNCredentials()); // net $section = $xpath->query("//section[@name='net']")->item(0); @@ -317,12 +309,6 @@ class ProvisioningController extends Controller $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) { diff --git a/flexiapi/app/Http/Controllers/Api/Account/AccountController.php b/flexiapi/app/Http/Controllers/Api/Account/AccountController.php index 641595f..a123118 100644 --- a/flexiapi/app/Http/Controllers/Api/Account/AccountController.php +++ b/flexiapi/app/Http/Controllers/Api/Account/AccountController.php @@ -60,6 +60,27 @@ class AccountController extends Controller ]); } + /** + * Get services credentials + */ + public function turnService(Request $request) + { + if (hasCoturnConfigured()) { + list($username, $password) = array_values(getCoTURNCredentials()); + + return [ + 'username' => $username, + 'password' => $password, + 'ttl' => config('app.coturn_session_ttl_minutes') * 60, + 'uris' => [ + 'turn:' . config('app.coturn_server_host'), + ] + ]; + } + + return abort(404, 'No TURN service configured'); + } + /** * /!\ Dangerous endpoint, disabled by default */ diff --git a/flexiapi/config/app.php b/flexiapi/config/app.php index 495bbc7..3521378 100644 --- a/flexiapi/config/app.php +++ b/flexiapi/config/app.php @@ -56,7 +56,6 @@ return [ '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 diff --git a/flexiapi/resources/views/api/documentation_markdown.blade.php b/flexiapi/resources/views/api/documentation_markdown.blade.php index 58b5e7f..f6e01f2 100644 --- a/flexiapi/resources/views/api/documentation_markdown.blade.php +++ b/flexiapi/resources/views/api/documentation_markdown.blade.php @@ -364,6 +364,11 @@ This endpoint is also setting the API Key as a Cookie. Retrieve the account information. +### `GET /accounts/me/services/turn` +User + +If configured, returns valid TURN credentials following the [draft-uberti-behave-turn-rest-00 IEFT Draft](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00). + ### `GET /accounts/me/provision` User diff --git a/flexiapi/routes/api.php b/flexiapi/routes/api.php index 56e2f09..2377428 100644 --- a/flexiapi/routes/api.php +++ b/flexiapi/routes/api.php @@ -68,6 +68,8 @@ Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blo Route::prefix('accounts/me')->group(function () { Route::get('api_key', 'Api\Account\ApiKeyController@generate')->middleware('cookie', 'cookie.encrypt'); + Route::get('services/turn', 'Api\Account\AccountController@turnService'); + Route::get('/', 'Api\Account\AccountController@show'); Route::delete('/', 'Api\Account\AccountController@delete'); Route::get('provision', 'Api\Account\AccountController@provision'); diff --git a/flexiapi/tests/Feature/AccountProvisioningTest.php b/flexiapi/tests/Feature/AccountProvisioningTest.php index feab65a..4a56dd7 100644 --- a/flexiapi/tests/Feature/AccountProvisioningTest.php +++ b/flexiapi/tests/Feature/AccountProvisioningTest.php @@ -351,6 +351,10 @@ class AccountProvisioningTest extends TestCase $host = 'coturn.tld'; $realm = 'realm.tld'; + $this->keyAuthenticated($account) + ->get('/api/accounts/me/services/turn') + ->assertStatus(404); + config()->set('app.coturn_server_host', $host); config()->set('app.coturn_static_auth_secret', 'secret'); @@ -364,21 +368,13 @@ class AccountProvisioningTest extends TestCase ->assertSee($host) ->assertSee('nat_policy_ref') ->assertSee('stun_server_username') - ->assertSee('nat_policy_0') - ->assertDontSee('realm') - ->assertDontSee($realm); + ->assertSee('nat_policy_0'); - config()->set('app.coturn_realm', $realm); - - $response = $this->withHeaders([ - 'x-linphone-provisioning' => true, - ]) - ->keyAuthenticated($account) - ->get($this->accountRoute) + $this->keyAuthenticated($account) + ->get('/api/accounts/me/services/turn') ->assertStatus(200) - ->assertHeader('Content-Type', 'application/xml') - ->assertSee($host) - ->assertSee('realm') - ->assertSee($realm); + ->assertJson([ + 'ttl' => config()->get('app.coturn_session_ttl_minutes') * 60 + ]); } }