mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-01-17 10:08:05 +00:00
Fix #118 Add a throttling system for the AccountCreationToken push notification endpoint
This commit is contained in:
parent
03bd8d8114
commit
1debbc5f10
7 changed files with 89 additions and 18 deletions
|
|
@ -10,6 +10,7 @@ APP_FLEXISIP_PUSHER_PATH=
|
|||
APP_FLEXISIP_PUSHER_FIREBASE_KEY=
|
||||
|
||||
APP_API_KEY_EXPIRATION_MINUTES=60 # Number of minutes the generated API Keys are valid
|
||||
APP_API_ACCOUNT_CREATION_TOKEN_RETRY_MINUTES=60 # Number of minutes between two consecutive account_creation_token creation
|
||||
|
||||
# Risky toggles
|
||||
APP_ADMINS_MANAGE_MULTI_DOMAINS=false # Allow admins to handle all the accounts in the database
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@
|
|||
|
||||
namespace App\Http\Controllers\Api\Account;
|
||||
|
||||
use App\AccountCreationRequestToken;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use App\AccountCreationToken;
|
||||
use App\Libraries\FlexisipPusherConnector;
|
||||
use App\AccountCreationRequestToken;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
use App\Libraries\FlexisipPusherConnector;
|
||||
use App\Rules\AccountCreationRequestToken as RulesAccountCreationRequestToken;
|
||||
|
||||
class CreationTokenController extends Controller
|
||||
|
|
@ -40,6 +41,17 @@ class CreationTokenController extends Controller
|
|||
'pn_prid' => 'required',
|
||||
]);
|
||||
|
||||
$last = AccountCreationToken::where('pn_provider', $request->get('pn_provider'))
|
||||
->where('pn_paparam', $request->get('pn_param'))
|
||||
->where('pn_prid', $request->get('pn_prid'))
|
||||
->where('created_at', '>=', Carbon::now()->subMinutes(config('app.account_creation_token_retry_minutes'))->toDateTimeString())
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if ($last) {
|
||||
abort(429, 'Last token requested too recently');
|
||||
}
|
||||
|
||||
$token = new AccountCreationToken;
|
||||
$token->token = Str::random(WebAuthenticateController::$emailCodeSize);
|
||||
$token->pn_provider = $request->get('pn_provider');
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ return [
|
|||
*/
|
||||
'api_key_expiration_minutes' => env('APP_API_KEY_EXPIRATION_MINUTES', 60),
|
||||
|
||||
/**
|
||||
* Amount of minutes before re-authorizing the generation of a new account creation token
|
||||
*/
|
||||
'account_creation_token_retry_minutes' => env('APP_API_ACCOUNT_CREATION_TOKEN_RETRY_MINUTES', 60),
|
||||
|
||||
/**
|
||||
* External interfaces
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use Illuminate\Support\Str;
|
|||
|
||||
use App\AccountCreationToken;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class AccountCreationTokenFactory extends Factory
|
||||
{
|
||||
|
|
@ -36,7 +37,8 @@ class AccountCreationTokenFactory extends Factory
|
|||
'pn_param' => $this->faker->uuid,
|
||||
'pn_prid' => $this->faker->uuid,
|
||||
'token' => Str::random(WebAuthenticateController::$emailCodeSize),
|
||||
'used' => false
|
||||
'used' => false,
|
||||
'created_at' => Carbon::now()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,20 +43,42 @@ class ApiAccountCreationTokenTest extends TestCase
|
|||
protected $pnParam = 'param';
|
||||
protected $pnPrid = 'id';
|
||||
|
||||
public function testMandatoryParameters()
|
||||
{
|
||||
$response = $this->json($this->method, $this->tokenRoute);
|
||||
$response->assertStatus(422);
|
||||
}
|
||||
|
||||
public function testCorrectParameters()
|
||||
{
|
||||
$response = $this->json($this->method, $this->tokenRoute, [
|
||||
$this->assertSame(AccountCreationToken::count(), 0);
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => $this->pnProvider,
|
||||
'pn_param' => $this->pnParam,
|
||||
'pn_prid' => $this->pnPrid,
|
||||
]);
|
||||
$response->assertStatus(503);
|
||||
])->assertStatus(503);
|
||||
}
|
||||
|
||||
public function testMandatoryParameters()
|
||||
{
|
||||
$this->json($this->method, $this->tokenRoute)->assertStatus(422);
|
||||
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => null,
|
||||
'pn_param' => null,
|
||||
'pn_prid' => null,
|
||||
])->assertStatus(422);
|
||||
}
|
||||
|
||||
public function testExpiration()
|
||||
{
|
||||
$existing = AccountCreationToken::factory()->create();
|
||||
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => $this->pnProvider,
|
||||
'pn_param' => $this->pnParam,
|
||||
'pn_prid' => $this->pnPrid,
|
||||
])->assertStatus(503);
|
||||
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => $existing->pnProvider,
|
||||
'pn_param' => $existing->pnParam,
|
||||
'pn_prid' => $existing->pnPrid,
|
||||
])->assertStatus(422);
|
||||
}
|
||||
|
||||
public function testAdminEndpoint()
|
||||
|
|
|
|||
|
|
@ -540,6 +540,32 @@ class ApiAccountTest extends TestCase
|
|||
->assertJsonValidationErrors(['email']);
|
||||
}
|
||||
|
||||
public function testNonAsciiPasswordAdmin()
|
||||
{
|
||||
$admin = Admin::factory()->create();
|
||||
$admin->account->generateApiKey();
|
||||
$admin->account->save();
|
||||
|
||||
$username = 'username';
|
||||
|
||||
$response = $this->generateFirstResponse($admin->account->passwords()->first(), $this->method, $this->route);
|
||||
$this->generateSecondResponse($admin->account->passwords()->first(), $response)
|
||||
->json($this->method, $this->route, [
|
||||
'username' => $username,
|
||||
'email' => 'email@test.com',
|
||||
'domain' => 'server.com',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => 'nonascii€',
|
||||
])
|
||||
->assertStatus(200);
|
||||
|
||||
$password = Account::where('username', $username)->first()->passwords()->first();
|
||||
|
||||
$response = $this->generateFirstResponse($password, 'GET', '/api/accounts/me');
|
||||
$response = $this->generateSecondResponse($password, $response)
|
||||
->json('GET', '/api/accounts/me');
|
||||
}
|
||||
|
||||
public function testEditAdmin()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
|
|
@ -554,18 +580,18 @@ class ApiAccountTest extends TestCase
|
|||
$password = 'other';
|
||||
|
||||
$this->keyAuthenticated($admin->account)
|
||||
->json('PUT', $this->route. '/1234')
|
||||
->json('PUT', $this->route . '/1234')
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors(['username']);
|
||||
|
||||
$this->keyAuthenticated($admin->account)
|
||||
->json('PUT', $this->route. '/1234', [
|
||||
->json('PUT', $this->route . '/1234', [
|
||||
'username' => 'good'
|
||||
])
|
||||
->assertStatus(422);
|
||||
|
||||
$this->keyAuthenticated($admin->account)
|
||||
->json('PUT', $this->route. '/'. $account->id, [
|
||||
->json('PUT', $this->route . '/' . $account->id, [
|
||||
'username' => $username,
|
||||
'algorithm' => $algorithm,
|
||||
'password' => $password,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ abstract class TestCase extends BaseTestCase
|
|||
|
||||
const ALGORITHMS = ['md5' => 'MD5', 'sha256' => 'SHA-256'];
|
||||
|
||||
protected $route = '/api/accounts/me';
|
||||
protected $method = 'GET';
|
||||
|
||||
protected function keyAuthenticated(Account $account)
|
||||
{
|
||||
return $this->withHeaders([
|
||||
|
|
@ -37,11 +40,11 @@ abstract class TestCase extends BaseTestCase
|
|||
]);
|
||||
}
|
||||
|
||||
protected function generateFirstResponse(Password $password)
|
||||
protected function generateFirstResponse(Password $password, ?string $method = null, ?string $route = null)
|
||||
{
|
||||
return $this->withHeaders([
|
||||
'From' => 'sip:'.$password->account->identifier
|
||||
])->json($this->method, $this->route);
|
||||
])->json($method ?? $this->method, $route ?? $this->route);
|
||||
}
|
||||
|
||||
protected function generateSecondResponse(Password $password, $firstResponse)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue