mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-01-17 10:08:05 +00:00
Fix FLEXIAPI-205 Remove the deprecated endpoints, compatibility code...
This commit is contained in:
parent
a3861304cc
commit
e2f40699fb
23 changed files with 47 additions and 993 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
v1.7
|
||||
----
|
||||
- Fix FLEXIAPI-205 Remove the deprecated endpoints, compatibility code documentation and tests. Drop the confirmation_key accounts column and activation_expirations table
|
||||
- Fix FLEXIAPI-206 Upgrade to Laravel 10, PHP 8.1 minimum and bump all the related dependencies, drop Debian 11 Bullseye
|
||||
- Fix FLEXIAPI-220 Migrate SIP Domains to Spaces
|
||||
- Fix GH-15 Add password import from CSV
|
||||
|
|
|
|||
|
|
@ -104,9 +104,6 @@ You can also seed the tables with test accounts for the liblinphone test suite w
|
|||
To send SMS to the USA some providers need to validate their templates before transfering them, see [Sending SMS messages to the USA - OVH](https://help.ovhcloud.com/csm/en-ie-sms-sending-sms-to-usa?id=kb_article_view&sysparm_article=KB0051359).
|
||||
|
||||
Here are the currently used SMS templates in the app to declare in your provider panel:
|
||||
|
||||
- Creation code: `Your #APP_NAME# creation code is #CODE#`. Sent to confirm the creation of the account by SMS.
|
||||
- Recovery code: `Your #APP_NAME# recovery code is #CODE#`. Sent to recover the account by SMS.
|
||||
- Validation code: `Your #APP_NAME# validation code is #CODE#`. Sent to validate the phone change by SMS.
|
||||
- Validation code with expiration: `Your #APP_NAME# validation code is #CODE#. The code is available for #CODE_MINUTES# minutes`. Sent to validate the phone change by SMS, include an expiration time.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ class Account extends Authenticatable
|
|||
use HasFactory;
|
||||
use Compoships;
|
||||
|
||||
protected $with = ['passwords', 'activationExpiration', 'emailChangeCode', 'types', 'actions', 'dictionaryEntries'];
|
||||
protected $hidden = ['expire_time', 'confirmation_key', 'pivot', 'currentProvisioningToken', 'currentRecoveryCode', 'dictionaryEntries'];
|
||||
protected $appends = ['realm', 'confirmation_key_expires', 'provisioning_token', 'provisioning_token_expire_at', 'dictionary'];
|
||||
protected $with = ['passwords', 'emailChangeCode', 'types', 'actions', 'dictionaryEntries'];
|
||||
protected $hidden = ['expire_time', 'pivot', 'currentProvisioningToken', 'currentRecoveryCode', 'dictionaryEntries'];
|
||||
protected $appends = ['realm', 'provisioning_token', 'provisioning_token_expire_at', 'dictionary'];
|
||||
protected $casts = [
|
||||
'activated' => 'boolean',
|
||||
];
|
||||
|
|
@ -111,11 +111,6 @@ class Account extends Authenticatable
|
|||
});
|
||||
}
|
||||
|
||||
public function activationExpiration()
|
||||
{
|
||||
return $this->hasOne(ActivationExpiration::class);
|
||||
}
|
||||
|
||||
public function apiKey()
|
||||
{
|
||||
return $this->hasOne(ApiKey::class)->whereNull('expires_after_last_used_minutes');
|
||||
|
|
@ -352,10 +347,6 @@ class Account extends Authenticatable
|
|||
/**
|
||||
* Utils
|
||||
*/
|
||||
public function activationExpired(): bool
|
||||
{
|
||||
return ($this->activationExpiration && $this->activationExpiration->isExpired());
|
||||
}
|
||||
|
||||
public function generateUserApiKey(?string $ip = null): ApiKey
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
<?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;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ActivationExpiration extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'expires' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
public function isExpired()
|
||||
{
|
||||
$now = Carbon::now();
|
||||
return $this->expires->lessThan($now);
|
||||
}
|
||||
}
|
||||
|
|
@ -80,29 +80,6 @@ class AuthenticateController extends Controller
|
|||
return redirect()->back()->withErrors(['authentication' => __('Wrong username or password')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
*/
|
||||
public function validateEmail(Request $request, string $code)
|
||||
{
|
||||
$request->merge(['code' => $code]);
|
||||
$request->validate(['code' => 'required|size:' . self::$emailCodeSize]);
|
||||
|
||||
$account = Account::where('confirmation_key', $code)->first();
|
||||
|
||||
if (!$account) {
|
||||
return redirect()->route('account.login');
|
||||
}
|
||||
|
||||
$account->confirmation_key = null;
|
||||
$account->activated = true;
|
||||
$account->save();
|
||||
|
||||
Auth::login($account);
|
||||
|
||||
return redirect()->route('account.home');
|
||||
}
|
||||
|
||||
public function loginAuthToken(Request $request, ?string $token = null)
|
||||
{
|
||||
$authToken = null;
|
||||
|
|
|
|||
|
|
@ -51,10 +51,6 @@ class ProvisioningController extends Controller
|
|||
})
|
||||
->firstOrFail();
|
||||
|
||||
if ($account->activationExpired()) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$params = ['provisioning_token' => $provisioningToken];
|
||||
|
||||
if ($request->has('reset_password')) {
|
||||
|
|
@ -130,7 +126,7 @@ class ProvisioningController extends Controller
|
|||
})
|
||||
->firstOrFail();
|
||||
|
||||
if ($account->activationExpired() || ($provisioningToken != $account->provisioning_token)) {
|
||||
if ($provisioningToken != $account->provisioning_token) {
|
||||
return abort(404);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,29 +20,12 @@
|
|||
namespace App\Http\Controllers\Api\Account;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use App\Http\Requests\Account\Create\Api\Request as ApiRequest;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountCreationToken;
|
||||
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
use App\Http\Requests\Account\Create\Api\Request as ApiRequest;
|
||||
use App\Libraries\OvhSMS;
|
||||
use App\Mail\RegisterConfirmation;
|
||||
|
||||
use App\Rules\AccountCreationToken as RulesAccountCreationToken;
|
||||
use App\Rules\AccountCreationTokenNotExpired;
|
||||
use App\Rules\BlacklistedUsername;
|
||||
use App\Rules\FilteredPhone;
|
||||
use App\Rules\NoUppercase;
|
||||
use App\Rules\SIPUsername;
|
||||
use App\Rules\PasswordAlgorithm;
|
||||
|
||||
use App\Services\AccountService;
|
||||
|
||||
class AccountController extends Controller
|
||||
|
|
@ -81,256 +64,11 @@ class AccountController extends Controller
|
|||
return abort(404, 'No TURN service configured');
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoint, disabled by default
|
||||
*/
|
||||
public function phoneInfo(Request $request, string $phone)
|
||||
{
|
||||
if (!config('app.dangerous_endpoints')) return abort(404);
|
||||
|
||||
$request->merge(['phone' => $phone]);
|
||||
$request->validate([
|
||||
'phone' => ['required', 'phone', new FilteredPhone]
|
||||
]);
|
||||
|
||||
$account = Account::where('domain', config('app.sip_domain'))
|
||||
->where(function ($query) use ($phone) {
|
||||
$query->where('username', $phone)
|
||||
->orWhere('phone', $phone);
|
||||
})->firstOrFail();
|
||||
|
||||
return \response()->json([
|
||||
'activated' => $account->activated,
|
||||
'realm' => $account->realm,
|
||||
'phone' => (bool)$account->phone
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoint, disabled by default
|
||||
* Store directly the account and alias in the DB and send a SMS or email for the validation
|
||||
*/
|
||||
public function storePublic(Request $request)
|
||||
{
|
||||
if (!config('app.dangerous_endpoints')) return abort(404);
|
||||
|
||||
$request->validate([
|
||||
'username' => [
|
||||
'required_without:phone',
|
||||
new NoUppercase,
|
||||
new BlacklistedUsername,
|
||||
new SIPUsername,
|
||||
Rule::unique('accounts', 'username')->where(function ($query) use ($request) {
|
||||
$query->where('domain', $request->has('domain') ? $request->get('domain') : config('app.sip_domain'));
|
||||
}),
|
||||
Rule::unique('accounts_tombstones', 'username')->where(function ($query) use ($request) {
|
||||
$query->where('domain', $request->has('domain') ? $request->get('domain') : config('app.sip_domain'));
|
||||
}),
|
||||
'filled',
|
||||
],
|
||||
'algorithm' => ['required', new PasswordAlgorithm],
|
||||
'password' => 'required|filled',
|
||||
'domain' => 'min:3',
|
||||
'email' => config('app.account_email_unique')
|
||||
? 'required_without:phone|email|unique:accounts,email'
|
||||
: 'required_without:phone|email',
|
||||
'phone' => [
|
||||
'required_without:email',
|
||||
'required_without:username',
|
||||
'phone',
|
||||
new FilteredPhone,
|
||||
'unique:accounts,phone',
|
||||
'unique:accounts,username',
|
||||
],
|
||||
'account_creation_token' => [
|
||||
'required',
|
||||
new RulesAccountCreationToken,
|
||||
new AccountCreationTokenNotExpired
|
||||
]
|
||||
]);
|
||||
|
||||
$account = new Account;
|
||||
$account->username = !empty($request->get('username'))
|
||||
? $request->get('username')
|
||||
: $request->get('phone');
|
||||
$account->email = $request->get('email');
|
||||
$account->activated = false;
|
||||
$account->domain = $request->has('domain')
|
||||
? $request->get('domain')
|
||||
: config('app.sip_domain');
|
||||
$account->ip_address = $request->ip();
|
||||
$account->created_at = Carbon::now();
|
||||
$account->user_agent = $request->header('User-Agent') ?? space()->name;
|
||||
$account->save();
|
||||
|
||||
$account->updatePassword($request->get('password'), $request->get('algorithm'));
|
||||
|
||||
$token = AccountCreationToken::where('token', $request->get('account_creation_token'))->first();
|
||||
$token->consume();
|
||||
$token->account_id = $account->id;
|
||||
$token->save();
|
||||
|
||||
Log::channel('events')->info('API deprecated - Store public: AccountCreationToken redeemed', ['account_creation_token' => $token->toLog()]);
|
||||
Log::channel('events')->info('API deprecated - Store public: Account created', ['id' => $account->identifier]);
|
||||
|
||||
// Send validation by phone
|
||||
if ($request->has('phone')) {
|
||||
$account->phone = $request->get('phone');
|
||||
$account->confirmation_key = generatePin();
|
||||
$account->save();
|
||||
|
||||
Log::channel('events')->info('API deprecated: Account created using the public endpoint by phone', ['id' => $account->identifier]);
|
||||
|
||||
$ovhSMS = new OvhSMS;
|
||||
$ovhSMS->send($request->get('phone'), 'Your ' . space()->name . ' creation code is ' . $account->confirmation_key);
|
||||
} elseif ($request->has('email')) {
|
||||
// Send validation by email
|
||||
$account->confirmation_key = Str::random(WebAuthenticateController::$emailCodeSize);
|
||||
$account->save();
|
||||
|
||||
Log::channel('events')->info('API deprecated - Store public: Account created using the public endpoint by email', ['id' => $account->identifier]);
|
||||
|
||||
try {
|
||||
Mail::to($account)->send(new RegisterConfirmation($account));
|
||||
} catch (\Exception $e) {
|
||||
Log::channel('events')->info('API deprecated - Store public: Public Register Confirmation email not sent, check errors log', ['id' => $account->identifier]);
|
||||
Log::error('Public Register Confirmation email not sent: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Full reload
|
||||
return Account::withoutGlobalScopes()->find($account->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoint, disabled by default
|
||||
*/
|
||||
public function recoverByPhone(Request $request)
|
||||
{
|
||||
if (!config('app.dangerous_endpoints')) return abort(404);
|
||||
|
||||
$request->validate([
|
||||
'phone' => [
|
||||
'required', 'phone', new FilteredPhone, 'exists:accounts,phone'
|
||||
],
|
||||
'account_creation_token' => [
|
||||
'required',
|
||||
new RulesAccountCreationToken,
|
||||
new AccountCreationTokenNotExpired
|
||||
]
|
||||
]);
|
||||
|
||||
$account = Account::where('phone', $request->get('phone'))->first();
|
||||
$account->confirmation_key = generatePin();
|
||||
$account->save();
|
||||
|
||||
$token = AccountCreationToken::where('token', $request->get('account_creation_token'))->first();
|
||||
$token->consume();
|
||||
$token->account_id = $account->id;
|
||||
$token->save();
|
||||
|
||||
Log::channel('events')->info('API deprecated - Account recovery: AccountCreationToken redeemed', ['account_creation_token' => $token->toLog()]);
|
||||
Log::channel('events')->info('API deprecated - Account recovery: Account recovery by phone', ['id' => $account->identifier]);
|
||||
|
||||
$ovhSMS = new OvhSMS;
|
||||
$ovhSMS->send($request->get('phone'), 'Your ' . space()->name . ' recovery code is ' . $account->confirmation_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoint, disabled by default
|
||||
*/
|
||||
public function recoverUsingKey(string $sip, string $recoveryKey)
|
||||
{
|
||||
if (!config('app.dangerous_endpoints')) return abort(404);
|
||||
|
||||
list($username, $domain) = explode('@', $sip);
|
||||
|
||||
$account = Account::where('domain', $domain)
|
||||
->where(function ($query) use ($username) {
|
||||
$query->where('username', $username)
|
||||
->orWhere('phone', $username);
|
||||
})->firstOrFail();
|
||||
|
||||
$confirmationKey = $account->confirmation_key;
|
||||
$account->confirmation_key = null;
|
||||
|
||||
if ($confirmationKey != $recoveryKey) abort(404);
|
||||
|
||||
if ($account->activationExpired()) abort(403, 'Activation expired');
|
||||
|
||||
$account->activated = true;
|
||||
$account->save();
|
||||
|
||||
$account->passwords->each(function ($i, $k) {
|
||||
$i->makeVisible(['password']);
|
||||
});
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
public function store(ApiRequest $request)
|
||||
{
|
||||
return (new AccountService)->store($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
*/
|
||||
public function activateEmail(Request $request, string $sip)
|
||||
{
|
||||
// For retro-compatibility
|
||||
if ($request->has('code')) {
|
||||
$request->merge(['confirmation_key' => $request->get('code')]);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'confirmation_key' => 'required|size:' . WebAuthenticateController::$emailCodeSize
|
||||
]);
|
||||
|
||||
$account = Account::sip($sip)
|
||||
->where('confirmation_key', $request->get('confirmation_key'))
|
||||
->firstOrFail();
|
||||
|
||||
if ($account->activationExpired()) abort(403, 'Activation expired');
|
||||
|
||||
$account->activated = true;
|
||||
$account->confirmation_key = null;
|
||||
$account->save();
|
||||
|
||||
Log::channel('events')->info('API: Account activated by email', ['id' => $account->identifier]);
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
*/
|
||||
public function activatePhone(Request $request, string $sip)
|
||||
{
|
||||
// For retro-compatibility
|
||||
if ($request->has('code')) {
|
||||
$request->merge(['confirmation_key' => $request->get('code')]);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'confirmation_key' => 'required|digits:4'
|
||||
]);
|
||||
|
||||
$account = Account::sip($sip)
|
||||
->where('confirmation_key', $request->get('confirmation_key'))
|
||||
->firstOrFail();
|
||||
|
||||
if ($account->activationExpired()) abort(403, 'Activation expired');
|
||||
|
||||
$account->activated = true;
|
||||
$account->confirmation_key = null;
|
||||
$account->save();
|
||||
|
||||
Log::channel('events')->info('API: Account activated by phone', ['id' => $account->identifier]);
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
return Account::where('id', $request->user()->id)
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class AccountController extends Controller
|
|||
|
||||
Log::channel('events')->info('API Admin: Account updated', ['id' => $account->identifier]);
|
||||
|
||||
return $account->makeVisible(['confirmation_key', 'provisioning_token']);
|
||||
return $account->makeVisible(['provisioning_token']);
|
||||
}
|
||||
|
||||
public function typeAdd(int $accountId, int $typeId)
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@ class AsAdminRequest extends Request
|
|||
$rules['algorithm'] = ['required', new PasswordAlgorithm()];
|
||||
$rules['admin'] = 'boolean|nullable';
|
||||
$rules['activated'] = 'boolean|nullable';
|
||||
$rules['confirmation_key_expires'] = [
|
||||
'date_format:Y-m-d H:i:s',
|
||||
'nullable',
|
||||
];
|
||||
|
||||
if (config('app.allow_phone_number_username_admin_api') == true) {
|
||||
array_splice(
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
<?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\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use App\Account;
|
||||
|
||||
class RegisterConfirmation extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
private $account;
|
||||
|
||||
public function __construct(Account $account)
|
||||
{
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
public function build()
|
||||
{
|
||||
return $this->view('mails.register_confirmation')
|
||||
->text('mails.register_confirmation_text')
|
||||
->with([
|
||||
'link' => route('account.authenticate.email_confirm', [$this->account->confirmation_key])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,9 +21,7 @@ namespace App\Services;
|
|||
|
||||
use App\Account;
|
||||
use App\AccountCreationToken;
|
||||
use App\ActivationExpiration;
|
||||
use App\EmailChangeCode;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
use App\Http\Requests\Account\Create\Request as CreateRequest;
|
||||
use App\Http\Requests\Account\Update\Request as UpdateRequest;
|
||||
use App\Libraries\OvhSMS;
|
||||
|
|
@ -38,7 +36,6 @@ use Carbon\Carbon;
|
|||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class AccountService
|
||||
{
|
||||
|
|
@ -79,21 +76,9 @@ class AccountService
|
|||
}
|
||||
}
|
||||
|
||||
if ($account->activated == false) {
|
||||
$account->confirmation_key = Str::random(WebAuthenticateController::$emailCodeSize);
|
||||
}
|
||||
|
||||
$account->save();
|
||||
|
||||
if ($request->asAdmin) {
|
||||
if ((!$request->has('activated') || !(bool)$request->get('activated'))
|
||||
&& $request->has('confirmation_key_expires')) {
|
||||
$actionvationExpiration = new ActivationExpiration();
|
||||
$actionvationExpiration->account_id = $account->id;
|
||||
$actionvationExpiration->expires = $request->get('confirmation_key_expires');
|
||||
$actionvationExpiration->save();
|
||||
}
|
||||
|
||||
if ($request->has('dictionary')) {
|
||||
foreach ($request->get('dictionary') as $key => $value) {
|
||||
$account->setDictionaryEntry($key, $value);
|
||||
|
|
|
|||
|
|
@ -76,11 +76,6 @@ class BlockingService
|
|||
Carbon::now()->subMinutes(config('app.blocking_time_period_check'))->toDateTimeString()
|
||||
)->count();
|
||||
|
||||
// Deprecated, also detect if the account itself was updated recently, might be because of the confirmation_key change
|
||||
if (Carbon::now()->subMinutes(config('app.blocking_time_period_check'))->isBefore($this->account->updated_at)) {
|
||||
$events++;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,11 +61,6 @@ return [
|
|||
'blocking_time_period_check' => env('BLOCKING_TIME_PERIOD_CHECK', 30),
|
||||
'blocking_amount_events_authorized_during_period' => env('BLOCKING_AMOUNT_EVENTS_AUTHORIZED_DURING_PERIOD', 5),
|
||||
|
||||
/**
|
||||
* /!\ Enable dangerous endpoints required for fallback
|
||||
*/
|
||||
'dangerous_endpoints' => env('APP_DANGEROUS_ENDPOINTS', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ class AccountFactory extends Factory
|
|||
'display_name' => $this->faker->name,
|
||||
'domain' => $domain->domain,
|
||||
'user_agent' => $this->faker->userAgent,
|
||||
'confirmation_key' => Str::random(WebAuthenticateController::$emailCodeSize),
|
||||
'ip_address' => $this->faker->ipv4,
|
||||
'created_at' => $this->faker->dateTimeBetween('-1 year'),
|
||||
'dtmf_protocol' => array_rand(Account::$dtmfProtocols),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::dropIfExists('activation_expirations');
|
||||
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
$table->dropColumn('confirmation_key');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
$table->string('confirmation_key', 14)->nullable();
|
||||
});
|
||||
|
||||
Schema::create('activation_expirations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('account_id')->unsigned();
|
||||
$table->dateTime('expires');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('account_id')->references('id')
|
||||
->on('accounts')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -41,7 +41,6 @@ class LiblinphoneTesterAccoutSeeder extends Seeder
|
|||
$element->domain,
|
||||
$element->phone ?? null,
|
||||
$element->activated ?? true,
|
||||
$element->confirmation_key ?? null
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -71,7 +70,6 @@ class LiblinphoneTesterAccoutSeeder extends Seeder
|
|||
$element->domain,
|
||||
$element->phone ?? null,
|
||||
$element->activated ?? true,
|
||||
$element->confirmation_key ?? null
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -104,7 +102,7 @@ class LiblinphoneTesterAccoutSeeder extends Seeder
|
|||
|
||||
private function generateAccountArray(
|
||||
int $id, string $username, string $domain, string $phone = null,
|
||||
bool $activated = true, string $confirmationKey = null
|
||||
bool $activated = true
|
||||
): array {
|
||||
return [
|
||||
'id' => $id,
|
||||
|
|
@ -114,7 +112,6 @@ class LiblinphoneTesterAccoutSeeder extends Seeder
|
|||
'email' => rawurlencode($username) . '@' . $domain,
|
||||
'activated' => $activated,
|
||||
'ip_address' => '',
|
||||
'confirmation_key' => $confirmationKey,
|
||||
'user_agent' => 'FlexiAPI Seeder',
|
||||
'created_at' => '2010-01-03 04:30:43'
|
||||
];
|
||||
|
|
|
|||
|
|
@ -312,23 +312,6 @@ Return `404` if the token is non existing or invalid.
|
|||
|
||||
## Accounts
|
||||
|
||||
### `POST /accounts/public`
|
||||
<span class="badge badge-message">Deprecated</span> @if(!config('app.dangerous_endpoints'))<span class="badge">Disabled</span>@endif <span class="badge badge-success">Public</span> <span class="badge badge-error">Unsecure endpoint</span>
|
||||
|
||||
Create an account.
|
||||
Return `422` if the parameters are invalid.
|
||||
Send an email with the activation key if `email` is set, send an SMS otherwise.
|
||||
|
||||
JSON parameters:
|
||||
|
||||
* `username` **required** if `phone` not set, unique username, minimum 6 characters
|
||||
* `password` **required** minimum 6 characters
|
||||
* `algorithm` **required**, values can be `SHA-256` or `MD5`
|
||||
* `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 valid 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>
|
||||
|
||||
|
|
@ -352,62 +335,6 @@ JSON parameters:
|
|||
Retrieve public information about the account.
|
||||
Return `404` if the account doesn't exists.
|
||||
|
||||
### `GET /accounts/{phone}/info-by-phone`
|
||||
<span class="badge badge-message">Deprecated</span> @if(!config('app.dangerous_endpoints'))<span class="badge">Disabled</span>@endif <span class="badge badge-success">Public</span> <span class="badge badge-error">Unsecure endpoint</span>
|
||||
|
||||
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`
|
||||
<span class="badge badge-message">Deprecated</span> @if(!config('app.dangerous_endpoints'))<span class="badge">Disabled</span>@endif <span class="badge badge-success">Public</span> <span class="badge badge-error">Unsecure endpoint</span>
|
||||
|
||||
Send a SMS with a recovery PIN code to the `phone` number provided.
|
||||
Return `404` if the account doesn't exists.
|
||||
|
||||
Can only be used once, a new `recover_key` need to be requested to be called again.
|
||||
|
||||
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}`
|
||||
<span class="badge badge-message">Deprecated</span> @if(!config('app.dangerous_endpoints'))<span class="badge">Disabled</span>@endif <span class="badge badge-success">Public</span> <span class="badge badge-error">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.
|
||||
|
||||
### `POST /accounts/{sip}/activate/email`
|
||||
<span class="badge badge-message">Deprecated</span> <span class="badge badge-success">Public</span>
|
||||
|
||||
<a href="#post-accountsmeemailrequest">Use `POST /accounts/me/email/request` instead</a>.
|
||||
|
||||
Activate an account using a secret code received by email.
|
||||
Return `404` if the account doesn't exists or if the code is incorrect, the validated account otherwise.
|
||||
|
||||
JSON parameters:
|
||||
|
||||
* `confirmation_key` the confirmation key
|
||||
|
||||
### `POST /accounts/{sip}/activate/phone`
|
||||
<span class="badge badge-message">Deprecated</span> <span class="badge badge-success">Public</span>
|
||||
|
||||
<a href="#post-accountsmephonerequest">Use `POST /accounts/me/phone/request` instead</a>.
|
||||
|
||||
Activate an account using a pin code received by phone.
|
||||
Return `404` if the account doesn't exists or if the code is incorrect, the validated account otherwise.
|
||||
|
||||
JSON parameters:
|
||||
|
||||
* `confirmation_key` the PIN code
|
||||
|
||||
### `GET /accounts/me/api_key/{auth_token}`
|
||||
<span class="badge badge-success">Public</span>
|
||||
|
||||
|
|
@ -459,7 +386,7 @@ JSON parameters:
|
|||
### `POST /accounts`
|
||||
<span class="badge badge-warning">Admin</span>
|
||||
|
||||
To create an account directly from the API. <span class="badge badge-message">Deprecated</span> If `activated` is set to `false` a random generated `confirmation_key` and `provisioning_token` will be returned to allow further activation using the public endpoints and provision the account. Check `confirmation_key_expires` to also set an expiration date on that `confirmation_key`.
|
||||
To create an account directly from the API.
|
||||
|
||||
Return `403` if the `max_accounts` limit of the corresponding Space is reached.
|
||||
|
||||
|
|
@ -476,7 +403,6 @@ JSON parameters:
|
|||
* `phone` optional, a valid phone number, set a phone number to the account
|
||||
* `dtmf_protocol` optional, values must be `sipinfo`, `sipmessage` or `rfc2833`
|
||||
* `dictionary` optional, an associative array attached to the account, <a href="#dictionary">see also the related endpoints</a>.
|
||||
* <span class="badge badge-message">Deprecated</span> `confirmation_key_expires` optional, a datetime of this format: Y-m-d H:i:s. Only used when `activated` is not used or `false`. Enforces an expiration date on the returned `confirmation_key`. After that datetime public email or phone activation endpoints will return `403`.
|
||||
|
||||
### `PUT /accounts/{id}`
|
||||
<span class="badge badge-warning">Admin</span>
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Register on {{ space()->name }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello,</p>
|
||||
<p>
|
||||
You just created an account on {{ space()->name }} using your email account.<br />
|
||||
Please follow the unique link bellow to set up your password and finish the registration process.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ $link }}">{{ $link }}</a>
|
||||
</p>
|
||||
<p>
|
||||
Regards,<br />
|
||||
{{ config('mail.signature') }}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
Hello,
|
||||
|
||||
You just created an account on {{ space()->name }} using your email account.
|
||||
Please follow the unique link bellow to set up your password and finish the registration process.
|
||||
|
||||
{{ $link }}
|
||||
|
||||
Regards,
|
||||
{{ config('mail.signature') }}
|
||||
|
|
@ -47,16 +47,6 @@ Route::post('accounts/with-account-creation-token', 'Api\Account\AccountControll
|
|||
|
||||
Route::get('accounts/{sip}/info', 'Api\Account\AccountController@info');
|
||||
|
||||
// Deprecated endpoints
|
||||
Route::post('accounts/{sip}/activate/email', 'Api\Account\AccountController@activateEmail');
|
||||
Route::post('accounts/{sip}/activate/phone', 'Api\Account\AccountController@activatePhone');
|
||||
|
||||
// Deprecated endpoints /!\ Dangerous endpoints
|
||||
Route::post('accounts/public', 'Api\Account\AccountController@storePublic');
|
||||
Route::get('accounts/{sip}/recover/{recovery_key}', 'Api\Account\AccountController@recoverUsingKey');
|
||||
Route::post('accounts/recover-by-phone', 'Api\Account\AccountController@recoverByPhone');
|
||||
Route::get('accounts/{phone}/info-by-phone', 'Api\Account\AccountController@phoneInfo');
|
||||
|
||||
Route::post('accounts/auth_token', 'Api\Account\AuthTokenController@store');
|
||||
|
||||
Route::get('accounts/me/api_key/{auth_token}', 'Api\Account\ApiKeyController@generateFromToken')->middleware('cookie', 'cookie.encrypt');
|
||||
|
|
|
|||
|
|
@ -60,9 +60,6 @@ Route::middleware(['web_panel_enabled', 'space.check'])->group(function () {
|
|||
Route::get('reset_password/{token}', 'Account\ResetPasswordEmailController@change')->name('account.reset_password_email.change');
|
||||
Route::post('reset_password', 'Account\ResetPasswordEmailController@reset')->name('account.reset_password_email.reset');
|
||||
|
||||
// Deprecated
|
||||
Route::get('authenticate/email/{code}', 'Account\AuthenticateController@validateEmail')->name('account.authenticate.email_confirm');
|
||||
|
||||
Route::prefix('creation_token')->controller(CreationRequestTokenController::class)->group(function () {
|
||||
Route::get('check/{token}', 'check')->name('account.creation_request_token.check');
|
||||
Route::post('validate', 'validateToken')->name('account.creation_request_token.validate');
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class AccountBlockingTest extends TestCase
|
|||
$account = Account::factory()->withConsumedAccountCreationToken()->create();
|
||||
$account->generateUserApiKey();
|
||||
|
||||
config()->set('app.blocking_amount_events_authorized_during_period', 2);
|
||||
config()->set('app.blocking_amount_events_authorized_during_period', 1);
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->json($this->method, $this->route . '/me/phone/request', [
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@
|
|||
namespace Tests\Feature;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountCreationToken;
|
||||
use App\AccountTombstone;
|
||||
use App\ActivationExpiration;
|
||||
use App\Password;
|
||||
use App\Space;
|
||||
use Carbon\Carbon;
|
||||
|
|
@ -182,7 +180,6 @@ class ApiAccountTest extends TestCase
|
|||
'activated' => false
|
||||
]);
|
||||
|
||||
$this->assertFalse(empty($response1['confirmation_key']));
|
||||
$this->assertFalse(empty($response1['provisioning_token']));
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +324,6 @@ class ApiAccountTest extends TestCase
|
|||
'activated' => false
|
||||
]);
|
||||
|
||||
$this->assertFalse(empty($response1['confirmation_key']));
|
||||
$this->assertFalse(empty($response1['provisioning_token']));
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +390,6 @@ class ApiAccountTest extends TestCase
|
|||
'admin' => true,
|
||||
]);
|
||||
|
||||
$this->assertTrue(!empty($response1['confirmation_key']));
|
||||
$this->assertFalse(empty($response1['provisioning_token']));
|
||||
}
|
||||
|
||||
|
|
@ -532,15 +527,13 @@ class ApiAccountTest extends TestCase
|
|||
$username = 'username';
|
||||
|
||||
$response0 = $this->generateFirstResponse($password);
|
||||
$response1 = $this->generateSecondResponse($password, $response0)
|
||||
$this->generateSecondResponse($password, $response0)
|
||||
->json($this->method, $this->route, [
|
||||
'username' => $username,
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => 'blabla',
|
||||
'activated' => true,
|
||||
]);
|
||||
|
||||
$response1
|
||||
])
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'id' => 2,
|
||||
|
|
@ -548,8 +541,6 @@ class ApiAccountTest extends TestCase
|
|||
'domain' => config('app.sip_domain'),
|
||||
'activated' => true,
|
||||
]);
|
||||
|
||||
$this->assertTrue(empty($response1['confirmation_key']));
|
||||
}
|
||||
|
||||
public function testNotActivated()
|
||||
|
|
@ -575,7 +566,6 @@ class ApiAccountTest extends TestCase
|
|||
'activated' => false,
|
||||
]);
|
||||
|
||||
$this->assertFalse(empty($response1['confirmation_key']));
|
||||
$this->assertFalse(empty($response1['provisioning_token']));
|
||||
}
|
||||
|
||||
|
|
@ -630,68 +620,6 @@ class ApiAccountTest extends TestCase
|
|||
->assertStatus(404);
|
||||
}
|
||||
|
||||
public function testActivateEmail()
|
||||
{
|
||||
$confirmationKey = '0123456789abc';
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateUserApiKey();
|
||||
$password->account->confirmation_key = $confirmationKey;
|
||||
$password->account->activated = false;
|
||||
$password->account->save();
|
||||
|
||||
$expiration = new ActivationExpiration();
|
||||
$expiration->account_id = $password->account->id;
|
||||
$expiration->expires = Carbon::now()->subYear();
|
||||
$expiration->save();
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/info')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'activated' => false
|
||||
]);
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $this->route . '/blabla/activate/email', [
|
||||
'confirmation_key' => $confirmationKey
|
||||
])
|
||||
->assertStatus(404);
|
||||
|
||||
$activateEmailRoute = $this->route . '/' . $password->account->identifier . '/activate/email';
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $activateEmailRoute, [
|
||||
'confirmation_key' => $confirmationKey . 'longer'
|
||||
])
|
||||
->assertStatus(422);
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $activateEmailRoute, [
|
||||
'confirmation_key' => 'X123456789abc'
|
||||
])
|
||||
->assertStatus(404);
|
||||
|
||||
// Expired
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $activateEmailRoute, [
|
||||
'confirmation_key' => $confirmationKey
|
||||
])
|
||||
->assertStatus(403);
|
||||
|
||||
$expiration->delete();
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $activateEmailRoute, [
|
||||
'confirmation_key' => $confirmationKey
|
||||
])
|
||||
->assertStatus(200);
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/info')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'activated' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function testUniqueEmailAdmin()
|
||||
{
|
||||
$email = 'collision@email.com';
|
||||
|
|
@ -794,329 +722,6 @@ class ApiAccountTest extends TestCase
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoints
|
||||
*/
|
||||
public function testRecover()
|
||||
{
|
||||
$confirmationKey = '0123';
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateUserApiKey();
|
||||
$password->account->confirmation_key = $confirmationKey;
|
||||
$password->account->activated = false;
|
||||
$password->account->save();
|
||||
|
||||
config()->set('app.dangerous_endpoints', true);
|
||||
|
||||
$this->assertDatabaseHas('accounts', [
|
||||
'username' => $password->account->username,
|
||||
'domain' => $password->account->domain,
|
||||
'activated' => false
|
||||
]);
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/recover/' . $confirmationKey)
|
||||
->assertJson(['passwords' => [[
|
||||
'password' => $password->password,
|
||||
'algorithm' => $password->algorithm
|
||||
]]])
|
||||
->assertStatus(200);
|
||||
|
||||
$this->json('GET', $this->route . '/' . $password->account->identifier . '/recover/' . $confirmationKey)
|
||||
->assertStatus(404);
|
||||
|
||||
$this->assertDatabaseHas('accounts', [
|
||||
'username' => $password->account->username,
|
||||
'domain' => $password->account->domain,
|
||||
'confirmation_key' => null,
|
||||
'activated' => true
|
||||
]);
|
||||
|
||||
// Recover by phone
|
||||
|
||||
$newConfirmationKey = '1345';
|
||||
$phone = '+1234';
|
||||
|
||||
$password->account->confirmation_key = $newConfirmationKey;
|
||||
$password->account->phone = $phone;
|
||||
$password->account->save();
|
||||
|
||||
$this->get($this->route . '/' . $phone . '@' . $password->account->domain . '/recover/' . $newConfirmationKey)
|
||||
->assertJson(['passwords' => [[
|
||||
'password' => $password->password,
|
||||
'algorithm' => $password->algorithm
|
||||
]]])
|
||||
->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testRecoverTwice()
|
||||
{
|
||||
$confirmationKey = '1234';
|
||||
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateUserApiKey();
|
||||
$password->account->confirmation_key = $confirmationKey;
|
||||
$password->account->activated = false;
|
||||
$password->account->save();
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/recover/wrongkey')
|
||||
->assertStatus(404);
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/recover/' . $confirmationKey)
|
||||
->assertStatus(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoints
|
||||
*/
|
||||
public function testRecoverPhone()
|
||||
{
|
||||
$phone = '+33612312312';
|
||||
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateUserApiKey();
|
||||
$password->account->activated = false;
|
||||
$password->account->phone = $phone;
|
||||
$password->account->save();
|
||||
|
||||
config()->set('app.dangerous_endpoints', true);
|
||||
|
||||
$this->json($this->method, $this->route . '/recover-by-phone', [
|
||||
'phone' => $phone
|
||||
])->assertJsonValidationErrors(['account_creation_token']);
|
||||
|
||||
$this->json($this->method, $this->route . '/recover-by-phone', [
|
||||
'phone' => $phone,
|
||||
'account_creation_token' => 'wrong'
|
||||
])->assertJsonValidationErrors(['account_creation_token']);
|
||||
|
||||
$token = AccountCreationToken::factory()->create();
|
||||
|
||||
// Wrong phone
|
||||
$this->json($this->method, $this->route . '/recover-by-phone', [
|
||||
'phone' => '+33612312313', // wrong phone number
|
||||
'account_creation_token' => $token->token
|
||||
])->assertJsonValidationErrors(['phone']);
|
||||
|
||||
$this->json($this->method, $this->route . '/recover-by-phone', [
|
||||
'phone' => $phone,
|
||||
'account_creation_token' => $token->token
|
||||
])->assertStatus(200);
|
||||
|
||||
$password->account->refresh();
|
||||
|
||||
// Use the token a second time
|
||||
$this->json($this->method, $this->route . '/recover-by-phone', [
|
||||
'phone' => $phone,
|
||||
'account_creation_token' => $token->token
|
||||
])->assertStatus(422);
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/recover/' . $password->account->confirmation_key)
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'activated' => true
|
||||
]);
|
||||
|
||||
$this->get($this->route . '/' . $phone . '/info-by-phone')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'activated' => true,
|
||||
'phone' => true
|
||||
]);
|
||||
|
||||
$this->get($this->route . '/+1234/info-by-phone')
|
||||
->assertStatus(302);
|
||||
|
||||
$this->get($this->route . '/+33612312312/info-by-phone')
|
||||
->assertStatus(200);
|
||||
|
||||
$this->json('GET', $this->route . '/' . $password->account->identifier . '/info-by-phone')
|
||||
->assertJsonValidationErrors(['phone']);
|
||||
|
||||
// Check the mixed username/phone resolution...
|
||||
$password->account->username = $phone;
|
||||
$password->account->phone = null;
|
||||
$password->account->save();
|
||||
|
||||
$this->get($this->route . '/' . $phone . '/info-by-phone')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'activated' => true,
|
||||
'phone' => false
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('account_creation_tokens', [
|
||||
'used' => true,
|
||||
'account_id' => $password->account->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* /!\ Dangerous endpoints
|
||||
*/
|
||||
|
||||
public function testCreatePublic()
|
||||
{
|
||||
$username = 'publicuser';
|
||||
|
||||
config()->set('app.dangerous_endpoints', true);
|
||||
|
||||
// Missing email
|
||||
$this->json($this->method, $this->route . '/public', [
|
||||
'username' => $username,
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
])->assertJsonValidationErrors(['email']);
|
||||
|
||||
$this->json($this->method, $this->route . '/public', [
|
||||
'username' => $username,
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
'email' => 'john@doe.tld',
|
||||
])->assertJsonValidationErrors(['account_creation_token']);
|
||||
|
||||
$token = AccountCreationToken::factory()->create();
|
||||
$userAgent = 'User Agent Test';
|
||||
|
||||
$this->withHeaders([
|
||||
'User-Agent' => $userAgent,
|
||||
])->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([
|
||||
'activated' => false
|
||||
]);
|
||||
|
||||
// Re-use the token
|
||||
$this->withHeaders([
|
||||
'User-Agent' => $userAgent,
|
||||
])->json($this->method, $this->route . '/public', [
|
||||
'username' => $username . 'foo',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
'email' => 'john@doe.tld',
|
||||
'account_creation_token' => $token->token
|
||||
])->assertStatus(422);
|
||||
|
||||
// Already created
|
||||
$this->json($this->method, $this->route . '/public', [
|
||||
'username' => $username,
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
'email' => 'john@doe.tld',
|
||||
])->assertJsonValidationErrors(['username']);
|
||||
|
||||
// Email is now unique
|
||||
config()->set('app.account_email_unique', true);
|
||||
|
||||
$this->json($this->method, $this->route . '/public', [
|
||||
'username' => 'johndoe',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
'email' => 'john@doe.tld',
|
||||
])->assertJsonValidationErrors(['email']);
|
||||
|
||||
$this->assertDatabaseHas('accounts', [
|
||||
'username' => $username,
|
||||
'domain' => config('app.sip_domain'),
|
||||
'user_agent' => $userAgent
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('account_creation_tokens', [
|
||||
'used' => true,
|
||||
'account_id' => Account::where('username', $username)->first()->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCreatePublicPhone()
|
||||
{
|
||||
$phone = '+33612312312';
|
||||
|
||||
config()->set('app.dangerous_endpoints', true);
|
||||
|
||||
// Bad phone format
|
||||
$this->json($this->method, $this->route . '/public', [
|
||||
'phone' => 'username',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
'email' => 'john@doe.tld',
|
||||
])->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([
|
||||
'activated' => false
|
||||
]);
|
||||
|
||||
// Already exists
|
||||
$this->json($this->method, $this->route . '/public', [
|
||||
'phone' => $phone,
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '2',
|
||||
'email' => 'john@doe.tld',
|
||||
])->assertJsonValidationErrors(['phone']);
|
||||
|
||||
$this->assertDatabaseHas('accounts', [
|
||||
'username' => $phone,
|
||||
'phone' => $phone,
|
||||
'domain' => config('app.sip_domain')
|
||||
]);
|
||||
}
|
||||
|
||||
public function testActivatePhone()
|
||||
{
|
||||
$confirmationKey = '0123';
|
||||
$password = Password::factory()->create();
|
||||
$password->account->generateUserApiKey();
|
||||
$password->account->confirmation_key = $confirmationKey;
|
||||
$password->account->activated = false;
|
||||
$password->account->save();
|
||||
|
||||
$expiration = new ActivationExpiration();
|
||||
$expiration->account_id = $password->account->id;
|
||||
$expiration->expires = Carbon::now()->subYear();
|
||||
$expiration->save();
|
||||
|
||||
$this->get($this->route . '/' . $password->account->identifier . '/info')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'activated' => false
|
||||
]);
|
||||
|
||||
// Expired
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $this->route . '/' . $password->account->identifier . '/activate/phone', [
|
||||
'confirmation_key' => $confirmationKey
|
||||
])
|
||||
->assertStatus(403);
|
||||
|
||||
$expiration->delete();
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->json($this->method, $this->route . '/' . $password->account->identifier . '/activate/phone', [
|
||||
'confirmation_key' => $confirmationKey
|
||||
])
|
||||
->assertStatus(200);
|
||||
|
||||
$this->assertDatabaseHas('accounts', [
|
||||
'username' => $password->account->username,
|
||||
'domain' => $password->account->domain,
|
||||
'activated' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function testChangePassword()
|
||||
{
|
||||
$account = Account::factory()->create();
|
||||
|
|
@ -1275,52 +880,6 @@ class ApiAccountTest extends TestCase
|
|||
]);
|
||||
}
|
||||
|
||||
public function testCodeExpires()
|
||||
{
|
||||
$admin = Account::factory()->admin()->create();
|
||||
$admin->generateUserApiKey();
|
||||
|
||||
// Activated, no no confirmation_key
|
||||
$this->keyAuthenticated($admin)
|
||||
->json($this->method, $this->route, [
|
||||
'username' => 'foobar',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123456',
|
||||
'activated' => true,
|
||||
'confirmation_key_expires' => '2040-12-12 12:12:12'
|
||||
])
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'confirmation_key_expires' => null
|
||||
]);
|
||||
|
||||
// Bad datetime format
|
||||
$this->keyAuthenticated($admin)
|
||||
->json($this->method, $this->route, [
|
||||
'username' => 'foobar2',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123456',
|
||||
'activated' => false,
|
||||
'confirmation_key_expires' => 'abc'
|
||||
])
|
||||
->assertStatus(422);
|
||||
|
||||
// Bad datetime format
|
||||
$this->keyAuthenticated($admin)
|
||||
->json($this->method, $this->route, [
|
||||
'username' => 'foobar2',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123456',
|
||||
'activated' => false,
|
||||
'confirmation_key_expires' => '2040-12-12 12:12:12'
|
||||
])
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'confirmation_key_expires' => '2040-12-12 12:12:12'
|
||||
]);
|
||||
;
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
$password = Password::factory()->create();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue