mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-01-17 10:08:05 +00:00
Resolve properly the domain/realm when hashing the password
Add aliases support through two new endpoints, allowing user to set a phone number on his account Hide the confirmation_key from the returned account JSON Bump version number
This commit is contained in:
parent
bc3d1d1f38
commit
4fc6aaa824
20 changed files with 321 additions and 35 deletions
|
|
@ -39,9 +39,10 @@ class Account extends Authenticatable
|
|||
use HasFactory;
|
||||
|
||||
protected $connection = 'external';
|
||||
protected $with = ['passwords', 'admin', 'emailChanged'];
|
||||
protected $with = ['passwords', 'admin', 'emailChanged', 'alias'];
|
||||
protected $hidden = ['alias', 'expire_time', 'confirmation_key'];
|
||||
protected $dateTimes = ['creation_time'];
|
||||
protected $appends = ['realm'];
|
||||
protected $appends = ['realm', 'phone'];
|
||||
protected $casts = [
|
||||
'activated' => 'boolean',
|
||||
];
|
||||
|
|
@ -72,6 +73,11 @@ class Account extends Authenticatable
|
|||
return $query->where('id', '<', 0);
|
||||
}
|
||||
|
||||
public function phoneChangeCode()
|
||||
{
|
||||
return $this->hasOne('App\PhoneChangeCode');
|
||||
}
|
||||
|
||||
public function passwords()
|
||||
{
|
||||
return $this->hasMany('App\Password');
|
||||
|
|
@ -112,6 +118,20 @@ class Account extends Authenticatable
|
|||
return config('app.realm');
|
||||
}
|
||||
|
||||
public function getResolvedRealmAttribute()
|
||||
{
|
||||
return config('app.realm') ?? $this->domain;
|
||||
}
|
||||
|
||||
public function getPhoneAttribute()
|
||||
{
|
||||
if ($this->alias) {
|
||||
return $this->alias->alias;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function requestEmailUpdate(string $newEmail)
|
||||
{
|
||||
// Remove all the old requests
|
||||
|
|
@ -150,7 +170,7 @@ class Account extends Authenticatable
|
|||
|
||||
$password = new Password;
|
||||
$password->account_id = $this->id;
|
||||
$password->password = Utils::bchash($this->username, $this->domain, $newPassword, $algorithm);
|
||||
$password->password = Utils::bchash($this->username, $this->resolvedRealm, $newPassword, $algorithm);
|
||||
$password->algorithm = $algorithm;
|
||||
$password->save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ use App\Http\Controllers\Controller;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
use App\Account;
|
||||
use App\Password;
|
||||
use App\Helpers\Utils;
|
||||
use App\Mail\ConfirmedRegistration;
|
||||
|
||||
|
|
@ -56,7 +54,7 @@ class PasswordController extends Controller
|
|||
// If one of the password stored equals the one entered
|
||||
if (hash_equals(
|
||||
$password->password,
|
||||
Utils::bchash($account->username, $account->domain, $request->get('old_password'), $password->algorithm)
|
||||
Utils::bchash($account->username, $account->resolvedRealm, $request->get('old_password'), $password->algorithm)
|
||||
)) {
|
||||
$account->updatePassword($request->get('password'), $algorithm);
|
||||
$request->session()->flash('success', 'Password successfully changed');
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ use Carbon\Carbon;
|
|||
|
||||
use App\Account;
|
||||
use App\Alias;
|
||||
use App\Rules\SIP;
|
||||
use App\Rules\WithoutSpaces;
|
||||
use App\Helpers\Utils;
|
||||
use App\Libraries\OvhSMS;
|
||||
|
|
|
|||
84
flexiapi/app/Http/Controllers/Api/AccountPhoneController.php
Normal file
84
flexiapi/app/Http/Controllers/Api/AccountPhoneController.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use App\Rules\WithoutSpaces;
|
||||
use App\Helpers\Utils;
|
||||
use App\Libraries\OvhSMS;
|
||||
|
||||
use App\PhoneChangeCode;
|
||||
use App\Alias;
|
||||
|
||||
class AccountPhoneController extends Controller
|
||||
{
|
||||
public function requestUpdate(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'phone' => [
|
||||
'required', 'unique:external.aliases,alias',
|
||||
'unique:external.accounts,username',
|
||||
new WithoutSpaces, 'starts_with:+', 'phone:AUTO'
|
||||
]
|
||||
]);
|
||||
|
||||
$account = $request->user();
|
||||
|
||||
$phoneChangeCode = $account->phoneChangeCode ?? new PhoneChangeCode;
|
||||
$phoneChangeCode->account_id = $account->id;
|
||||
$phoneChangeCode->phone = $request->get('phone');
|
||||
$phoneChangeCode->code = Utils::generatePin();
|
||||
$phoneChangeCode->save();
|
||||
|
||||
$ovhSMS = new OvhSMS;
|
||||
$ovhSMS->send($request->get('phone'), 'Your ' . config('app.name') . ' validation code is ' . $phoneChangeCode->code);
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|digits:4'
|
||||
]);
|
||||
|
||||
$account = $request->user();
|
||||
|
||||
$phoneChangeCode = $account->phoneChangeCode()->firstOrFail();
|
||||
if ($phoneChangeCode->code == $request->get('code')) {
|
||||
$account->alias()->delete();
|
||||
|
||||
$alias = new Alias;
|
||||
$alias->alias = $phoneChangeCode->phone;
|
||||
$alias->domain = config('app.sip_domain');
|
||||
$alias->account_id = $account->id;
|
||||
$alias->save();
|
||||
|
||||
$phoneChangeCode->delete();
|
||||
|
||||
$account->refresh();
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
$phoneChangeCode->delete();
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ class AccountController extends Controller
|
|||
|
||||
$password = new Password;
|
||||
$password->account_id = $account->id;
|
||||
$password->password = Utils::bchash($account->username, $account->domain, $request->get('password'), $request->get('algorithm'));
|
||||
$password->password = Utils::bchash($account->username, $account->resolvedRealm, $request->get('password'), $request->get('algorithm'));
|
||||
$password->algorithm = $request->get('algorithm');
|
||||
$password->save();
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class PasswordController extends Controller
|
|||
foreach ($account->passwords as $password) {
|
||||
if (hash_equals(
|
||||
$password->password,
|
||||
Utils::bchash($account->username, $account->domain, $request->get('old_password'), $password->algorithm)
|
||||
Utils::bchash($account->username, $account->resolvedRealm, $request->get('old_password'), $password->algorithm)
|
||||
)) {
|
||||
$account->updatePassword($request->get('password'), $algorithm);
|
||||
return response()->json();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace App\Http\Middleware;
|
|||
use App\Account;
|
||||
use App\Helpers\Utils;
|
||||
|
||||
use Fabiang\Sasl\Sasl;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Response;
|
||||
|
|
@ -45,7 +44,7 @@ class AuthenticateDigestOrKey
|
|||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$validator = Validator::make(['from' => $request->header('From')], [
|
||||
Validator::make(['from' => $request->header('From')], [
|
||||
'from' => 'required',
|
||||
])->validate();
|
||||
|
||||
|
|
@ -95,7 +94,7 @@ class AuthenticateDigestOrKey
|
|||
$storedNonce->save();
|
||||
|
||||
// Validation
|
||||
$validator = Validator::make($auth, [
|
||||
Validator::make($auth, [
|
||||
'opaque' => 'required|in:'.$this->getOpaque(),
|
||||
//'uri' => 'in:/'.$request->path(),
|
||||
'qop' => 'required|in:auth',
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
namespace App\Libraries;
|
||||
|
||||
use App\Device;
|
||||
use Ovh\Api;
|
||||
|
||||
class OvhSMS
|
||||
|
|
@ -55,9 +54,9 @@ class OvhSMS
|
|||
'validityPeriod' => 2880
|
||||
];
|
||||
|
||||
$resultPostJob = $this->_api->post('/sms/'. $this->_smsService . '/jobs', $content);
|
||||
$this->_api->post('/sms/'. $this->_smsService . '/jobs', $content);
|
||||
// One credit removed
|
||||
|
||||
$smsJobs = $this->_api->get('/sms/'. $this->_smsService . '/jobs');
|
||||
$this->_api->get('/sms/'. $this->_smsService . '/jobs');
|
||||
}
|
||||
}
|
||||
18
flexiapi/app/PhoneChangeCode.php
Normal file
18
flexiapi/app/PhoneChangeCode.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PhoneChangeCode extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $connection = 'local';
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo('App\Account');
|
||||
}
|
||||
}
|
||||
BIN
flexiapi/composer.phar
Executable file
BIN
flexiapi/composer.phar
Executable file
Binary file not shown.
|
|
@ -21,7 +21,6 @@ namespace Database\Factories;
|
|||
|
||||
use App\Account;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class AccountFactory extends Factory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,10 +21,7 @@ namespace Database\Factories;
|
|||
|
||||
use App\Account;
|
||||
use App\Password;
|
||||
use App\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PasswordFactory extends Factory
|
||||
{
|
||||
|
|
|
|||
42
flexiapi/database/factories/PhoneChangeCodeFactory.php
Normal file
42
flexiapi/database/factories/PhoneChangeCodeFactory.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Helpers\Utils;
|
||||
use App\Password;
|
||||
use App\PhoneChangeCode;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class PhoneChangeCodeFactory extends Factory
|
||||
{
|
||||
protected $model = PhoneChangeCode::class;
|
||||
|
||||
public function definition()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateApiKey();
|
||||
|
||||
return [
|
||||
'account_id' => $password->account->id,
|
||||
'code' => Utils::generatePin(),
|
||||
'phone' => '+3312341234',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddPhoneChangeCodesTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::connection('local')->create('phone_change_codes', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->integer('account_id')->unsigned();
|
||||
$table->string('code');
|
||||
$table->string('phone');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::connection('local')->dropIfExists('phone_change_codes');
|
||||
}
|
||||
}
|
||||
|
|
@ -100,6 +100,24 @@ For the moment only DIGEST-MD5 and DIGEST-SHA-256 are supported through the auth
|
|||
<li><code>password</code> required, the new password</li>
|
||||
</ul>
|
||||
|
||||
<h4>Phone number</h4>
|
||||
|
||||
<h4><code>POST /accounts/me/phone/request</code></h4>
|
||||
<p>Request a specific code by SMS</p>
|
||||
<p>JSON parameters:</p>
|
||||
<ul>
|
||||
<li><code>phone</code> the phone number to send the SMS</li>
|
||||
</ul>
|
||||
|
||||
<h4><code>POST /accounts/me/phone</code></h4>
|
||||
<p>Confirm the code received and change the phone number</p>
|
||||
<p>JSON parameters:</p>
|
||||
<ul>
|
||||
<li><code>code</code> the received SMS code</li>
|
||||
</ul>
|
||||
|
||||
<p>Return the updated account</p>
|
||||
|
||||
<h4>Devices</h4>
|
||||
|
||||
<h4><code>GET /accounts/me/devices</code></h4>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ Route::group(['middleware' => ['auth.digest_or_key']], function () {
|
|||
Route::get('accounts/me', 'Api\AccountController@show');
|
||||
Route::delete('accounts/me', 'Api\AccountController@delete');
|
||||
|
||||
Route::post('accounts/me/phone/request', 'Api\AccountPhoneController@requestUpdate');
|
||||
Route::post('accounts/me/phone', 'Api\AccountPhoneController@update');
|
||||
|
||||
Route::get('accounts/me/devices', 'Api\DeviceController@index');
|
||||
Route::delete('accounts/me/devices/{uuid}', 'Api\DeviceController@destroy');
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,9 @@ namespace Tests\Feature;
|
|||
use App\Password;
|
||||
use App\Account;
|
||||
use App\Admin;
|
||||
use App\User;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AccountApiTest extends TestCase
|
||||
{
|
||||
|
|
@ -38,7 +35,7 @@ class AccountApiTest extends TestCase
|
|||
|
||||
public function testMandatoryFrom()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
Password::factory()->create();
|
||||
$response = $this->json($this->method, $this->route);
|
||||
$response->assertStatus(422);
|
||||
}
|
||||
|
|
@ -109,8 +106,6 @@ class AccountApiTest extends TestCase
|
|||
'domain' => $domain,
|
||||
'activated' => false
|
||||
]);
|
||||
|
||||
$this->assertFalse(empty($response1['confirmation_key']));
|
||||
}
|
||||
|
||||
public function testUsernameNoDomain()
|
||||
|
|
@ -179,8 +174,6 @@ class AccountApiTest extends TestCase
|
|||
'domain' => config('app.sip_domain'),
|
||||
'activated' => true,
|
||||
]);
|
||||
|
||||
$this->assertTrue(empty($response1['confirmation_key']));
|
||||
}
|
||||
|
||||
public function testNotActivated()
|
||||
|
|
@ -208,8 +201,6 @@ class AccountApiTest extends TestCase
|
|||
'domain' => config('app.sip_domain'),
|
||||
'activated' => false,
|
||||
]);
|
||||
|
||||
$this->assertFalse(empty($response1['confirmation_key']));
|
||||
}
|
||||
|
||||
public function testSimpleAccount()
|
||||
|
|
@ -424,7 +415,7 @@ class AccountApiTest extends TestCase
|
|||
]);
|
||||
|
||||
// Set the new password with incorrect old password
|
||||
$response = $this->keyAuthenticated($account)
|
||||
$this->keyAuthenticated($account)
|
||||
->json($this->method, $this->route.'/me/password', [
|
||||
'algorithm' => $newAlgorithm,
|
||||
'old_password' => 'blabla',
|
||||
|
|
@ -513,7 +504,8 @@ class AccountApiTest extends TestCase
|
|||
->get($this->route.'/'.$admin->id)
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'id' => 1
|
||||
'id' => 1,
|
||||
'phone' => null
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
95
flexiapi/tests/Feature/AccountPhoneChangeTest.php
Normal file
95
flexiapi/tests/Feature/AccountPhoneChangeTest.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Password;
|
||||
use App\Account;
|
||||
use App\Admin;
|
||||
use App\PhoneChangeCode;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AccountPhoneChangeTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected $route = '/api/accounts/me/phone';
|
||||
protected $method = 'POST';
|
||||
|
||||
public function testRequest()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateApiKey();
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $this->route.'/request', [
|
||||
'phone' => 'blabla'
|
||||
])
|
||||
->assertStatus(422);
|
||||
|
||||
// Send a SMS
|
||||
/*$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $this->route.'/request', [
|
||||
'phone' => '+33667545663'
|
||||
])
|
||||
->assertStatus(200);*/
|
||||
}
|
||||
|
||||
public function testConfirmLongCode()
|
||||
{
|
||||
$phoneChange = PhoneChangeCode::factory()->create();
|
||||
|
||||
$this->keyAuthenticated($phoneChange->account)
|
||||
->json($this->method, $this->route, [
|
||||
'code' => 'wrong'
|
||||
])
|
||||
->assertStatus(422);
|
||||
}
|
||||
|
||||
public function testConfirmGoodCode()
|
||||
{
|
||||
$phoneChange = PhoneChangeCode::factory()->create();
|
||||
$phone = $phoneChange->phone;
|
||||
|
||||
$this->keyAuthenticated($phoneChange->account)
|
||||
->get('/api/accounts/me')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'phone' => null
|
||||
]);
|
||||
|
||||
$this->keyAuthenticated($phoneChange->account)
|
||||
->json($this->method, $this->route, [
|
||||
'code' => $phoneChange->code
|
||||
])
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'phone' => $phone,
|
||||
]);
|
||||
|
||||
$this->keyAuthenticated($phoneChange->account)
|
||||
->get('/api/accounts/me')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'phone' => $phone
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ namespace Tests\Feature;
|
|||
use App\Password;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AuthenticateDigestAndKeyTest extends TestCase
|
||||
|
|
@ -34,14 +33,14 @@ class AuthenticateDigestAndKeyTest extends TestCase
|
|||
|
||||
public function testMandatoryFrom()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
Password::factory()->create();
|
||||
$response = $this->json($this->method, $this->route);
|
||||
$response->assertStatus(422);
|
||||
}
|
||||
|
||||
public function testWrongFrom()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
Password::factory()->create();
|
||||
$response = $this->withHeaders([
|
||||
'From' => 'sip:missing@username',
|
||||
])->json($this->method, $this->route);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#%define _datadir %{_datarootdir}
|
||||
#%define _docdir %{_datadir}/doc
|
||||
|
||||
%define build_number 48
|
||||
%define build_number 49
|
||||
%define var_dir /var/opt/belledonne-communications
|
||||
%define opt_dir /opt/belledonne-communications/share/flexisip-account-manager
|
||||
%define env_file "$RPM_BUILD_ROOT/etc/flexisip-account-manager/flexiapi.env"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue