mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-01-17 01:58:07 +00:00
Fix FLEXIAPI-192 Add DotEnv configuration to allow the expiration of tokens and codes in the app
This commit is contained in:
parent
29fc1744a3
commit
f6c5562201
46 changed files with 486 additions and 66 deletions
|
|
@ -1,5 +1,9 @@
|
|||
# Flexisip Account Manager Changelog
|
||||
|
||||
v1.6
|
||||
----
|
||||
- Fix FLEXIAPI-192 Add DotEnv configuration to allow the expiration of tokens and codes in the app
|
||||
|
||||
v1.5
|
||||
---
|
||||
- Fix FLEXIAPI-195 Fix LiblinphoneTesterAccoutSeeder to fit with the latest Account related changes
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ APP_LINPHONE_DAEMON_UNIX_PATH=
|
|||
APP_FLEXISIP_PUSHER_PATH=
|
||||
APP_FLEXISIP_PUSHER_FIREBASE_KEYSMAP= # Each pair is separated using a space and defined as a key:value
|
||||
|
||||
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
|
||||
|
||||
APP_ALLOW_PHONE_NUMBER_USERNAME_ADMIN_API=false # Allow phone numbers to be set as username in admin account creation endpoints
|
||||
|
|
@ -22,9 +21,16 @@ ACCOUNT_PROXY_REGISTRAR_ADDRESS=sip.example.com # Proxy registrar address, can b
|
|||
ACCOUNT_TRANSPORT_PROTOCOL_TEXT="TLS (recommended), TCP or UDP" # Simple text, to explain how the SIP server can be reached
|
||||
ACCOUNT_REALM=null # Default realm for the accounts, fallback to the domain if not set, enforce null by default
|
||||
|
||||
# Expiration time for tokens and code, in minutes, 0 means no expiration
|
||||
APP_ACCOUNT_CREATION_TOKEN_EXPIRATION_MINUTES=0
|
||||
APP_EMAIL_CHANGE_CODE_EXPIRATION_MINUTES=10
|
||||
APP_PHONE_CHANGE_CODE_EXPIRATION_MINUTES=10
|
||||
APP_RECOVERY_CODE_EXPIRATION_MINUTES=10
|
||||
APP_PROVISIONING_TOKEN_EXPIRATION_MINUTES=0
|
||||
APP_API_KEY_EXPIRATION_MINUTES=60 # Number of minutes the unused API Keys are valid
|
||||
|
||||
# Account creation
|
||||
ACCOUNT_EMAIL_UNIQUE=false # Emails are unique between all the accounts
|
||||
ACCOUNT_CONSUME_EXTERNAL_ACCOUNT_ON_CREATE=false
|
||||
ACCOUNT_BLACKLISTED_USERNAMES=
|
||||
ACCOUNT_USERNAME_REGEX="^[a-z0-9+_.-]*$"
|
||||
ACCOUNT_DEFAULT_PASSWORD_ALGORITHM=SHA-256 # Can ONLY be MD5 or SHA-256 in capital, default to SHA-256
|
||||
|
|
@ -45,12 +51,12 @@ INSTANCE_CUSTOM_THEME=false
|
|||
INSTANCE_CONFIRMED_REGISTRATION_TEXT= # Markdown text displayed when an account is confirmed
|
||||
|
||||
WEB_PANEL=true # Fully enable/disable the web panels
|
||||
NEWSLETTER_REGISTRATION_ADDRESS= # Address to contact when a user wants to register to the newsletter
|
||||
PUBLIC_REGISTRATION=true # Toggle to enable/disable the public registration forms
|
||||
PHONE_AUTHENTICATION=true # Toggle to enable/disable the SMS support, requires public registration
|
||||
DEVICES_MANAGEMENT=false # Toggle to enable/disable the devices management supporttrue
|
||||
INTERCOM_FEATURES=false # Toggle to enable/disable the intercom related features
|
||||
|
||||
NEWSLETTER_REGISTRATION_ADDRESS= # Address to contact when a user wants to register to the newsletter
|
||||
TERMS_OF_USE_URL= # A URL pointing to the Terms of Use
|
||||
PRIVACY_POLICY_URL= # A URL pointing to the Privacy Policy
|
||||
APP_PROJECT_URL= # A URL pointing to the project information page
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class Account extends Authenticatable
|
|||
|
||||
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', 'dictionary'];
|
||||
protected $appends = ['realm', 'confirmation_key_expires', 'provisioning_token', 'provisioning_token_expire_at', 'dictionary'];
|
||||
protected $casts = [
|
||||
'activated' => 'boolean',
|
||||
];
|
||||
|
|
@ -272,6 +272,15 @@ class Account extends Authenticatable
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getProvisioningTokenExpireAtAttribute(): ?string
|
||||
{
|
||||
if ($this->currentProvisioningToken) {
|
||||
return $this->currentProvisioningToken->expire_at;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getIdentifierAttribute(): string
|
||||
{
|
||||
return $this->attributes['username'] . '@' . $this->attributes['domain'];
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ class AccountCreationToken extends Consommable
|
|||
use HasFactory;
|
||||
|
||||
protected $hidden = ['id', 'updated_at', 'created_at'];
|
||||
protected $appends = ['expire_at'];
|
||||
protected ?string $configExpirationMinutesKey = 'account_creation_token_expiration_minutes';
|
||||
|
||||
public function accountCreationRequestToken()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use App\AccountTombstone;
|
||||
|
||||
class ClearOldAccountsTombstones extends Command
|
||||
class ClearAccountsTombstones extends Command
|
||||
{
|
||||
protected $signature = 'accounts:clear-accounts-tombstones {days} {--apply}';
|
||||
protected $description = 'Clear deleted accounts tombstones after n days';
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
|
|
@ -38,6 +38,11 @@ class ClearApiKeys extends Command
|
|||
{
|
||||
$minutes = $this->argument('minutes') ?? config('app.api_key_expiration_minutes');
|
||||
|
||||
if ($minutes == 0) {
|
||||
$this->info('Expiration time is set to 0, nothing to clear');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info('Deleting api keys unused after ' . $minutes . ' minutes');
|
||||
|
||||
$count = ApiKey::where(
|
||||
|
|
@ -17,14 +17,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use App\Account;
|
||||
|
||||
class RemoveUnconfirmedAccounts extends Command
|
||||
class ClearUnconfirmed extends Command
|
||||
{
|
||||
protected $signature = 'accounts:clear-unconfirmed {days} {--apply} {--and-confirmed}';
|
||||
protected $description = 'Clear unconfirmed accounts after n days';
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
|
|
@ -17,16 +17,15 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use App\Account;
|
||||
use App\ApiKey;
|
||||
use App\SipDomain;
|
||||
|
||||
class CreateAdminAccountTest extends Command
|
||||
class CreateAdminTest extends Command
|
||||
{
|
||||
protected $signature = 'accounts:create-admin-test';
|
||||
protected $description = 'Create a test admin account, only for tests purpose';
|
||||
|
|
@ -17,13 +17,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Database\Seeders\LiblinphoneTesterAccoutSeeder;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class RunAccountSeeder extends Command
|
||||
class Seed extends Command
|
||||
{
|
||||
protected $signature = 'accounts:seed {json-file-path}';
|
||||
protected $description = 'Seed some accounts from a JSON file';
|
||||
|
|
@ -17,13 +17,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use App\Account;
|
||||
|
||||
class SetAccountAdmin extends Command
|
||||
class SetAdmin extends Command
|
||||
{
|
||||
protected $signature = 'accounts:set-admin {id}';
|
||||
protected $description = 'Give the admin role to an account';
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\Digest;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
|
|
@ -17,12 +17,12 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Console\Commands\SipDomains;
|
||||
|
||||
use App\SipDomain;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CreateSipDomain extends Command
|
||||
class CreateUpdate extends Command
|
||||
{
|
||||
protected $signature = 'sip_domains:create-update {domain} {--super}';
|
||||
protected $description = 'Create a SIP Domain';
|
||||
|
|
@ -2,12 +2,18 @@
|
|||
|
||||
namespace App;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DateTime;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
abstract class Consommable extends Model
|
||||
{
|
||||
protected string $consommableAttribute = 'code';
|
||||
protected ?string $configExpirationMinutesKey = null;
|
||||
protected $casts = [
|
||||
'expire_at' => 'datetime'
|
||||
];
|
||||
|
||||
public function consume()
|
||||
{
|
||||
|
|
@ -25,4 +31,26 @@ abstract class Consommable extends Model
|
|||
{
|
||||
return $this->{$this->consommableAttribute} == null;
|
||||
}
|
||||
|
||||
public function getExpireAtAttribute(): ?string
|
||||
{
|
||||
if ($this->isExpirable()) {
|
||||
return $this->created_at->addMinutes(config('app.' . $this->configExpirationMinutesKey))->toJSON();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function expired(): bool
|
||||
{
|
||||
return ($this->isExpirable()
|
||||
&& Carbon::now()->subMinutes(config('app.' . $this->configExpirationMinutesKey))->isAfter($this->created_at));
|
||||
}
|
||||
|
||||
private function isExpirable(): bool
|
||||
{
|
||||
return $this->configExpirationMinutesKey != null
|
||||
&& config('app.' . $this->configExpirationMinutesKey) != null
|
||||
&& config('app.' . $this->configExpirationMinutesKey) > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class EmailChangeCode extends Consommable
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected ?string $configExpirationMinutesKey = 'email_change_code_expiration_minutes';
|
||||
protected $hidden = ['id', 'account_id', 'code'];
|
||||
|
||||
public function account()
|
||||
|
|
|
|||
|
|
@ -128,7 +128,11 @@ class ProvisioningController extends Controller
|
|||
->firstOrFail();
|
||||
|
||||
if ($account->activationExpired() || ($provisioningToken != $account->provisioning_token)) {
|
||||
abort(404);
|
||||
return abort(404);
|
||||
}
|
||||
|
||||
if ($account->currentProvisioningToken->expired()) {
|
||||
return abort(410, 'Expired');
|
||||
}
|
||||
|
||||
$account->activated = true;
|
||||
|
|
|
|||
|
|
@ -120,6 +120,14 @@ class RecoveryController extends Controller
|
|||
|
||||
$account = Account::where('id', Crypt::decryptString($request->get('account_id')))->firstOrFail();
|
||||
|
||||
if ($account->currentRecoveryCode->expired()) {
|
||||
return redirect()->route($request->get('method') == 'phone'
|
||||
? 'account.recovery.show.phone'
|
||||
: 'account.recovery.show.email')->withErrors([
|
||||
'code' => 'The code is expired'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($account->recovery_code != $code) {
|
||||
return redirect()->route($request->get('method') == 'phone'
|
||||
? 'account.recovery.show.phone'
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ 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\NoUppercase;
|
||||
use App\Rules\SIPUsername;
|
||||
|
|
@ -43,7 +44,6 @@ use App\Rules\WithoutSpaces;
|
|||
use App\Rules\PasswordAlgorithm;
|
||||
|
||||
use App\Services\AccountService;
|
||||
use App\SipDomain;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
|
|
@ -122,7 +122,8 @@ class AccountController extends Controller
|
|||
],
|
||||
'account_creation_token' => [
|
||||
'required',
|
||||
new RulesAccountCreationToken
|
||||
new RulesAccountCreationToken,
|
||||
new AccountCreationTokenNotExpired
|
||||
]
|
||||
]);
|
||||
|
||||
|
|
@ -192,7 +193,8 @@ class AccountController extends Controller
|
|||
],
|
||||
'account_creation_token' => [
|
||||
'required',
|
||||
new RulesAccountCreationToken
|
||||
new RulesAccountCreationToken,
|
||||
new AccountCreationTokenNotExpired
|
||||
]
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Create\Api;
|
||||
|
||||
|
|
@ -7,7 +24,6 @@ use App\Http\Requests\Api as RequestsApi;
|
|||
use App\Http\Requests\AsAdmin;
|
||||
use App\Rules\IsNotPhoneNumber;
|
||||
use App\Rules\PasswordAlgorithm;
|
||||
use App\SipDomain;
|
||||
|
||||
class AsAdminRequest extends Request
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,28 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Create\Api;
|
||||
|
||||
use App\Http\Requests\Account\Create\Request as CreateRequest;
|
||||
use App\Http\Requests\Api as RequestsApi;
|
||||
use App\Rules\AccountCreationToken;
|
||||
use App\Rules\AccountCreationTokenNotExpired;
|
||||
|
||||
class Request extends CreateRequest
|
||||
{
|
||||
|
|
@ -18,7 +36,7 @@ class Request extends CreateRequest
|
|||
public function rules()
|
||||
{
|
||||
$rules = parent::rules();
|
||||
$rules['account_creation_token'] = ['required', new AccountCreationToken()];
|
||||
$rules['account_creation_token'] = ['required', new AccountCreationToken, new AccountCreationTokenNotExpired];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Create;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Create\Web;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Create\Web;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Update\Api;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Update;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests\Account\Update\Web;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Requests;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class RecoverByCode extends Mailable
|
|||
? 'mails.authentication_text_custom'
|
||||
: 'mails.authentication_text')
|
||||
->with([
|
||||
'expiration_minutes' => config('app.recovery_code_expiration_minutes'),
|
||||
'recovery_code' => $this->account->recovery_code,
|
||||
'provisioning_link' => route('provisioning.provision', [
|
||||
'provisioning_token' => $this->account->provisioning_token,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class PhoneChangeCode extends Consommable
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected ?string $configExpirationMinutesKey = 'phone_change_code_expiration_minutes';
|
||||
protected $hidden = ['id', 'account_id', 'code'];
|
||||
|
||||
public function account()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class ProvisioningToken extends Consommable
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected ?string $configExpirationMinutesKey = 'provisioning_token_expiration_minutes';
|
||||
protected $casts = [
|
||||
'used' => 'boolean',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
class RecoveryCode extends Consommable
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected ?string $configExpirationMinutesKey = 'recovery_code_expiration_minutes';
|
||||
}
|
||||
|
|
|
|||
38
flexiapi/app/Rules/AccountCreationTokenNotExpired.php
Normal file
38
flexiapi/app/Rules/AccountCreationTokenNotExpired.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 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\Rules;
|
||||
|
||||
use App\AccountCreationToken as AppAccountCreationToken;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class AccountCreationTokenNotExpired implements Rule
|
||||
{
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
$token = AppAccountCreationToken::where('token', $value)->where('used', false)->first();
|
||||
|
||||
return $token && !$token->expired();
|
||||
}
|
||||
|
||||
public function message()
|
||||
{
|
||||
return 'The provided token is expired';
|
||||
}
|
||||
}
|
||||
|
|
@ -262,6 +262,10 @@ class AccountService
|
|||
|
||||
$phoneChangeCode = $account->phoneChangeCode()->firstOrFail();
|
||||
|
||||
if ($phoneChangeCode->expired()) {
|
||||
return abort(410, 'Expired code');
|
||||
}
|
||||
|
||||
if ($phoneChangeCode->code == $code) {
|
||||
$account->phone = $phoneChangeCode->phone;
|
||||
$account->activated = true;
|
||||
|
|
@ -329,6 +333,11 @@ class AccountService
|
|||
$account = $request->user();
|
||||
|
||||
$emailChangeCode = $account->emailChangeCode()->firstOrFail();
|
||||
|
||||
if ($emailChangeCode->expired()) {
|
||||
return abort(410, 'Expired code');
|
||||
}
|
||||
|
||||
if ($emailChangeCode->validate($code)) {
|
||||
$account->email = $emailChangeCode->email;
|
||||
$account->save();
|
||||
|
|
@ -371,8 +380,14 @@ class AccountService
|
|||
{
|
||||
$account = $this->recoverAccount($account);
|
||||
|
||||
$message = 'Your ' . config('app.name') . ' validation code is ' . $account->recovery_code . ' .';
|
||||
|
||||
if (config('app.recovery_code_expiration_minutes') > 0) {
|
||||
$message .= 'The code is available for ' . config('app.recovery_code_expiration_minutes') . ' minutes';
|
||||
}
|
||||
|
||||
$ovhSMS = new OvhSMS();
|
||||
$ovhSMS->send($account->phone, 'Your ' . config('app.name') . ' validation code is ' . $account->recovery_code);
|
||||
$ovhSMS->send($account->phone, $message);
|
||||
|
||||
Log::channel('events')->info('Account Service: Sending recovery SMS', ['id' => $account->identifier]);
|
||||
|
||||
|
|
@ -383,7 +398,6 @@ class AccountService
|
|||
{
|
||||
$account->recover();
|
||||
$account->provision();
|
||||
|
||||
$account->refresh();
|
||||
|
||||
return $account;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,11 @@ return [
|
|||
* Time limit before the API Key and related cookie are expired
|
||||
*/
|
||||
'api_key_expiration_minutes' => env('APP_API_KEY_EXPIRATION_MINUTES', 60),
|
||||
|
||||
'account_creation_token_expiration_minutes' => env('APP_ACCOUNT_CREATION_TOKEN_EXPIRATION_MINUTES', 0),
|
||||
'email_change_code_expiration_minutes' => env('APP_EMAIL_CHANGE_CODE_EXPIRATION_MINUTES', 10),
|
||||
'phone_change_code_expiration_minutes' => env('APP_PHONE_CHANGE_CODE_EXPIRATION_MINUTES', 10),
|
||||
'recovery_code_expiration_minutes' => env('APP_RECOVERY_CODE_EXPIRATION_MINUTES', 10),
|
||||
'provisioning_token_expiration_minutes' => env('APP_PROVISIONING_TOKEN_EXPIRATION_MINUTES', 0),
|
||||
/**
|
||||
* Amount of minutes before re-authorizing the generation of a new account creation token
|
||||
*/
|
||||
|
|
|
|||
5
flexiapi/config/rcfile
Normal file
5
flexiapi/config/rcfile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[auth_info_0]
|
||||
test=foobar
|
||||
|
||||
[auth_info_1]
|
||||
blabla=gnap
|
||||
|
|
@ -43,4 +43,11 @@ class AccountCreationTokenFactory extends Factory
|
|||
'created_at' => Carbon::now()
|
||||
];
|
||||
}
|
||||
|
||||
public function expired()
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'created_at' => Carbon::now()->subMinutes(1000)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,12 @@
|
|||
|
||||
@if ($method == 'email')
|
||||
<div class="large">
|
||||
<p class="large">Enter your email account to recover it.</p>
|
||||
<p class="large">
|
||||
Enter your email account to recover it.
|
||||
@if (config('app.recovery_code_expiration_minutes') > 0)
|
||||
<br /> The code will be available {{ config('app.recovery_code_expiration_minutes') }} minutes.
|
||||
@endif
|
||||
</p>
|
||||
@include('parts.errors', ['name' => 'code'])
|
||||
</div>
|
||||
<div class="large">
|
||||
|
|
@ -29,7 +34,12 @@
|
|||
</div>
|
||||
@endif
|
||||
@elseif($method == 'phone')
|
||||
<p class="large">Enter your phone number to recover your account.</p>
|
||||
<p class="large">
|
||||
Enter your phone number to recover your account.
|
||||
@if (config('app.recovery_code_expiration_minutes') > 0)
|
||||
<br />The code will be available {{ config('app.recovery_code_expiration_minutes') }} minutes.
|
||||
@endif
|
||||
</p>
|
||||
<div>
|
||||
<input placeholder="+123456789" name="phone" type="text" value="{{ old('phone') }}">
|
||||
<label for="phone">Phone</label>
|
||||
|
|
|
|||
5
flexiapi/resources/views/errors/410.blade.php
Normal file
5
flexiapi/resources/views/errors/410.blade.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@extends('errors::minimal')
|
||||
|
||||
@section('title', __('Expired resource'))
|
||||
@section('code', '410')
|
||||
@section('message', __('The resource you requested is expired'))
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<p class="text-center">
|
||||
@yield('message')
|
||||
<br />
|
||||
<br /><br />
|
||||
<a class="btn btn-secondary mt-5" href="{{ route('account.home') }}">
|
||||
Go back to the homepage
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@
|
|||
<p>
|
||||
<h2>{{ $recovery_code }}</h2>
|
||||
</p>
|
||||
@if ($expiration_minutes > 0)
|
||||
<p>
|
||||
The code is only available {{ $expiration_minutes }} minutes.
|
||||
</p>
|
||||
@endif
|
||||
<p>
|
||||
You can as well configure your new device using the following code or by directly flashing the QRCode:<br />
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ Please enter the code bellow to finish the authentication process.
|
|||
|
||||
{{ $recovery_code }}
|
||||
|
||||
@if ($expiration_minutes > 0)
|
||||
The code is only available {{ $expiration_minutes }} minutes.
|
||||
@endif
|
||||
|
||||
You can as well configure your new device using the following code or by directly flashing the QRCode in the following link:
|
||||
|
||||
{{ $provisioning_qrcode}}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ namespace Tests\Feature;
|
|||
use App\Account;
|
||||
use App\AuthToken;
|
||||
use App\Password;
|
||||
use App\ProvisioningToken;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AccountProvisioningTest extends TestCase
|
||||
|
|
@ -134,9 +136,6 @@ class AccountProvisioningTest extends TestCase
|
|||
|
||||
// Regenerate a new provisioning token from the authenticated account
|
||||
$this->keyAuthenticated($password->account)
|
||||
->withHeaders([
|
||||
'x-linphone-provisioning' => true,
|
||||
])
|
||||
->get('/api/accounts/me/provision')
|
||||
->assertStatus(200)
|
||||
->assertSee('provisioning_token')
|
||||
|
|
@ -230,9 +229,6 @@ class AccountProvisioningTest extends TestCase
|
|||
$admin->generateApiKey();
|
||||
|
||||
$this->keyAuthenticated($admin)
|
||||
->withHeaders([
|
||||
'x-linphone-provisioning' => true,
|
||||
])
|
||||
->json($this->method, '/api/accounts/' . $password->account->id . '/provision')
|
||||
->assertStatus(200)
|
||||
->assertSee('provisioning_token')
|
||||
|
|
@ -255,10 +251,7 @@ class AccountProvisioningTest extends TestCase
|
|||
public function testAuthTokenProvisioning()
|
||||
{
|
||||
// Generate a public auth_token and attach it
|
||||
$response = $this->withHeaders([
|
||||
'x-linphone-provisioning' => true,
|
||||
])
|
||||
->json('POST', '/api/accounts/auth_token')
|
||||
$response = $this->json('POST', '/api/accounts/auth_token')
|
||||
->assertStatus(201)
|
||||
->assertJson([
|
||||
'token' => true
|
||||
|
|
@ -270,9 +263,6 @@ class AccountProvisioningTest extends TestCase
|
|||
$password->account->generateApiKey();
|
||||
|
||||
$this->keyAuthenticated($password->account)
|
||||
->withHeaders([
|
||||
'x-linphone-provisioning' => true,
|
||||
])
|
||||
->json($this->method, '/api/accounts/auth_token/' . $authToken . '/attach')
|
||||
->assertStatus(200);
|
||||
|
||||
|
|
@ -296,4 +286,38 @@ class AccountProvisioningTest extends TestCase
|
|||
->get($this->route . '/auth_token/' . $authToken)
|
||||
->assertStatus(404);
|
||||
}
|
||||
|
||||
public function testTokenExpiration()
|
||||
{
|
||||
$account = Account::factory()->create();
|
||||
$account->generateApiKey();
|
||||
$expirationMinutes = 10;
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->get('/api/accounts/me/provision')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'provisioning_token_expire_at' => null
|
||||
]);
|
||||
|
||||
config()->set('app.provisioning_token_expiration_minutes', $expirationMinutes);
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->get('/api/accounts/me/provision')
|
||||
->assertStatus(200)
|
||||
->assertJson([
|
||||
'provisioning_token_expire_at' => $account->currentProvisioningToken->created_at->addMinutes($expirationMinutes)->toJSON()
|
||||
]);
|
||||
|
||||
$account->refresh();
|
||||
|
||||
ProvisioningToken::where('id', $account->currentProvisioningToken->id)
|
||||
->update(['created_at' => $account->currentProvisioningToken->created_at->subMinutes(1000)]);
|
||||
|
||||
$this->withHeaders([
|
||||
'x-linphone-provisioning' => true,
|
||||
])
|
||||
->get($this->route . '/' . $account->provisioning_token)
|
||||
->assertStatus(410);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,19 @@ class ApiAccountCreationTokenTest extends TestCase
|
|||
|
||||
$response = $this->keyAuthenticated($admin)
|
||||
->json($this->method, $this->adminRoute)
|
||||
->assertStatus(201);
|
||||
->assertStatus(201)
|
||||
->assertJson(['expire_at' => null]);
|
||||
|
||||
$this->assertDatabaseHas('account_creation_tokens', [
|
||||
'token' => $response->json()['token']
|
||||
]);
|
||||
|
||||
config()->set('app.account_creation_token_expiration_minutes', 10);
|
||||
|
||||
$response = $this->keyAuthenticated($admin)
|
||||
->json($this->method, $this->adminRoute)
|
||||
->assertStatus(201)
|
||||
->assertJson(['expire_at' => AccountCreationToken::latest()->first()->expire_at]);
|
||||
}
|
||||
|
||||
public function testInvalidToken()
|
||||
|
|
@ -103,31 +111,28 @@ class ApiAccountCreationTokenTest extends TestCase
|
|||
$token = AccountCreationToken::factory()->create();
|
||||
|
||||
// Invalid token
|
||||
$response = $this->json($this->method, $this->accountRoute, [
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'username',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => '0123456789abc'
|
||||
]);
|
||||
$response->assertStatus(422);
|
||||
])->assertStatus(422);
|
||||
|
||||
// Valid token
|
||||
$response = $this->json($this->method, $this->accountRoute, [
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'username',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => $token->token
|
||||
]);
|
||||
$response->assertStatus(200);
|
||||
])->assertStatus(200);
|
||||
|
||||
// Expired token
|
||||
$response = $this->json($this->method, $this->accountRoute, [
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'username2',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => $token->token
|
||||
]);
|
||||
$response->assertStatus(422);
|
||||
])->assertStatus(422);
|
||||
|
||||
$this->assertDatabaseHas('account_creation_tokens', [
|
||||
'used' => true,
|
||||
|
|
@ -135,6 +140,21 @@ class ApiAccountCreationTokenTest extends TestCase
|
|||
]);
|
||||
}
|
||||
|
||||
public function testTokenExpiration()
|
||||
{
|
||||
$token = AccountCreationToken::factory()->expired()->create();
|
||||
|
||||
config()->set('app.account_creation_token_expiration_minutes', 10);
|
||||
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'username',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => $token->token
|
||||
])->assertStatus(422)
|
||||
->assertJsonValidationErrors(['account_creation_token']);
|
||||
}
|
||||
|
||||
public function testBlacklistedUsername()
|
||||
{
|
||||
$token = AccountCreationToken::factory()->create();
|
||||
|
|
@ -142,33 +162,28 @@ class ApiAccountCreationTokenTest extends TestCase
|
|||
config()->set('app.blacklisted_usernames', 'foobar,blacklisted,username-.*');
|
||||
|
||||
// Blacklisted username
|
||||
$response = $this->json($this->method, $this->accountRoute, [
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'blacklisted',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => $token->token
|
||||
]);
|
||||
$response->assertJsonValidationErrors(['username']);
|
||||
])->assertJsonValidationErrors(['username']);
|
||||
|
||||
// Blacklisted regex username
|
||||
$response = $this->json($this->method, $this->accountRoute, [
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'username-gnap',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => $token->token
|
||||
]);
|
||||
|
||||
$response->assertJsonValidationErrors(['username']);
|
||||
])->assertJsonValidationErrors(['username']);
|
||||
|
||||
// Valid username
|
||||
$response = $this->json($this->method, $this->accountRoute, [
|
||||
$this->json($this->method, $this->accountRoute, [
|
||||
'username' => 'valid-username',
|
||||
'algorithm' => 'SHA-256',
|
||||
'password' => '123',
|
||||
'account_creation_token' => $token->token
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
])->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testAccountCreationRequestToken()
|
||||
|
|
|
|||
|
|
@ -74,6 +74,29 @@ class ApiAccountEmailChangeTest extends TestCase
|
|||
])->assertJsonValidationErrors(['email']);
|
||||
}
|
||||
|
||||
public function testCodeExpiration()
|
||||
{
|
||||
$account = Account::factory()->withConsumedAccountCreationToken()->create();
|
||||
$account->generateApiKey();
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->json($this->method, $this->route.'/request', [
|
||||
'email' => 'new@email.com'
|
||||
])
|
||||
->assertStatus(200);
|
||||
|
||||
config()->set('app.email_change_code_expiration_minutes', 10);
|
||||
|
||||
EmailChangeCode::where('id', $account->emailChangeCode->id)
|
||||
->update(['created_at' => $account->emailChangeCode->created_at->subMinutes(1000)]);
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->json($this->method, $this->route, [
|
||||
'code' => $account->emailChangeCode->code
|
||||
])
|
||||
->assertStatus(410);
|
||||
}
|
||||
|
||||
public function testUnvalidatedAccount()
|
||||
{
|
||||
$account = Account::factory()->create();
|
||||
|
|
|
|||
|
|
@ -48,6 +48,29 @@ class ApiAccountPhoneChangeTest extends TestCase
|
|||
->assertStatus(200);*/
|
||||
}
|
||||
|
||||
public function testCodeExpiration()
|
||||
{
|
||||
$account = Account::factory()->withConsumedAccountCreationToken()->create();
|
||||
$account->generateApiKey();
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->json($this->method, $this->route.'/request', [
|
||||
'phone' => '+123123'
|
||||
])
|
||||
->assertStatus(200);
|
||||
|
||||
config()->set('app.phone_change_code_expiration_minutes', 10);
|
||||
|
||||
PhoneChangeCode::where('id', $account->phoneChangeCode->id)
|
||||
->update(['created_at' => $account->phoneChangeCode->created_at->subMinutes(1000)]);
|
||||
|
||||
$this->keyAuthenticated($account)
|
||||
->json($this->method, $this->route, [
|
||||
'code' => $account->phoneChangeCode->code
|
||||
])
|
||||
->assertStatus(410);
|
||||
}
|
||||
|
||||
public function testUnvalidatedAccount()
|
||||
{
|
||||
$account = Account::factory()->create();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue