Fix #94 Implement the deprecated endpoint changes + tests + documentation for 1.4

This commit is contained in:
Timothée Jaussoin 2023-05-03 13:20:26 +00:00
parent b18b500af2
commit 2514de1754
5 changed files with 133 additions and 15 deletions

View file

@ -30,4 +30,16 @@ class Alias extends Model
{
return $this->belongsTo('App\Account');
}
public function scopeSip($query, string $sip)
{
if (\str_contains($sip, '@')) {
list($usernane, $domain) = explode('@', $sip);
return $query->where('alias', $usernane)
->where('domain', $domain);
};
return $query->where('id', '<', 0);
}
}

View file

@ -34,6 +34,7 @@ use App\Alias;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
use App\Libraries\OvhSMS;
use App\Mail\RegisterConfirmation;
use App\Rules\AccountCreationToken as RulesAccountCreationToken;
use App\Rules\BlacklistedUsername;
use App\Rules\IsNotPhoneNumber;
use App\Rules\NoUppercase;
@ -70,11 +71,13 @@ class AccountController extends Controller
$alias = Alias::where('alias', $phone)->first();
$account = $alias
? $alias->account
: Account::sip($phone)->firstOrFail();
// Injecting the default sip domain to try to resolve the account
: Account::sip($phone . '@' . config('app.sip_domain'))->firstOrFail();
return \response()->json([
'activated' => $account->activated,
'realm' => $account->realm
'realm' => $account->realm,
'phone' => (bool)$alias
]);
}
@ -113,6 +116,10 @@ class AccountController extends Controller
'unique:aliases,alias',
'unique:accounts,username',
new WithoutSpaces, 'starts_with:+'
],
'account_creation_token' => [
'required',
new RulesAccountCreationToken
]
]);
@ -178,13 +185,17 @@ class AccountController extends Controller
$request->validate([
'phone' => [
'required', new WithoutSpaces, 'starts_with:+'
],
'account_creation_token' => [
'required',
new RulesAccountCreationToken
]
]);
$alias = Alias::where('alias', $request->get('phone'))->first();
$account = $alias
? $alias->account
: Account::sip($request->get('phone'))->firstOrFail();
: Account::sip($request->get('phone') . '@' . config('app.sip_domain'))->firstOrFail();
$account->confirmation_key = generatePin();
$account->save();
@ -202,9 +213,12 @@ class AccountController extends Controller
{
if (!config('app.dangerous_endpoints')) return abort(404);
$account = Account::sip($sip)
->where('confirmation_key', $recoveryKey)
->firstOrFail();
$alias = Alias::sip($sip)->first();
$account = $alias
? $alias->account
: Account::sip($sip)->firstOrFail();
if ($account->confirmation_key != $recoveryKey) abort(404);
if ($account->activationExpired()) abort(403, 'Activation expired');
@ -241,10 +255,7 @@ class AccountController extends Controller
'dtmf_protocol' => 'nullable|in:' . Account::dtmfProtocolsRule(),
'account_creation_token' => [
'required_without:token',
Rule::exists('account_creation_tokens', 'token')->where(function ($query) {
$query->where('used', false);
}),
'size:' . WebAuthenticateController::$emailCodeSize
new RulesAccountCreationToken
],
'email' => config('app.account_email_unique')
? 'nullable|email|unique:accounts,email'
@ -252,10 +263,7 @@ class AccountController extends Controller
// For retro-compatibility
'token' => [
'required_without:account_creation_token',
Rule::exists('account_creation_tokens', 'token')->where(function ($query) {
$query->where('used', false);
}),
'size:' . WebAuthenticateController::$emailCodeSize
new RulesAccountCreationToken
],
]);

View file

@ -0,0 +1,21 @@
<?php
namespace App\Rules;
use App\AccountCreationToken as AppAccountCreationToken;
use App\Http\Controllers\Account\AuthenticateController;
use Illuminate\Contracts\Validation\Rule;
class AccountCreationToken implements Rule
{
public function passes($attribute, $value)
{
return AppAccountCreationToken::where('token', $value)->where('used', false)->exists()
&& strlen($value) == AuthenticateController::$emailCodeSize;
}
public function message()
{
return 'Please provide a valid account_creation_token';
}
}

View file

@ -134,6 +134,7 @@ JSON parameters:
* `domain` if not set the value is enforced to the default registration domain set in the global configuration
* `email` optional if `phone` set, an email, set an email to the account, must be unique if `ACCOUNT_EMAIL_UNIQUE` is set to `true`
* `phone` required if `username` not set, optional if `email` set, a phone number, set a phone number to the account
* `account_creation_token` the unique `account_creation_token`
### `POST /accounts/with-account-creation-token`
<span class="badge badge-success">Public</span>
@ -162,6 +163,8 @@ Return `404` if the account doesn't exists.
Retrieve public information about the account.
Return `404` if the account doesn't exists.
Return `phone: true` if the returned account has a phone number.
### `POST /accounts/recover-by-phone`
@if(config('app.dangerous_endpoints'))**Enabled on this instance**@else**Not enabled on this instance**@endif
@ -174,6 +177,7 @@ Return `404` if the account doesn't exists.
JSON parameters:
* `phone` required the phone number to send the SMS to
* `account_creation_token` the unique `account_creation_token`
### `GET /accounts/{sip}/recover/{recover_key}`
@if(config('app.dangerous_endpoints'))**Enabled on this instance**@else**Not enabled on this instance**@endif
@ -182,6 +186,9 @@ JSON parameters:
<span class="badge badge-success">Public</span>
<span class="badge badge-warning">Unsecure endpoint</span>
Activate the account if the correct `recover_key` is provided.
The `sip` parameter can be the default SIP account or the phone based one.
Return the account information (including the hashed password) if valid.
Return `404` if the account doesn't exists.

View file

@ -21,6 +21,7 @@ namespace Tests\Feature;
use App\Password;
use App\Account;
use App\AccountCreationToken;
use App\AccountTombstone;
use App\ActivationExpiration;
use App\Admin;
@ -565,8 +566,31 @@ class ApiAccountTest extends TestCase
$this->assertDatabaseHas('accounts', [
'username' => $password->account->username,
'domain' => $password->account->domain,
'confirmation_key' => null,
'activated' => true
]);
// Recover by alias
$newConfirmationKey = '1345';
$password->account->confirmation_key = $newConfirmationKey;
$password->account->save();
$phone = '+1234';
$alias = new AppAlias;
$alias->alias = $phone;
$alias->domain = $password->account->domain;
$alias->account_id = $password->account->id;
$alias->save();
$this->get($this->route . '/' . $phone . '@' . $alias->domain . '/recover/' . $newConfirmationKey)
->assertJson(['passwords' => [[
'password' => $password->password,
'algorithm' => $password->algorithm
]]])
->assertStatus(200);
}
/**
@ -591,6 +615,22 @@ class ApiAccountTest extends TestCase
$this->json($this->method, $this->route . '/recover-by-phone', [
'phone' => $phone
])
->assertStatus(422)
->assertJsonValidationErrors(['account_creation_token']);
$this->json($this->method, $this->route . '/recover-by-phone', [
'phone' => $phone,
'account_creation_token' => 'wrong'
])
->assertStatus(422)
->assertJsonValidationErrors(['account_creation_token']);
$token = AccountCreationToken::factory()->create();
$this->json($this->method, $this->route . '/recover-by-phone', [
'phone' => $phone,
'account_creation_token' => $token->token
])
->assertStatus(200);
@ -605,7 +645,8 @@ class ApiAccountTest extends TestCase
$this->get($this->route . '/' . $phone . '/info-by-phone')
->assertStatus(200)
->assertJson([
'activated' => true
'activated' => true,
'phone' => true
]);
$this->get($this->route . '/+1234/info-by-phone')
@ -614,11 +655,25 @@ class ApiAccountTest extends TestCase
$this->json('GET', $this->route . '/' . $password->account->identifier . '/info-by-phone')
->assertStatus(422)
->assertJsonValidationErrors(['phone']);
// Check the mixed username/phone resolution...
$password->account->username = $phone;
$password->account->save();
$alias->delete();
$this->get($this->route . '/' . $phone . '/info-by-phone')
->assertStatus(200)
->assertJson([
'activated' => true,
'phone' => false
]);
}
/**
* /!\ Dangerous endpoints
*/
public function testCreatePublic()
{
$username = 'publicuser';
@ -639,6 +694,18 @@ class ApiAccountTest extends TestCase
'algorithm' => 'SHA-256',
'password' => '2',
'email' => 'john@doe.tld',
])
->assertStatus(422)
->assertJsonValidationErrors(['account_creation_token']);
$token = AccountCreationToken::factory()->create();
$this->json($this->method, $this->route . '/public', [
'username' => $username,
'algorithm' => 'SHA-256',
'password' => '2',
'email' => 'john@doe.tld',
'account_creation_token' => $token->token
])
->assertStatus(200)
->assertJson([
@ -700,11 +767,14 @@ class ApiAccountTest extends TestCase
->assertStatus(422)
->assertJsonValidationErrors(['phone']);
$token = AccountCreationToken::factory()->create();
$this->json($this->method, $this->route . '/public', [
'phone' => $phone,
'algorithm' => 'SHA-256',
'password' => '2',
'email' => 'john@doe.tld',
'account_creation_token' => $token->token
])
->assertStatus(200)
->assertJson([