mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-04-17 19:58:27 +00:00
Fix FLEXIAPI-286 Send an account_recovery_token using a push notification
This commit is contained in:
parent
682b0ae67b
commit
cd3b9b818b
26 changed files with 604 additions and 169 deletions
|
|
@ -33,6 +33,7 @@ v1.7
|
|||
- Fix FLEXIAPI-284 Add configurable admin API Keys
|
||||
- Fix FLEXIAPI-232 Add provisioning email + important redesign of the contacts page
|
||||
- Fix FLEXIAPI-287 Refactor the emails templates
|
||||
- Fix FLEXIAPI-286 Send an account_recovery_token using a push notification and protect the account recovery using phone page with the account_recovery_token
|
||||
|
||||
v1.6
|
||||
----
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|||
|
||||
- **Spaces:** A new way to manage your SIP domains and hosts. A Space is defined by a unique SIP Domain and Host pair.
|
||||
- **New mandatory DotEnv variable** `APP_ROOT_HOST`, replaces `APP_URL` and `APP_SIP_DOMAIN` that are now configured using the new dedicated Artisan script. It defines the root hostname where all the Spaces will be configured. All the Spaces will be as subdomains of `APP_ROOT_HOST` except one that can be equal to `APP_ROOT_HOST`. Example: if `APP_ROOT_HOST=myhost.com` the Spaces hosts will be `myhost.com`, `alpha.myhost.com` , `beta.myhost.com`...
|
||||
- **New DotEnv variable:** `APP_ACCOUNT_RECOVERY_TOKEN_EXPIRATION_MINUTES=0` Number of minutes before expiring the recovery tokens
|
||||
- **New Artisan script** `php artisan spaces:create-update {sip_domain} {host} {name} {--super}`, replaces `php artisan sip_domains:create-update {sip_domain} {--super}`. Can create a Space or update a Space Host base on its Space SIP Domain.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Removing and moving DotEnv instance environnement variables to the Spaces** The following DotEnv variables were removed. You can now configure them directly in the designated spaces.
|
||||
- **Removing and moving DotEnv instance environnement variables to the Spaces** The following DotEnv variables were removed. You can now configure them directly in the designated spaces after the migration.
|
||||
- INSTANCE_COPYRIGHT
|
||||
- INSTANCE_INTRO_REGISTRATION
|
||||
- INSTANCE_CUSTOM_THEME
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ APP_DANGEROUS_ENDPOINTS=false # Enable some dangerous endpoints used for XMLRPC
|
|||
# Expiration time for tokens and code, in minutes, 0 means no expiration
|
||||
APP_API_ACCOUNT_CREATION_TOKEN_RETRY_MINUTES=60 # Number of minutes between two consecutive account_creation_token creation
|
||||
APP_ACCOUNT_CREATION_TOKEN_EXPIRATION_MINUTES=0
|
||||
APP_ACCOUNT_RECOVERY_TOKEN_EXPIRATION_MINUTES=0
|
||||
APP_EMAIL_CHANGE_CODE_EXPIRATION_MINUTES=10
|
||||
APP_PHONE_CHANGE_CODE_EXPIRATION_MINUTES=10
|
||||
APP_RECOVERY_CODE_EXPIRATION_MINUTES=10
|
||||
|
|
|
|||
|
|
@ -256,6 +256,11 @@ class Account extends Authenticatable
|
|||
return $this->hasOne(AccountCreationToken::class);
|
||||
}
|
||||
|
||||
public function accountRecoveryTokens()
|
||||
{
|
||||
return $this->hasMany(AccountRecoveryToken::class);
|
||||
}
|
||||
|
||||
public function authTokens()
|
||||
{
|
||||
return $this->hasMany(AuthToken::class);
|
||||
|
|
@ -385,10 +390,12 @@ class Account extends Authenticatable
|
|||
return $authToken;
|
||||
}
|
||||
|
||||
public function recover(?string $code = null): string
|
||||
public function recover(?string $code = null, ?string $phone = null, ?string $email = null): string
|
||||
{
|
||||
$recoveryCode = new RecoveryCode;
|
||||
$recoveryCode->code = $code ?? generatePin();
|
||||
$recoveryCode->phone = $phone;
|
||||
$recoveryCode->email = $email;
|
||||
$recoveryCode->account_id = $this->id;
|
||||
|
||||
if (request()) {
|
||||
|
|
|
|||
42
flexiapi/app/AccountRecoveryToken.php
Normal file
42
flexiapi/app/AccountRecoveryToken.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class AccountRecoveryToken extends Consommable
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $hidden = ['id', 'updated_at', 'created_at'];
|
||||
protected $appends = ['expire_at'];
|
||||
protected ?string $configExpirationMinutesKey = 'account_recovery_token_expiration_minutes';
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
public function consume()
|
||||
{
|
||||
$this->used = true;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function consumed(): bool
|
||||
{
|
||||
return $this->used == true;
|
||||
}
|
||||
|
||||
public function toLog()
|
||||
{
|
||||
return [
|
||||
'token' => $this->token,
|
||||
'pn_param' => $this->pn_param,
|
||||
'used' => $this->used,
|
||||
'account_id' => $this->account_id,
|
||||
'ip' => $this->ip,
|
||||
'user_agent' => $this->user_agent,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -20,12 +20,16 @@
|
|||
namespace App\Http\Controllers\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountRecoveryToken;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\AccountService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
|
||||
class RecoveryController extends Controller
|
||||
{
|
||||
public function showEmail(Request $request)
|
||||
|
|
@ -36,10 +40,16 @@ class RecoveryController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function showPhone(Request $request)
|
||||
public function showPhone(Request $request, string $accountRecoveryToken)
|
||||
{
|
||||
$accountRecoveryToken = AccountRecoveryToken::where('token', $accountRecoveryToken)
|
||||
->where('used', false)
|
||||
->firstOrFail();
|
||||
|
||||
return view('account.recovery.show', [
|
||||
'method' => 'phone',
|
||||
'account_recovery_token' => $accountRecoveryToken->token,
|
||||
'phone' => $request->get('phone'),
|
||||
'domain' => resolveDomain($request)
|
||||
]);
|
||||
}
|
||||
|
|
@ -49,7 +59,8 @@ class RecoveryController extends Controller
|
|||
$rules = [
|
||||
'email' => 'required_without:phone|email|exists:accounts,email',
|
||||
'phone' => 'required_without:email|starts_with:+',
|
||||
'h-captcha-response' => captchaConfigured() ? 'required|HCaptcha' : '',
|
||||
'h-captcha-response' => captchaConfigured() ? 'required_if:email|HCaptcha' : '',
|
||||
'account_recovery_token' => 'required_if:phone',
|
||||
];
|
||||
|
||||
$account = null;
|
||||
|
|
@ -94,9 +105,17 @@ class RecoveryController extends Controller
|
|||
}
|
||||
|
||||
if ($request->get('email')) {
|
||||
$account = (new AccountService)->recoverByEmail($account);
|
||||
$account = (new AccountService)->recoverByEmail($account, $request->get('email'));
|
||||
} elseif ($request->get('phone')) {
|
||||
$account = (new AccountService)->recoverByPhone($account);
|
||||
$accountRecoveryToken = AccountRecoveryToken::where('token', $request->get('account_recovery_token'))
|
||||
->where('used', false)
|
||||
->first();
|
||||
|
||||
if (!$accountRecoveryToken) {
|
||||
abort(403, 'Wrong Account Recovery Token');
|
||||
}
|
||||
|
||||
$account = (new AccountService)->recoverByPhone($account, $request->get('phone'), $accountRecoveryToken);
|
||||
}
|
||||
|
||||
return view('account.recovery.confirm', [
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ProvisioningEmailController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function send(Request $request, int $accountId)
|
||||
public function send(int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account->provision();
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class CreationTokenController extends Controller
|
|||
|
||||
$fp = new FlexisipPusherConnector($token->pn_provider, $token->pn_param, $token->pn_prid);
|
||||
if ($fp->sendToken($token->token)) {
|
||||
Log::channel('events')->info('API: Token sent', ['token' => $token->token]);
|
||||
Log::channel('events')->info('API: Account Creation Token sent', ['token' => $token->token]);
|
||||
|
||||
$token->save();
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Account;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use App\AccountRecoveryToken;
|
||||
use App\Rules\PnParam;
|
||||
use App\Rules\PnPrid;
|
||||
use App\Rules\PnProvider;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
use App\Libraries\FlexisipPusherConnector;
|
||||
|
||||
class RecoveryTokenController extends Controller
|
||||
{
|
||||
public function sendByPush(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'pn_provider' => ['required', new PnProvider],
|
||||
'pn_param' => [new PnParam],
|
||||
'pn_prid' => [new PnPrid],
|
||||
]);
|
||||
|
||||
$last = AccountRecoveryToken::where('pn_provider', $request->get('pn_provider'))
|
||||
->where('pn_param', $request->get('pn_param'))
|
||||
->where('pn_prid', $request->get('pn_prid'))
|
||||
->where('created_at', '>=', Carbon::now()->subMinutes(config('app.account_recovery_token_retry_minutes'))->toDateTimeString())
|
||||
->where('used', true)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if ($last) {
|
||||
Log::channel('events')->info('API: Token throttled', ['token' => $last->token]);
|
||||
abort(429, 'Last token requested too recently');
|
||||
}
|
||||
|
||||
$token = new AccountRecoveryToken;
|
||||
$token->token = Str::random(WebAuthenticateController::$emailCodeSize);
|
||||
$token->pn_provider = $request->get('pn_provider');
|
||||
$token->pn_param = $request->get('pn_param');
|
||||
$token->pn_prid = $request->get('pn_prid');
|
||||
$token->fillRequestInfo($request);
|
||||
|
||||
$fp = new FlexisipPusherConnector($token->pn_provider, $token->pn_param, $token->pn_prid);
|
||||
if ($fp->sendToken($token->token)) {
|
||||
Log::channel('events')->info('API: AccountRecoveryToken sent', ['token' => $token->token]);
|
||||
|
||||
$token->save();
|
||||
return;
|
||||
}
|
||||
|
||||
abort(503, "Token not sent");
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ namespace App\Services;
|
|||
|
||||
use App\Account;
|
||||
use App\AccountCreationToken;
|
||||
use App\AccountRecoveryToken;
|
||||
use App\EmailChangeCode;
|
||||
use App\Http\Requests\Account\Create\Request as CreateRequest;
|
||||
use App\Http\Requests\Account\Update\Request as UpdateRequest;
|
||||
|
|
@ -220,7 +221,7 @@ class AccountService
|
|||
|
||||
$account = $request->user();
|
||||
|
||||
$phoneChangeCode = $account->phoneChangeCode ?? new PhoneChangeCode();
|
||||
$phoneChangeCode = new PhoneChangeCode();
|
||||
$phoneChangeCode->account_id = $account->id;
|
||||
$phoneChangeCode->phone = $request->get('phone');
|
||||
$phoneChangeCode->code = generatePin();
|
||||
|
|
@ -255,7 +256,7 @@ class AccountService
|
|||
|
||||
$account = $request->user();
|
||||
|
||||
$phoneChangeCode = $account->phoneChangeCode()->firstOrFail();
|
||||
$phoneChangeCode = $account->phoneChangeCodes()->firstOrFail();
|
||||
|
||||
if ($phoneChangeCode->expired()) {
|
||||
return abort(410, 'Expired code');
|
||||
|
|
@ -299,7 +300,7 @@ class AccountService
|
|||
|
||||
$account = $request->user();
|
||||
|
||||
$emailChangeCode = $account->emailChangeCode ?? new EmailChangeCode();
|
||||
$emailChangeCode = new EmailChangeCode();
|
||||
$emailChangeCode->account_id = $account->id;
|
||||
$emailChangeCode->email = $request->get('email');
|
||||
$emailChangeCode->code = generatePin();
|
||||
|
|
@ -327,7 +328,7 @@ class AccountService
|
|||
|
||||
$account = $request->user();
|
||||
|
||||
$emailChangeCode = $account->emailChangeCode()->firstOrFail();
|
||||
$emailChangeCode = $account->emailChangeCodes()->firstOrFail();
|
||||
|
||||
if ($emailChangeCode->expired()) {
|
||||
return abort(410, 'Expired code');
|
||||
|
|
@ -360,9 +361,11 @@ class AccountService
|
|||
* Account recovery
|
||||
*/
|
||||
|
||||
public function recoverByEmail(Account $account): Account
|
||||
public function recoverByEmail(Account $account, string $email): Account
|
||||
{
|
||||
$account = $this->recoverAccount($account);
|
||||
$account->recover(email: $email);
|
||||
$account->provision();
|
||||
$account->refresh();
|
||||
|
||||
Mail::to($account)->send(new RecoverByCode($account));
|
||||
|
||||
|
|
@ -371,9 +374,11 @@ class AccountService
|
|||
return $account;
|
||||
}
|
||||
|
||||
public function recoverByPhone(Account $account): Account
|
||||
public function recoverByPhone(Account $account, string $phone, AccountRecoveryToken $accountRecoveryToken): Account
|
||||
{
|
||||
$account = $this->recoverAccount($account);
|
||||
$account->recover(phone: $phone);
|
||||
$account->provision();
|
||||
$account->refresh();
|
||||
|
||||
$message = 'Your ' . $account->space->name . ' validation code is ' . $account->recovery_code . '.';
|
||||
|
||||
|
|
@ -386,14 +391,11 @@ class AccountService
|
|||
|
||||
Log::channel('events')->info('Account Service: Sending recovery SMS', ['id' => $account->identifier]);
|
||||
|
||||
return $account;
|
||||
}
|
||||
$accountRecoveryToken->consume();
|
||||
$accountRecoveryToken->account_id = $account->id;
|
||||
$accountRecoveryToken->save();
|
||||
|
||||
private function recoverAccount(Account $account): Account
|
||||
{
|
||||
$account->recover();
|
||||
$account->provision();
|
||||
$account->refresh();
|
||||
Log::channel('events')->info('API: AccountRecoveryToken redeemed', ['account_recovery_token' => $accountRecoveryToken->toLog()]);
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
|
|
|||
251
flexiapi/composer.lock
generated
251
flexiapi/composer.lock
generated
|
|
@ -1324,16 +1324,16 @@
|
|||
},
|
||||
{
|
||||
"name": "giggsey/libphonenumber-for-php-lite",
|
||||
"version": "9.0.3",
|
||||
"version": "9.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/giggsey/libphonenumber-for-php-lite.git",
|
||||
"reference": "e4f4c5834d35773bf9d5e72e3e764cfe4e301a8c"
|
||||
"reference": "6b36e32fddce37738c4f6df66e49dd9a2475841c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/e4f4c5834d35773bf9d5e72e3e764cfe4e301a8c",
|
||||
"reference": "e4f4c5834d35773bf9d5e72e3e764cfe4e301a8c",
|
||||
"url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/6b36e32fddce37738c4f6df66e49dd9a2475841c",
|
||||
"reference": "6b36e32fddce37738c4f6df66e49dd9a2475841c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1398,7 +1398,7 @@
|
|||
"issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues",
|
||||
"source": "https://github.com/giggsey/libphonenumber-for-php-lite"
|
||||
},
|
||||
"time": "2025-04-11T07:36:14+00:00"
|
||||
"time": "2025-04-28T07:26:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "graham-campbell/result-type",
|
||||
|
|
@ -2405,16 +2405,16 @@
|
|||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
"version": "2.6.2",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/commonmark.git",
|
||||
"reference": "06c3b0bf2540338094575612f4a1778d0d2d5e94"
|
||||
"reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/06c3b0bf2540338094575612f4a1778d0d2d5e94",
|
||||
"reference": "06c3b0bf2540338094575612f4a1778d0d2d5e94",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
|
||||
"reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2451,7 +2451,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.7-dev"
|
||||
"dev-main": "2.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -2508,7 +2508,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-18T21:09:27+00:00"
|
||||
"time": "2025-05-05T12:20:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/config",
|
||||
|
|
@ -2950,16 +2950,16 @@
|
|||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.13.0",
|
||||
"version": "1.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
|
||||
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
|
||||
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2998,7 +2998,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3006,7 +3006,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-12T12:17:51+00:00"
|
||||
"time": "2025-04-29T12:36:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "namoshek/laravel-redis-sentinel",
|
||||
|
|
@ -4096,16 +4096,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "10.5.45",
|
||||
"version": "10.5.46",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8"
|
||||
"reference": "8080be387a5be380dda48c6f41cee4a13aadab3d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8",
|
||||
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8080be387a5be380dda48c6f41cee4a13aadab3d",
|
||||
"reference": "8080be387a5be380dda48c6f41cee4a13aadab3d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4115,7 +4115,7 @@
|
|||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"myclabs/deep-copy": "^1.12.1",
|
||||
"myclabs/deep-copy": "^1.13.1",
|
||||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.1",
|
||||
|
|
@ -4177,7 +4177,7 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.46"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -4188,12 +4188,20 @@
|
|||
"url": "https://github.com/sebastianbergmann",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://liberapay.com/sebastianbergmann",
|
||||
"type": "liberapay"
|
||||
},
|
||||
{
|
||||
"url": "https://thanks.dev/u/gh/sebastianbergmann",
|
||||
"type": "thanks_dev"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-06T16:08:12+00:00"
|
||||
"time": "2025-05-02T06:46:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "propaganistas/laravel-phone",
|
||||
|
|
@ -6818,16 +6826,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v6.4.20",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "2e4af9c952617cc3f9559ff706aee420a8464c36"
|
||||
"reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/2e4af9c952617cc3f9559ff706aee420a8464c36",
|
||||
"reference": "2e4af9c952617cc3f9559ff706aee420a8464c36",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/a3011c7b7adb58d89f6c0d822abb641d7a5f9719",
|
||||
"reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -6892,7 +6900,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v6.4.20"
|
||||
"source": "https://github.com/symfony/console/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6908,7 +6916,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-03T17:16:38+00:00"
|
||||
"time": "2025-04-07T15:42:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
|
|
@ -7339,16 +7347,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v6.4.18",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "d0492d6217e5ab48f51fca76f64cf8e78919d0db"
|
||||
"reference": "3f0c7ea41db479383b81d436b836d37168fd5b99"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0492d6217e5ab48f51fca76f64cf8e78919d0db",
|
||||
"reference": "d0492d6217e5ab48f51fca76f64cf8e78919d0db",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f0c7ea41db479383b81d436b836d37168fd5b99",
|
||||
"reference": "3f0c7ea41db479383b81d436b836d37168fd5b99",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7396,7 +7404,7 @@
|
|||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v6.4.18"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7412,20 +7420,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-09T15:48:56+00:00"
|
||||
"time": "2025-04-27T13:27:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v6.4.20",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "6be6db31bc74693ce5516e1fd5e5ff1171005e37"
|
||||
"reference": "983ca05eec6623920d24ec0f1005f487d3734a0c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/6be6db31bc74693ce5516e1fd5e5ff1171005e37",
|
||||
"reference": "6be6db31bc74693ce5516e1fd5e5ff1171005e37",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/983ca05eec6623920d24ec0f1005f487d3734a0c",
|
||||
"reference": "983ca05eec6623920d24ec0f1005f487d3734a0c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7510,7 +7518,7 @@
|
|||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v6.4.20"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7526,20 +7534,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-28T13:27:10+00:00"
|
||||
"time": "2025-05-02T08:46:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
"version": "v6.4.18",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mailer.git",
|
||||
"reference": "e93a6ae2767d7f7578c2b7961d9d8e27580b2b11"
|
||||
"reference": "ada2809ccd4ec27aba9fc344e3efdaec624c6438"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/e93a6ae2767d7f7578c2b7961d9d8e27580b2b11",
|
||||
"reference": "e93a6ae2767d7f7578c2b7961d9d8e27580b2b11",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/ada2809ccd4ec27aba9fc344e3efdaec624c6438",
|
||||
"reference": "ada2809ccd4ec27aba9fc344e3efdaec624c6438",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7590,7 +7598,7 @@
|
|||
"description": "Helps sending emails",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mailer/tree/v6.4.18"
|
||||
"source": "https://github.com/symfony/mailer/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7606,20 +7614,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-24T15:27:15+00:00"
|
||||
"time": "2025-04-26T23:47:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
"version": "v6.4.19",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mime.git",
|
||||
"reference": "ac537b6c55ccc2c749f3c979edfa9ec14aaed4f3"
|
||||
"reference": "fec8aa5231f3904754955fad33c2db50594d22d1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/ac537b6c55ccc2c749f3c979edfa9ec14aaed4f3",
|
||||
"reference": "ac537b6c55ccc2c749f3c979edfa9ec14aaed4f3",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/fec8aa5231f3904754955fad33c2db50594d22d1",
|
||||
"reference": "fec8aa5231f3904754955fad33c2db50594d22d1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7675,7 +7683,7 @@
|
|||
"mime-type"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mime/tree/v6.4.19"
|
||||
"source": "https://github.com/symfony/mime/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7691,11 +7699,11 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-17T21:23:52+00:00"
|
||||
"time": "2025-04-27T13:27:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
|
|
@ -7754,7 +7762,7 @@
|
|||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7774,7 +7782,7 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
|
|
@ -7832,7 +7840,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7852,16 +7860,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773"
|
||||
"reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773",
|
||||
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
|
||||
"reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7915,7 +7923,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7931,11 +7939,11 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
"time": "2024-09-10T14:38:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
|
|
@ -7996,7 +8004,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8016,19 +8024,20 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-iconv": "*",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
|
|
@ -8076,7 +8085,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8092,20 +8101,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
"time": "2024-12-23T08:48:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
|
||||
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8156,7 +8165,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8172,11 +8181,11 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
"time": "2025-01-02T08:10:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php83",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php83.git",
|
||||
|
|
@ -8232,7 +8241,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8252,7 +8261,7 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-uuid",
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-uuid.git",
|
||||
|
|
@ -8311,7 +8320,7 @@
|
|||
"uuid"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0"
|
||||
"source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8558,16 +8567,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v6.4.15",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f"
|
||||
"reference": "73e2c6966a5aef1d4892873ed5322245295370c6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f",
|
||||
"reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6",
|
||||
"reference": "73e2c6966a5aef1d4892873ed5322245295370c6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8624,7 +8633,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v6.4.15"
|
||||
"source": "https://github.com/symfony/string/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8640,20 +8649,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-13T13:31:12+00:00"
|
||||
"time": "2025-04-18T15:23:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v6.4.19",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "3b9bf9f33997c064885a7bfc126c14b9daa0e00e"
|
||||
"reference": "bb92ea5588396b319ba43283a5a3087a034cb29c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/3b9bf9f33997c064885a7bfc126c14b9daa0e00e",
|
||||
"reference": "3b9bf9f33997c064885a7bfc126c14b9daa0e00e",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/bb92ea5588396b319ba43283a5a3087a034cb29c",
|
||||
"reference": "bb92ea5588396b319ba43283a5a3087a034cb29c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8719,7 +8728,7 @@
|
|||
"description": "Provides tools to internationalize your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation/tree/v6.4.19"
|
||||
"source": "https://github.com/symfony/translation/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8735,7 +8744,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-13T10:18:43+00:00"
|
||||
"time": "2025-04-07T19:02:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
|
|
@ -8891,16 +8900,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v6.4.18",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "4ad10cf8b020e77ba665305bb7804389884b4837"
|
||||
"reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/4ad10cf8b020e77ba665305bb7804389884b4837",
|
||||
"reference": "4ad10cf8b020e77ba665305bb7804389884b4837",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/22560f80c0c5cd58cc0bcaf73455ffd81eb380d5",
|
||||
"reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8956,7 +8965,7 @@
|
|||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v6.4.18"
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8972,7 +8981,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-17T11:26:11+00:00"
|
||||
"time": "2025-04-09T07:34:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
|
@ -9081,16 +9090,16 @@
|
|||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
"version": "v5.6.1",
|
||||
"version": "v5.6.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||
"reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2"
|
||||
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2",
|
||||
"reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
|
||||
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -9149,7 +9158,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1"
|
||||
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -9161,7 +9170,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-20T21:52:34+00:00"
|
||||
"time": "2025-04-30T23:37:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "voku/portable-ascii",
|
||||
|
|
@ -9515,20 +9524,20 @@
|
|||
},
|
||||
{
|
||||
"name": "hamcrest/hamcrest-php",
|
||||
"version": "v2.0.1",
|
||||
"version": "v2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/hamcrest/hamcrest-php.git",
|
||||
"reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
|
||||
"reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
|
||||
"reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
|
||||
"url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
|
||||
"reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3|^7.0|^8.0"
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"cordoval/hamcrest-php": "*",
|
||||
|
|
@ -9536,8 +9545,8 @@
|
|||
"kodova/hamcrest-php": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/php-file-iterator": "^1.4 || ^2.0",
|
||||
"phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
|
||||
"phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
|
||||
"phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
|
@ -9560,9 +9569,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/hamcrest/hamcrest-php/issues",
|
||||
"source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
|
||||
"source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
|
||||
},
|
||||
"time": "2020-07-09T08:09:16+00:00"
|
||||
"time": "2025-04-30T06:54:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
|
|
@ -10197,16 +10206,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/var-exporter",
|
||||
"version": "v6.4.20",
|
||||
"version": "v6.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-exporter.git",
|
||||
"reference": "998df255e9e6a15a36ae35e9c6cd818c17cf92a2"
|
||||
"reference": "717e7544aa99752c54ecba5c0e17459c48317472"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/998df255e9e6a15a36ae35e9c6cd818c17cf92a2",
|
||||
"reference": "998df255e9e6a15a36ae35e9c6cd818c17cf92a2",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/717e7544aa99752c54ecba5c0e17459c48317472",
|
||||
"reference": "717e7544aa99752c54ecba5c0e17459c48317472",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -10254,7 +10263,7 @@
|
|||
"serialize"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-exporter/tree/v6.4.20"
|
||||
"source": "https://github.com/symfony/var-exporter/tree/v6.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -10270,7 +10279,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-13T09:55:08+00:00"
|
||||
"time": "2025-04-27T21:06:26+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -31,6 +31,7 @@ return [
|
|||
*/
|
||||
'api_key_expiration_minutes' => env('APP_API_KEY_EXPIRATION_MINUTES', 60),
|
||||
'account_creation_token_expiration_minutes' => env('APP_ACCOUNT_CREATION_TOKEN_EXPIRATION_MINUTES', 0),
|
||||
'account_recovery_token_expiration_minutes' => env('APP_ACCOUNT_RECOVERY_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),
|
||||
|
|
|
|||
53
flexiapi/database/factories/AccountRecoveryTokenFactory.php
Normal file
53
flexiapi/database/factories/AccountRecoveryTokenFactory.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2021 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\AccountRecoveryToken;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class AccountRecoveryTokenFactory extends Factory
|
||||
{
|
||||
protected $model = AccountRecoveryToken::class;
|
||||
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'pn_provider' => $this->faker->uuid(),
|
||||
'pn_param' => $this->faker->uuid(),
|
||||
'pn_prid' => $this->faker->uuid(),
|
||||
'token' => Str::random(WebAuthenticateController::$emailCodeSize),
|
||||
'used' => false,
|
||||
'ip' => $this->faker->ipv4(),
|
||||
'user_agent' => $this->faker->userAgent(),
|
||||
'created_at' => Carbon::now()
|
||||
];
|
||||
}
|
||||
|
||||
public function expired()
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'created_at' => Carbon::now()->subMinutes(1000)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('recovery_codes', function (Blueprint $table) {
|
||||
$table->string('phone')->nullable();
|
||||
$table->string('email')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('account_recovery_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('token');
|
||||
$table->string('pn_provider');
|
||||
$table->string('pn_param');
|
||||
$table->string('pn_prid');
|
||||
$table->boolean('used')->default(false);
|
||||
$table->string('ip')->nullable();
|
||||
$table->string('user_agent')->nullable();
|
||||
$table->integer('account_id')->unsigned()->nullable();
|
||||
$table->foreign('account_id')->references('id')
|
||||
->on('accounts')->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('token');
|
||||
$table->index(['pn_provider', 'pn_param', 'pn_prid']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('recovery_codes', function (Blueprint $table) {
|
||||
$table->dropColumn('phone');
|
||||
$table->dropColumn('email');
|
||||
});
|
||||
|
||||
Schema::dropIfExists('account_recovery_tokens');
|
||||
}
|
||||
};
|
||||
|
|
@ -141,7 +141,7 @@
|
|||
"Realm": "Royaume",
|
||||
"Record calls": "Enregistrements des appels",
|
||||
"Recover your account using your email": "Récupérer votre compte avec votre email",
|
||||
"Recover your account using your phone number": "Récupérer votre compte avec votre numéro de téléphone",
|
||||
"Use the mobile app to recover your account using your phone number": "Utilisez l'application mobile pour récupérer votre compte avec votre numéro de téléphone",
|
||||
"Register": "Inscription",
|
||||
"Registrar": "Registrar",
|
||||
"Registration introduction": "Présentation lors de l'inscription",
|
||||
|
|
@ -198,6 +198,7 @@
|
|||
"Username": "Identifiant",
|
||||
"Value": "Valeur",
|
||||
"Verify": "Vérifier",
|
||||
"Via": "Via",
|
||||
"Week": "Semaine",
|
||||
"Welcome on :app_name": "Bienvenue sur :app_name",
|
||||
"Wrong username or password": "Mauvais identifiant ou mot de passe",
|
||||
|
|
|
|||
|
|
@ -39,14 +39,18 @@
|
|||
</p>
|
||||
@endif
|
||||
<div>
|
||||
<input placeholder="+123456789" name="phone" type="text" value="{{ old('phone') }}">
|
||||
<input placeholder="+123456789" name="phone" type="text" value="@if ($phone){{ $phone }}@else{{ old('phone') }}@endif">
|
||||
<label for="phone">{{ __('Phone number') }}</label>
|
||||
@include('parts.errors', ['name' => 'phone'])
|
||||
@include('parts.errors', ['name' => 'identifier'])
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include('parts.captcha')
|
||||
@if (!empty($account_recovery_token))
|
||||
<input name="account_recovery_token" type="hidden" value="{{ $account_recovery_token }}">
|
||||
@else
|
||||
@include('parts.captcha')
|
||||
@endif
|
||||
|
||||
<div class="large">
|
||||
<input class="btn oppose" type="submit" value="{{ __('Send')}}">
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
@if ($account->recoveryCodes->isNotEmpty())
|
||||
<div class="card">
|
||||
<h3>Recovery Codes</h3>
|
||||
@if ($account->accountRecoveryTokens)
|
||||
<div class="card large">
|
||||
<h3>Account Recovery Tokens</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -84,11 +84,55 @@
|
|||
<th>{{ __('Used on') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($account->accountRecoveryTokens as $key => $accountRecoveryToken)
|
||||
<tr>
|
||||
<td>
|
||||
{{ $accountRecoveryToken->created_at }}
|
||||
<small @if ($accountRecoveryToken->consumed())class="crossed"@endif>
|
||||
{{ __('Token') }}: {{ $accountRecoveryToken->token }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ $accountRecoveryToken->created_at != $accountRecoveryToken->updated_at ? $accountRecoveryToken->updated_at : '-' }}
|
||||
<small title="{{ $accountRecoveryToken->user_agent }}">
|
||||
IP: {{ $accountRecoveryToken->ip ?? '-' }} |
|
||||
{{ \Illuminate\Support\Str::limit($accountRecoveryToken->user_agent, 20, $end='...') }}
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($account->recoveryCodes->isNotEmpty())
|
||||
<div class="card large">
|
||||
<h3>Recovery Codes</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Created') }}</th>
|
||||
<th>{{ __('Via') }} <i class="ph">phone</i>/<i class="ph">envelope</i></th>
|
||||
<th>{{ __('Used on') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($account->recoveryCodes as $key => $recoveryCode)
|
||||
<tr>
|
||||
<td>
|
||||
{{ $recoveryCode->created_at }}
|
||||
<small @if ($recoveryCode->consumed())class="crossed"@endif>
|
||||
{{ __('Code') }}: {{ $recoveryCode->code ?? '-' }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
@if ($recoveryCode->phone)
|
||||
<i class="ph">phone</i> {{ $recoveryCode->phone }}
|
||||
@elseif($recoveryCode->email)
|
||||
<i class="ph">envelope</i> {{ $recoveryCode->email }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
{{ $recoveryCode->created_at != $recoveryCode->updated_at ? $recoveryCode->updated_at : '-' }}
|
||||
|
|
@ -120,10 +164,12 @@
|
|||
<tr>
|
||||
<td>
|
||||
{{ $phoneChangeCode->created_at }}
|
||||
<small @if ($phoneChangeCode->consumed())class="crossed"@endif>
|
||||
{{ __('Code') }}: {{ $phoneChangeCode->code ?? '-' }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ $phoneChangeCode->phone }}
|
||||
<small>{{ __('Code') }}: {{ $phoneChangeCode->code ?? '-' }}</small>
|
||||
</td>
|
||||
<td title="{{ $phoneChangeCode->user_agent }}">
|
||||
{{ $phoneChangeCode->created_at != $phoneChangeCode->updated_at ? $phoneChangeCode->updated_at : '-' }}
|
||||
|
|
@ -155,10 +201,12 @@
|
|||
<tr>
|
||||
<td>
|
||||
{{ $emailChangeCode->created_at }}
|
||||
<small @if ($emailChangeCode->consumed())class="crossed"@endif>
|
||||
{{ __('Code') }}: {{ $emailChangeCode->code ?? '-' }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ $emailChangeCode->email }}
|
||||
<small>{{ __('Code') }}: {{ $emailChangeCode->code ?? '-' }}</small>
|
||||
</td>
|
||||
<td title="{{ $emailChangeCode->user_agent }}">
|
||||
{{ $emailChangeCode->created_at != $emailChangeCode->updated_at ? $emailChangeCode->updated_at : '-' }}
|
||||
|
|
@ -189,6 +237,9 @@
|
|||
<tr>
|
||||
<td>
|
||||
{{ $provisioningToken->created_at }}
|
||||
<small @if ($provisioningToken->offed())class="crossed"@endif>
|
||||
{{ $provisioningToken->token }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ $provisioningToken->consumed() ? $provisioningToken->updated_at : '-' }}
|
||||
|
|
@ -205,7 +256,7 @@
|
|||
@endif
|
||||
|
||||
@if ($account->resetPasswordEmailTokens->isNotEmpty())
|
||||
<div class="card">
|
||||
<div class="card large">
|
||||
<h3>{{ __('Reset password emails') }}</h3>
|
||||
<table>
|
||||
<thead>
|
||||
|
|
@ -220,12 +271,12 @@
|
|||
<tr>
|
||||
<td>
|
||||
{{ $resetPasswordEmailToken->created_at }}
|
||||
<small @if ($resetPasswordEmailToken->offed())class="crossed"@endif>
|
||||
{{ $resetPasswordEmailToken->token }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ $resetPasswordEmailToken->email }}
|
||||
<small>
|
||||
{{ $resetPasswordEmailToken->token }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ $resetPasswordEmailToken->consumed() ? $resetPasswordEmailToken->updated_at : '-' }}
|
||||
|
|
|
|||
|
|
@ -121,15 +121,6 @@ You can find more documentation on the related [IETF RFC-7616](https://tools.iet
|
|||
|
||||
Returns `pong`
|
||||
|
||||
## Account Creation Request Tokens
|
||||
|
||||
An `account_creation_request_token` is a unique token that can be validated and then used to generate a valid `account_creation_token`.
|
||||
|
||||
### `POST /account_creation_request_tokens`
|
||||
<span class="badge badge-success">Public</span>
|
||||
|
||||
Create and return an `account_creation_request_token` that should then be validated to be used.
|
||||
|
||||
## Spaces
|
||||
|
||||
Manage the list of allowed `spaces`. The admin accounts declared with a `domain` that is a `super` `sip_domain` will become <span class="badge badge-error">Super Admin</span>.
|
||||
|
|
@ -249,6 +240,15 @@ JSON parameters:
|
|||
|
||||
Delete the a space email server configuration.
|
||||
|
||||
## Account Creation Request Tokens
|
||||
|
||||
An `account_creation_request_token` is a unique token that can be validated and then used to generate a valid `account_creation_token`.
|
||||
|
||||
### `POST /account_creation_request_tokens`
|
||||
<span class="badge badge-success">Public</span>
|
||||
|
||||
Create and return an `account_creation_request_token` that should then be validated to be used.
|
||||
|
||||
## Account Creation Tokens
|
||||
|
||||
An `account_creation_token` is a unique token that allow the creation or the validation of a unique account.
|
||||
|
|
@ -294,6 +294,28 @@ JSON parameters:
|
|||
|
||||
Create and return an `account_creation_token`.
|
||||
|
||||
## Account Recovery Tokens
|
||||
|
||||
An `account_recovery_token` is a unique token that allow the recovery of an account.
|
||||
|
||||
It can be used on the following page that also accepts a `phone` optional parameter to prefil the recovery form:
|
||||
|
||||
{{ route('account.recovery.show.phone', ['account_recovery_token' => '_the_token_']) }}
|
||||
{{ route('account.recovery.show.phone', ['account_recovery_token' => '_the_token_', 'phone' => '+3312341234']) }}
|
||||
|
||||
### `POST /account_recovery_tokens/send-by-push`
|
||||
<span class="badge badge-success">Public</span>
|
||||
|
||||
Create and send an `account_recovery_token` using a push notification to the device.
|
||||
Return `403` if a token was already sent, or if the tokens limit is reached for this device.
|
||||
Return `503` if the token was not successfully sent.
|
||||
|
||||
JSON parameters:
|
||||
|
||||
* `pn_provider` **required**, the push notification provider, must be in apns.dev, apns or fcm
|
||||
* `pn_param` the push notification parameter, can be null or contain only alphanumeric and underscore characters
|
||||
* `pn_prid` the push notification unique id, can be null or contain only alphanumeric, dashes and colon characters
|
||||
|
||||
## Auth Tokens
|
||||
|
||||
### `POST /accounts/auth_token`
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ You are trying to authenticate to {{ $account->space->name }} using your email a
|
|||
|
||||
Please enter the code bellow to finish the authentication process.
|
||||
|
||||
## {{ $account->recovery_code }
|
||||
## {{ $account->recovery_code }}
|
||||
|
||||
@if (config('app.recovery_code_expiration_minutes') > 0)
|
||||
The code is only available {{ config('app.recovery_code_expiration_minutes') }} minutes.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ You are trying to authenticate to {{ $account->space->name }} using your email a
|
|||
|
||||
Please enter the code bellow to finish the authentication process.
|
||||
|
||||
## {{ $account->recovery_code }
|
||||
## {{ $account->recovery_code }}
|
||||
|
||||
@if (config('app.recovery_code_expiration_minutes') > 0)
|
||||
The code is only available {{ config('app.recovery_code_expiration_minutes') }} minutes.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<p class="text-center pt-3">
|
||||
<i class="ph">envelope</i><a href="{{ route('account.recovery.show.email') }}">{{ __('Recover your account using your email') }}</a><br />
|
||||
@if (space()->phone_registration)
|
||||
<i class="ph">phone</i><a href="{{ route('account.recovery.show.phone') }}">{{ __('Recover your account using your phone number') }}</a><br />
|
||||
<i class="ph">phone</i>{{ __('Use the mobile app to recover your account using your phone number') }}<br />
|
||||
@endif
|
||||
<i class="ph">qr-code</i><a href="{{ route('account.authenticate.auth_token') }}">{{ __('Login using a QRCode') }}</a>
|
||||
</p>
|
||||
|
|
@ -44,6 +44,7 @@ Route::post('account_creation_request_tokens', 'Api\Account\CreationRequestToken
|
|||
Route::post('account_creation_tokens/send-by-push', 'Api\Account\CreationTokenController@sendByPush');
|
||||
Route::post('account_creation_tokens/using-account-creation-request-token', 'Api\Account\CreationTokenController@usingAccountRequestToken');
|
||||
Route::post('accounts/with-account-creation-token', 'Api\Account\AccountController@store');
|
||||
Route::post('account_recovery_tokens/send-by-push', 'Api\Account\RecoveryTokenController@sendByPush');
|
||||
|
||||
Route::get('accounts/{sip}/info', 'Api\Account\AccountController@info');
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ Route::middleware(['web_panel_enabled', 'space.check'])->group(function () {
|
|||
});
|
||||
|
||||
Route::prefix('recovery')->controller(RecoveryController::class)->group(function () {
|
||||
Route::get('phone', 'showPhone')->name('account.recovery.show.phone');
|
||||
Route::get('phone/{account_recovery_token}', 'showPhone')->name('account.recovery.show.phone');
|
||||
Route::get('email', 'showEmail')->name('account.recovery.show.email');
|
||||
Route::post('/', 'send')->name('account.recovery.send');
|
||||
Route::post('confirm', 'confirm')->name('account.recovery.confirm');
|
||||
|
|
|
|||
95
flexiapi/tests/Feature/ApiAccountRecoveryTokenTest.php
Normal file
95
flexiapi/tests/Feature/ApiAccountRecoveryTokenTest.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Account;
|
||||
use App\Space;
|
||||
use App\AccountRecoveryToken;
|
||||
use Tests\TestCase;
|
||||
use Carbon\Carbon;
|
||||
use App\Http\Middleware\IsWebPanelEnabled;
|
||||
|
||||
class ApiAccountRecoveryTokenTest extends TestCase
|
||||
{
|
||||
protected $tokenRoute = '/api/account_recovery_tokens/send-by-push';
|
||||
protected $tokenRequestRoute = '/api/account_recovery_request_tokens';
|
||||
protected $method = 'POST';
|
||||
|
||||
protected $pnProvider = 'fcm';
|
||||
protected $pnParam = 'param';
|
||||
protected $pnPrid = 'id';
|
||||
|
||||
public function testMandatoryParameters()
|
||||
{
|
||||
$this->json($this->method, $this->tokenRoute)->assertStatus(422);
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => null,
|
||||
'pn_param' => null,
|
||||
'pn_prid' => null,
|
||||
])->assertStatus(422);
|
||||
}
|
||||
|
||||
public function testThrottling()
|
||||
{
|
||||
AccountRecoveryToken::factory()->create([
|
||||
'pn_provider' => $this->pnProvider,
|
||||
'pn_param' => $this->pnParam,
|
||||
'pn_prid' => $this->pnPrid,
|
||||
]);
|
||||
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => $this->pnProvider,
|
||||
'pn_param' => $this->pnParam,
|
||||
'pn_prid' => $this->pnPrid,
|
||||
])->assertStatus(503);
|
||||
|
||||
// Redeem all the tokens
|
||||
AccountRecoveryToken::where('used', false)->update(['used' => true]);
|
||||
|
||||
$this->json($this->method, $this->tokenRoute, [
|
||||
'pn_provider' => $this->pnProvider,
|
||||
'pn_param' => $this->pnParam,
|
||||
'pn_prid' => $this->pnPrid,
|
||||
])->assertStatus(429);
|
||||
}
|
||||
|
||||
public function testTokenRecoveryPage()
|
||||
{
|
||||
$token = AccountRecoveryToken::factory()->create();
|
||||
$space = Space::factory()->create();
|
||||
$phone = '+3312345';
|
||||
|
||||
$this->get($this->setSpaceOnRoute($space, route('account.recovery.show.phone', ['account_recovery_token' => 'bad_token'])))
|
||||
->assertStatus(404);
|
||||
|
||||
$this->get($this->setSpaceOnRoute($space, route('account.recovery.show.phone', ['account_recovery_token' => $token->token])))
|
||||
->assertDontSee($phone)
|
||||
->assertStatus(200);
|
||||
|
||||
$this->get($this->setSpaceOnRoute($space, route('account.recovery.show.phone', ['account_recovery_token' => $token->token, 'phone' => $phone])))
|
||||
->assertSee($phone)
|
||||
->assertStatus(200);
|
||||
|
||||
$token->consume();
|
||||
|
||||
$this->get($this->setSpaceOnRoute($space, route('account.recovery.show.phone', ['account_recovery_token' => $token->token])))
|
||||
->assertStatus(404);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
namespace Tests;
|
||||
|
||||
use App\PhoneCountry;
|
||||
use App\Space;
|
||||
use App\Http\Middleware\SpaceCheck;
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
|
@ -45,4 +46,9 @@ abstract class TestCase extends BaseTestCase
|
|||
PhoneCountry::factory()->france()->activated()->create();
|
||||
PhoneCountry::factory()->netherlands()->create();
|
||||
}
|
||||
|
||||
protected function setSpaceOnRoute(Space $space, string $route)
|
||||
{
|
||||
return str_replace('localhost', $space->domain, $route);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue