Fix FLEXIAPI-213 Add TURN credentials support in the API as defined in...

This commit is contained in:
Timothée Jaussoin 2024-10-14 09:03:22 +00:00
parent 12ef6d472e
commit 648936514f
9 changed files with 65 additions and 32 deletions

View file

@ -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
---

View file

@ -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=

View file

@ -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));

View file

@ -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) {

View file

@ -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
*/

View file

@ -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

View file

@ -364,6 +364,11 @@ This endpoint is also setting the API Key as a Cookie.
Retrieve the account information.
### `GET /accounts/me/services/turn`
<span class="badge badge-info">User</span>
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`
<span class="badge badge-info">User</span>

View file

@ -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');

View file

@ -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
]);
}
}