1
0
mirror of https://github.com/Bubka/2FAuth.git synced 2025-07-08 16:36:52 +02:00

Apply Laravel Pint fixes

This commit is contained in:
Bubka
2022-11-22 15:15:52 +01:00
parent d84dd6659e
commit d6fd8e3c52
178 changed files with 2409 additions and 2899 deletions
app
Api
Console
Events
Exceptions
Extensions
Facades
Factories
Helpers
Http
Listeners
Models
Notifications
Providers
Rules
Services
pint.json
tests
Api
Classes
CreatesApplication.php
Feature
FeatureTestCase.phpModelTestCase.phpTestCase.php
Unit

@ -2,18 +2,16 @@
namespace App\Api\v1\Controllers; namespace App\Api\v1\Controllers;
use App\Models\Group;
use App\Facades\Groups;
use App\Api\v1\Requests\GroupStoreRequest;
use App\Api\v1\Requests\GroupAssignRequest; use App\Api\v1\Requests\GroupAssignRequest;
use App\Api\v1\Requests\GroupStoreRequest;
use App\Api\v1\Resources\GroupResource; use App\Api\v1\Resources\GroupResource;
use App\Api\v1\Resources\TwoFAccountCollection; use App\Api\v1\Resources\TwoFAccountCollection;
use App\Facades\Groups;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\App; use App\Models\Group;
class GroupController extends Controller class GroupController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
@ -26,7 +24,6 @@ class GroupController extends Controller
return GroupResource::collection($groups); return GroupResource::collection($groups);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -44,7 +41,6 @@ class GroupController extends Controller
->setStatusCode(201); ->setStatusCode(201);
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
@ -56,7 +52,6 @@ class GroupController extends Controller
return new GroupResource($group); return new GroupResource($group);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
@ -71,10 +66,8 @@ class GroupController extends Controller
Groups::update($group, $validated); Groups::update($group, $validated);
return new GroupResource($group); return new GroupResource($group);
} }
/** /**
* Associate the specified accounts with the group * Associate the specified accounts with the group
* *
@ -89,10 +82,8 @@ class GroupController extends Controller
Groups::assign($validated['ids'], $group); Groups::assign($validated['ids'], $group);
return new GroupResource($group); return new GroupResource($group);
} }
/** /**
* Get accounts assign to the group * Get accounts assign to the group
* *
@ -104,10 +95,8 @@ class GroupController extends Controller
$twofaccounts = Groups::getAccounts($group); $twofaccounts = Groups::getAccounts($group);
return new TwoFAccountCollection($twofaccounts); return new TwoFAccountCollection($twofaccounts);
} }
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
@ -120,5 +109,4 @@ class GroupController extends Controller
return response()->json(null, 204); return response()->json(null, 204);
} }
} }

@ -2,12 +2,11 @@
namespace App\Api\v1\Controllers; namespace App\Api\v1\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Services\LogoService; use App\Services\LogoService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Storage;
class IconController extends Controller class IconController extends Controller
{ {
@ -31,7 +30,6 @@ class IconController extends Controller
: response()->json(['message' => __('errors.file_upload_failed')], 500); : response()->json(['message' => __('errors.file_upload_failed')], 500);
} }
/** /**
* Fetch a logo * Fetch a logo
* *
@ -52,7 +50,6 @@ class IconController extends Controller
: response()->json(null, 204); : response()->json(null, 204);
} }
/** /**
* delete an icon * delete an icon
* *

@ -2,15 +2,13 @@
namespace App\Api\v1\Controllers; namespace App\Api\v1\Controllers;
use App\Models\TwoFAccount;
use App\Facades\QrCode;
use App\Api\v1\Requests\QrCodeDecodeRequest; use App\Api\v1\Requests\QrCodeDecodeRequest;
use App\Facades\QrCode;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\TwoFAccount;
class QrCodeController extends Controller class QrCodeController extends Controller
{ {
/** /**
* Show a QR code image * Show a QR code image
* *
@ -24,7 +22,6 @@ class QrCodeController extends Controller
return response()->json(['qrcode' => QrCode::encode($uri)], 200); return response()->json(['qrcode' => QrCode::encode($uri)], 200);
} }
/** /**
* Decode an uploaded QR Code image * Decode an uploaded QR Code image
* *
@ -39,5 +36,4 @@ class QrCodeController extends Controller
? response()->json(['data' => QrCode::decode($file)], 200) ? response()->json(['data' => QrCode::decode($file)], 200)
: response()->json(['message' => __('errors.file_upload_failed')], 500); : response()->json(['message' => __('errors.file_upload_failed')], 500);
} }
} }

@ -2,12 +2,11 @@
namespace App\Api\v1\Controllers; namespace App\Api\v1\Controllers;
use App\Facades\Settings;
use App\Api\v1\Requests\SettingStoreRequest; use App\Api\v1\Requests\SettingStoreRequest;
use App\Api\v1\Requests\SettingUpdateRequest; use App\Api\v1\Requests\SettingUpdateRequest;
use App\Facades\Settings;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
class SettingController extends Controller class SettingController extends Controller
{ {
/** /**
@ -22,14 +21,13 @@ class SettingController extends Controller
$settings->each(function (mixed $item, string $key) use ($settingsResources) { $settings->each(function (mixed $item, string $key) use ($settingsResources) {
$settingsResources->push([ $settingsResources->push([
'key' => $key, 'key' => $key,
'value' => $item 'value' => $item,
]); ]);
}); });
return response()->json($settingsResources->all(), 200); return response()->json($settingsResources->all(), 200);
} }
/** /**
* Display a setting * Display a setting
* *
@ -46,11 +44,10 @@ class SettingController extends Controller
return response()->json([ return response()->json([
'key' => $settingName, 'key' => $settingName,
'value' => $setting 'value' => $setting,
], 200); ], 200);
} }
/** /**
* Store a setting * Store a setting
* *
@ -65,11 +62,10 @@ class SettingController extends Controller
return response()->json([ return response()->json([
'key' => $validated['key'], 'key' => $validated['key'],
'value' => $validated['value'] 'value' => $validated['value'],
], 201); ], 201);
} }
/** /**
* Update a setting * Update a setting
* *
@ -84,12 +80,10 @@ class SettingController extends Controller
return response()->json([ return response()->json([
'key' => $settingName, 'key' => $settingName,
'value' => $validated['value'] 'value' => $validated['value'],
], 200); ], 200);
} }
/** /**
* Delete a setting * Delete a setting
* *
@ -105,10 +99,10 @@ class SettingController extends Controller
} }
$optionsConfig = config('2fauth.options'); $optionsConfig = config('2fauth.options');
if(array_key_exists($settingName, $optionsConfig)) { if (array_key_exists($settingName, $optionsConfig)) {
return response()->json( return response()->json(
['message' => 'bad request', ['message' => 'bad request',
'reason' => [__('errors.delete_user_setting_only')] 'reason' => [__('errors.delete_user_setting_only')],
], 400); ], 400);
} }
@ -116,5 +110,4 @@ class SettingController extends Controller
return response()->json(null, 204); return response()->json(null, 204);
} }
} }

@ -2,26 +2,25 @@
namespace App\Api\v1\Controllers; namespace App\Api\v1\Controllers;
use App\Models\TwoFAccount; use App\Api\v1\Requests\TwoFAccountBatchRequest;
use App\Api\v1\Requests\TwoFAccountDynamicRequest;
use App\Api\v1\Requests\TwoFAccountImportRequest;
use App\Api\v1\Requests\TwoFAccountReorderRequest; use App\Api\v1\Requests\TwoFAccountReorderRequest;
use App\Api\v1\Requests\TwoFAccountStoreRequest; use App\Api\v1\Requests\TwoFAccountStoreRequest;
use App\Api\v1\Requests\TwoFAccountUpdateRequest; use App\Api\v1\Requests\TwoFAccountUpdateRequest;
use App\Api\v1\Requests\TwoFAccountImportRequest;
use App\Api\v1\Requests\TwoFAccountBatchRequest;
use App\Api\v1\Requests\TwoFAccountUriRequest; use App\Api\v1\Requests\TwoFAccountUriRequest;
use App\Api\v1\Requests\TwoFAccountDynamicRequest;
use App\Api\v1\Resources\TwoFAccountCollection; use App\Api\v1\Resources\TwoFAccountCollection;
use App\Api\v1\Resources\TwoFAccountReadResource; use App\Api\v1\Resources\TwoFAccountReadResource;
use App\Api\v1\Resources\TwoFAccountStoreResource; use App\Api\v1\Resources\TwoFAccountStoreResource;
use App\Facades\Groups; use App\Facades\Groups;
use App\Facades\TwoFAccounts; use App\Facades\TwoFAccounts;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\TwoFAccount;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class TwoFAccountController extends Controller class TwoFAccountController extends Controller
{ {
/** /**
* List all resources * List all resources
* *
@ -32,12 +31,10 @@ class TwoFAccountController extends Controller
return new TwoFAccountCollection(TwoFAccount::ordered()->get()); return new TwoFAccountCollection(TwoFAccount::ordered()->get());
} }
/** /**
* Display a 2FA account * Display a 2FA account
* *
* @param \App\Models\TwoFAccount $twofaccount * @param \App\Models\TwoFAccount $twofaccount
*
* @return \App\Api\v1\Resources\TwoFAccountReadResource * @return \App\Api\v1\Resources\TwoFAccountReadResource
*/ */
public function show(TwoFAccount $twofaccount) public function show(TwoFAccount $twofaccount)
@ -45,7 +42,6 @@ class TwoFAccountController extends Controller
return new TwoFAccountReadResource($twofaccount); return new TwoFAccountReadResource($twofaccount);
} }
/** /**
* Store a new 2FA account * Store a new 2FA account
* *
@ -65,8 +61,7 @@ class TwoFAccountController extends Controller
if (Arr::has($validated, 'uri')) { if (Arr::has($validated, 'uri')) {
$twofaccount->fillWithURI($validated['uri'], Arr::get($validated, 'custom_otp') === TwoFAccount::STEAM_TOTP); $twofaccount->fillWithURI($validated['uri'], Arr::get($validated, 'custom_otp') === TwoFAccount::STEAM_TOTP);
} } else {
else {
$twofaccount->fillWithOtpParameters($validated); $twofaccount->fillWithOtpParameters($validated);
} }
$twofaccount->save(); $twofaccount->save();
@ -79,8 +74,6 @@ class TwoFAccountController extends Controller
->setStatusCode(201); ->setStatusCode(201);
} }
/** /**
* Update a 2FA account * Update a 2FA account
* *
@ -98,10 +91,8 @@ class TwoFAccountController extends Controller
return (new TwoFAccountReadResource($twofaccount)) return (new TwoFAccountReadResource($twofaccount))
->response() ->response()
->setStatusCode(200); ->setStatusCode(200);
} }
/** /**
* Convert a migration resource to a valid TwoFAccounts collection * Convert a migration resource to a valid TwoFAccounts collection
* *
@ -118,13 +109,11 @@ class TwoFAccountController extends Controller
return $migrationResource instanceof \Illuminate\Http\UploadedFile return $migrationResource instanceof \Illuminate\Http\UploadedFile
? new TwoFAccountCollection(TwoFAccounts::migrate($migrationResource->get())) ? new TwoFAccountCollection(TwoFAccounts::migrate($migrationResource->get()))
: response()->json(['message' => __('errors.file_upload_failed')], 500); : response()->json(['message' => __('errors.file_upload_failed')], 500);
} } else {
else {
return new TwoFAccountCollection(TwoFAccounts::migrate($request->payload)); return new TwoFAccountCollection(TwoFAccounts::migrate($request->payload));
} }
} }
/** /**
* Save 2FA accounts order * Save 2FA accounts order
* *
@ -140,7 +129,6 @@ class TwoFAccountController extends Controller
return response()->json(['message' => 'order saved'], 200); return response()->json(['message' => 'order saved'], 200);
} }
/** /**
* Preview account using an uri, without any db moves * Preview account using an uri, without any db moves
* *
@ -155,7 +143,6 @@ class TwoFAccountController extends Controller
return new TwoFAccountStoreResource($twofaccount); return new TwoFAccountStoreResource($twofaccount);
} }
/** /**
* Get a One-Time Password * Get a One-Time Password
* *
@ -173,15 +160,14 @@ class TwoFAccountController extends Controller
} }
// The request input is an uri // The request input is an uri
else if ( $request->has('uri') ) { elseif ($request->has('uri')) {
// return 404 if uri is provided with any parameter other than otp_type // return 404 if uri is provided with any parameter other than otp_type
if ((count($inputs) == 2 && $request->missing('custom_otp')) || count($inputs) > 2) { if ((count($inputs) == 2 && $request->missing('custom_otp')) || count($inputs) > 2) {
return response()->json([ return response()->json([
'message' => 'bad request', 'message' => 'bad request',
'reason' => ['uri' => __('validation.onlyCustomOtpWithUri')] 'reason' => ['uri' => __('validation.onlyCustomOtpWithUri')],
], 400); ], 400);
} } else {
else {
$validatedData = $request->validate((new TwoFAccountUriRequest)->rules()); $validatedData = $request->validate((new TwoFAccountUriRequest)->rules());
$twofaccount = new TwoFAccount; $twofaccount = new TwoFAccount;
$twofaccount->fillWithURI($validatedData['uri'], Arr::get($validatedData, 'custom_otp') === TwoFAccount::STEAM_TOTP, true); $twofaccount->fillWithURI($validatedData['uri'], Arr::get($validatedData, 'custom_otp') === TwoFAccount::STEAM_TOTP, true);
@ -198,7 +184,6 @@ class TwoFAccountController extends Controller
return response()->json($twofaccount->getOTP(), 200); return response()->json($twofaccount->getOTP(), 200);
} }
/** /**
* A simple and light method to get the account count. * A simple and light method to get the account count.
* *
@ -207,12 +192,10 @@ class TwoFAccountController extends Controller
*/ */
public function count(Request $request) public function count(Request $request)
{ {
return response()->json([ 'count' => TwoFAccount::count() ], 200); return response()->json(['count' => TwoFAccount::count()], 200);
} }
/** /**
*
* Withdraw one or more accounts from their group * Withdraw one or more accounts from their group
* *
* @param \App\Api\v1\Requests\TwoFAccountBatchRequest $request * @param \App\Api\v1\Requests\TwoFAccountBatchRequest $request
@ -225,16 +208,15 @@ class TwoFAccountController extends Controller
if ($this->tooManyIds($validated['ids'])) { if ($this->tooManyIds($validated['ids'])) {
return response()->json([ return response()->json([
'message' => 'bad request', 'message' => 'bad request',
'reason' => [__('errors.too_many_ids')] 'reason' => [__('errors.too_many_ids')],
], 400); ], 400);
} }
TwoFAccounts::withdraw($validated['ids']); TwoFAccounts::withdraw($validated['ids']);
return response()->json([ 'message' => 'accounts withdrawn' ], 200); return response()->json(['message' => 'accounts withdrawn'], 200);
} }
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
@ -248,7 +230,6 @@ class TwoFAccountController extends Controller
return response()->json(null, 204); return response()->json(null, 204);
} }
/** /**
* Remove the specified resources from storage. * Remove the specified resources from storage.
* *
@ -262,7 +243,7 @@ class TwoFAccountController extends Controller
if ($this->tooManyIds($validated['ids'])) { if ($this->tooManyIds($validated['ids'])) {
return response()->json([ return response()->json([
'message' => 'bad request', 'message' => 'bad request',
'reason' => [__('errors.too_many_ids')] 'reason' => [__('errors.too_many_ids')],
], 400); ], 400);
} }
@ -271,7 +252,6 @@ class TwoFAccountController extends Controller
return response()->json(null, 204); return response()->json(null, 204);
} }
/** /**
* Checks ids length * Checks ids length
* *
@ -285,5 +265,4 @@ class TwoFAccountController extends Controller
return $nb > 99 ? true : false; return $nb > 99 ? true : false;
} }
} }

@ -2,9 +2,9 @@
namespace App\Api\v1\Controllers; namespace App\Api\v1\Controllers;
use App\Models\User;
use App\Api\v1\Resources\UserResource; use App\Api\v1\Resources\UserResource;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class UserController extends Controller class UserController extends Controller
@ -24,6 +24,5 @@ class UserController extends Controller
return $user return $user
? new UserResource($user) ? new UserResource($user)
: response()->json(['name' => null], 200); : response()->json(['name' => null], 200);
} }
} }

@ -26,7 +26,7 @@ class GroupAssignRequest extends FormRequest
{ {
return [ return [
'ids' => 'required|array', 'ids' => 'required|array',
'ids.*' => 'integer' 'ids.*' => 'integer',
]; ];
} }
} }

@ -2,8 +2,8 @@
namespace App\Api\v1\Requests; namespace App\Api\v1\Requests;
use Illuminate\Support\Arr;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
class TwoFAccountDynamicRequest extends FormRequest class TwoFAccountDynamicRequest extends FormRequest
@ -32,7 +32,6 @@ class TwoFAccountDynamicRequest extends FormRequest
return $rules; return $rules;
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

@ -37,7 +37,6 @@ class TwoFAccountStoreRequest extends FormRequest
]; ];
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

@ -37,7 +37,6 @@ class TwoFAccountUpdateRequest extends FormRequest
]; ];
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

@ -30,7 +30,6 @@ class TwoFAccountUriRequest extends FormRequest
]; ];
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

@ -3,7 +3,6 @@
namespace App\Api\v1\Resources; namespace App\Api\v1\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Http\Resources\Json\ResourceCollection;
use App\Api\v1\Resources\TwoFAccountReadResource;
class TwoFAccountCollection extends ResourceCollection class TwoFAccountCollection extends ResourceCollection
{ {
@ -14,7 +13,6 @@ class TwoFAccountCollection extends ResourceCollection
*/ */
public $collects = TwoFAccountReadResource::class; public $collects = TwoFAccountReadResource::class;
/** /**
* Transform the resource collection into an array. * Transform the resource collection into an array.
* *
@ -27,7 +25,7 @@ class TwoFAccountCollection extends ResourceCollection
// The underlying TwoFAccountReadResource hides the secret only when withSecret == false. // The underlying TwoFAccountReadResource hides the secret only when withSecret == false.
// When withSecret is provided the underlying resource will return secret according to the parameter value // When withSecret is provided the underlying resource will return secret according to the parameter value
// If no withSecret is set we force it to false to ensure the secret will not being returned. // If no withSecret is set we force it to false to ensure the secret will not being returned.
if (!$request->has('withSecret')) { if (! $request->has('withSecret')) {
$request->merge(['withSecret' => false]); $request->merge(['withSecret' => false]);
} }

@ -31,13 +31,13 @@ class TwoFAccountStoreResource extends JsonResource
'service' => $this->service, 'service' => $this->service,
'icon' => $this->icon, 'icon' => $this->icon,
'secret' => $this->when( 'secret' => $this->when(
!$request->has('withSecret') || (int) filter_var($request->input('withSecret'), FILTER_VALIDATE_BOOLEAN) == 1, ! $request->has('withSecret') || (int) filter_var($request->input('withSecret'), FILTER_VALIDATE_BOOLEAN) == 1,
$this->secret $this->secret
), ),
'digits' => (int) $this->digits, 'digits' => (int) $this->digits,
'algorithm' => $this->algorithm, 'algorithm' => $this->algorithm,
'period' => is_null($this->period) ? null : (int)$this->period, 'period' => is_null($this->period) ? null : (int) $this->period,
'counter' => is_null($this->counter) ? null : (int)$this->counter 'counter' => is_null($this->counter) ? null : (int) $this->counter,
]; ];
} }
} }

@ -20,9 +20,9 @@ class UserResource extends JsonResource
public function toArray($request) public function toArray($request)
{ {
return [ return [
'id' => $this->when(!is_null($request->user()), $this->id), 'id' => $this->when(! is_null($request->user()), $this->id),
'name' => $this->name, 'name' => $this->name,
'email' => $this->when(!is_null($request->user()), $this->email), 'email' => $this->when(! is_null($request->user()), $this->email),
]; ];
} }
} }

@ -4,7 +4,6 @@ namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class CheckDbConnection extends Command class CheckDbConnection extends Command
{ {
@ -44,6 +43,7 @@ class CheckDbConnection extends Command
try { try {
DB::connection()->getPDO(); DB::connection()->getPDO();
$this->line(DB::connection()->getDatabaseName()); $this->line(DB::connection()->getDatabaseName());
return 1; return 1;
} catch (\Exception $e) { } catch (\Exception $e) {
return 0; return 0;

@ -42,12 +42,13 @@ class FixUnsplittedAccounts extends Command
*/ */
public function handle() public function handle()
{ {
if (! Schema::hasColumn('twofaccounts', 'legacy_uri')) {
if (!Schema::hasColumn('twofaccounts', 'legacy_uri')) {
$this->comment('2fauth:fix-unsplitted-accounts is useful only after SplitTwofaccountsUriInMultipleColumns migration ran'); $this->comment('2fauth:fix-unsplitted-accounts is useful only after SplitTwofaccountsUriInMultipleColumns migration ran');
return; return;
} else {
$this->line('Fetching accounts...');
} }
else $this->line('Fetching accounts...');
$twofaccounts = TwoFAccount::where('otp_type', '') $twofaccounts = TwoFAccount::where('otp_type', '')
->where('secret', '') ->where('secret', '')
@ -61,6 +62,7 @@ class FixUnsplittedAccounts extends Command
if ($twofaccounts->count() == 0) { if ($twofaccounts->count() == 0) {
$this->info('Nothing to fix'); $this->info('Nothing to fix');
return; return;
} }
@ -69,16 +71,14 @@ class FixUnsplittedAccounts extends Command
foreach ($twofaccounts as $twofaccount) { foreach ($twofaccounts as $twofaccount) {
if ($twofaccount->legacy_uri === __('errors.indecipherable')) { if ($twofaccount->legacy_uri === __('errors.indecipherable')) {
$this->error(sprintf('Account #%d cannot be deciphered', $twofaccount->id)); $this->error(sprintf('Account #%d cannot be deciphered', $twofaccount->id));
} } else {
else {
try { try {
// Get a consistent account // Get a consistent account
$twofaccount->fillWithURI($twofaccount->legacy_uri, false, true); $twofaccount->fillWithURI($twofaccount->legacy_uri, false, true);
$twofaccount->save(); $twofaccount->save();
$this->info(sprintf('Account #%d fixed', $twofaccount->id)); $this->info(sprintf('Account #%d fixed', $twofaccount->id));
} } catch (\Exception $ex) {
catch (\Exception $ex) {
$this->error(sprintf('Error while updating account #%d', $twofaccount->id)); $this->error(sprintf('Error while updating account #%d', $twofaccount->id));
} }
} }

@ -2,8 +2,8 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Console\Commands\Utils\ResetTrait; use App\Console\Commands\Utils\ResetTrait;
use Illuminate\Console\Command;
class ResetDemo extends Command class ResetDemo extends Command
{ {
@ -40,15 +40,15 @@ class ResetDemo extends Command
*/ */
public function handle() public function handle()
{ {
if( !config('2fauth.config.isDemoApp') ) { if (! config('2fauth.config.isDemoApp')) {
$this->comment('2fauth:reset-demo can only run when isDemoApp option is On'); $this->comment('2fauth:reset-demo can only run when isDemoApp option is On');
return; return;
} }
if( $this->option('no-confirm') ) { if ($this->option('no-confirm')) {
$demo = 'demo'; $demo = 'demo';
} } else {
else {
$this->line('This will reset the app in order to run a clean and fresh demo.'); $this->line('This will reset the app in order to run a clean and fresh demo.');
$demo = $this->ask('To prevent any mistake please type the word "demo" to go on'); $demo = $this->ask('To prevent any mistake please type the word "demo" to go on');
} }
@ -57,8 +57,7 @@ class ResetDemo extends Command
$this->resetIcons(); $this->resetIcons();
$this->resetDB('DemoSeeder'); $this->resetDB('DemoSeeder');
$this->info('Demo app refreshed'); $this->info('Demo app refreshed');
} } else {
else {
$this->comment('Bad confirmation word, nothing appened'); $this->comment('Bad confirmation word, nothing appened');
} }
} }

@ -40,15 +40,15 @@ class ResetTesting extends Command
*/ */
public function handle() public function handle()
{ {
if( !config('2fauth.config.isTestingApp') ) { if (! config('2fauth.config.isTestingApp')) {
$this->comment('2fauth:reset-testing can only run when isTestingApp option is On'); $this->comment('2fauth:reset-testing can only run when isTestingApp option is On');
return; return;
} }
if( $this->option('no-confirm') ) { if ($this->option('no-confirm')) {
$testing = 'testing'; $testing = 'testing';
} } else {
else {
$this->line('This will reset the app in order to run a clean and fresh testing app.'); $this->line('This will reset the app in order to run a clean and fresh testing app.');
$testing = $this->ask('To prevent any mistake please type the word "testing" to go on'); $testing = $this->ask('To prevent any mistake please type the word "testing" to go on');
} }
@ -58,10 +58,8 @@ class ResetTesting extends Command
$this->resetDB('TestingSeeder'); $this->resetDB('TestingSeeder');
$this->info('Testing app refreshed'); $this->info('Testing app refreshed');
} } else {
else {
$this->comment('Bad confirmation word, nothing appened'); $this->comment('Bad confirmation word, nothing appened');
} }
} }
} }

File diff suppressed because one or more lines are too long

@ -2,7 +2,6 @@
namespace App\Console\Commands\Utils; namespace App\Console\Commands\Utils;
use App\Console\Commands\Utils\IconGenerator;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -81,10 +80,9 @@ trait ResetTrait
protected function seedDB(string $seeder) : void protected function seedDB(string $seeder) : void
{ {
$this->callSilent('db:seed', [ $this->callSilent('db:seed', [
'--class' => $seeder '--class' => $seeder,
]); ]);
$this->line('Database seeded'); $this->line('Database seeded');
} }
} }

@ -27,7 +27,7 @@ class Kernel extends ConsoleKernel
*/ */
protected function commands() protected function commands()
{ {
$this->load(__DIR__.'/Commands'); $this->load(__DIR__ . '/Commands');
require base_path('routes/console.php'); require base_path('routes/console.php');
} }

@ -44,64 +44,71 @@ class Handler extends ExceptionHandler
{ {
$this->renderable(function (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $exception, $request) { $this->renderable(function (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $exception, $request) {
return response()->json([ return response()->json([
'message' => 'not found'], 404); 'message' => 'not found',
], 404);
}); });
$this->renderable(function (InvalidOtpParameterException $exception, $request) { $this->renderable(function (InvalidOtpParameterException $exception, $request) {
return response()->json([ return response()->json([
'message' => 'invalid OTP parameters', 'message' => 'invalid OTP parameters',
'reason' => [$exception->getMessage()] 'reason' => [$exception->getMessage()],
], 400); ], 400);
}); });
$this->renderable(function (InvalidQrCodeException $exception, $request) { $this->renderable(function (InvalidQrCodeException $exception, $request) {
return response()->json([ return response()->json([
'message' => 'not a valid QR code'], 400); 'message' => 'not a valid QR code', ], 400);
}); });
$this->renderable(function (InvalidSecretException $exception, $request) { $this->renderable(function (InvalidSecretException $exception, $request) {
return response()->json([ return response()->json([
'message' => 'not a valid base32 encoded secret'], 400); 'message' => 'not a valid base32 encoded secret', ], 400);
}); });
$this->renderable(function (DbEncryptionException $exception, $request) { $this->renderable(function (DbEncryptionException $exception, $request) {
return response()->json([ return response()->json([
'message' => $exception->getMessage()], 400); 'message' => $exception->getMessage(), ], 400);
}); });
$this->renderable(function (InvalidMigrationDataException $exception, $request) { $this->renderable(function (InvalidMigrationDataException $exception, $request) {
return response()->json([ return response()->json([
'message' => __('errors.invalid_x_migration', ['appname' => $exception->getMessage()])], 400); 'message' => __('errors.invalid_x_migration', ['appname' => $exception->getMessage()]),
], 400);
}); });
$this->renderable(function (UnsupportedMigrationException $exception, $request) { $this->renderable(function (UnsupportedMigrationException $exception, $request) {
return response()->json([ return response()->json([
'message' => __('errors.unsupported_migration')], 400); 'message' => __('errors.unsupported_migration'),
], 400);
}); });
$this->renderable(function (EncryptedMigrationException $exception, $request) { $this->renderable(function (EncryptedMigrationException $exception, $request) {
return response()->json([ return response()->json([
'message' => __('errors.encrypted_migration')], 400); 'message' => __('errors.encrypted_migration'),
], 400);
}); });
$this->renderable(function (UndecipherableException $exception, $request) { $this->renderable(function (UndecipherableException $exception, $request) {
return response()->json([ return response()->json([
'message' => __('errors.cannot_decipher_secret')], 400); 'message' => __('errors.cannot_decipher_secret'),
], 400);
}); });
$this->renderable(function (UnsupportedOtpTypeException $exception, $request) { $this->renderable(function (UnsupportedOtpTypeException $exception, $request) {
return response()->json([ return response()->json([
'message' => __('errors.unsupported_otp_type')], 400); 'message' => __('errors.unsupported_otp_type'),
], 400);
}); });
$this->renderable(function (\Illuminate\Auth\AuthenticationException $exception, $request) { $this->renderable(function (\Illuminate\Auth\AuthenticationException $exception, $request) {
if ($exception->guards() === ['reverse-proxy-guard']) { if ($exception->guards() === ['reverse-proxy-guard']) {
return response()->json([ return response()->json([
'message' => $exception->getMessage()], 407); 'message' => $exception->getMessage(),
} ], 407);
else { } else {
return response()->json([ return response()->json([
'message' => $exception->getMessage()], 401); 'message' => $exception->getMessage(),
], 401);
} }
}); });
} }

@ -6,10 +6,10 @@
namespace App\Extensions; namespace App\Extensions;
use App\Models\User; use App\Models\User;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Exception;
class RemoteUserProvider implements UserProvider class RemoteUserProvider implements UserProvider
{ {
@ -23,7 +23,6 @@ class RemoteUserProvider implements UserProvider
// The downside of this approach is that we have to be sure that no change that needs // The downside of this approach is that we have to be sure that no change that needs
// to be persisted will be made to the user instance afterward (i.e through middlewares). // to be persisted will be made to the user instance afterward (i.e through middlewares).
/** /**
* The currently authenticated user. * The currently authenticated user.
* *
@ -31,7 +30,6 @@ class RemoteUserProvider implements UserProvider
*/ */
protected $user; protected $user;
/** /**
* Get the In-memory user * Get the In-memory user
* *
@ -48,9 +46,8 @@ class RemoteUserProvider implements UserProvider
return $this->user; return $this->user;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function retrieveById($identifier) public function retrieveById($identifier)
{ {
@ -67,7 +64,7 @@ class RemoteUserProvider implements UserProvider
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
@ -77,7 +74,7 @@ class RemoteUserProvider implements UserProvider
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
@ -87,7 +84,7 @@ class RemoteUserProvider implements UserProvider
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
@ -97,7 +94,7 @@ class RemoteUserProvider implements UserProvider
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */

@ -2,8 +2,8 @@
namespace App\Extensions; namespace App\Extensions;
use Closure;
use App\Models\WebAuthnAuthenticatable; use App\Models\WebAuthnAuthenticatable;
use Closure;
use Illuminate\Auth\Passwords\PasswordBroker; use Illuminate\Auth\Passwords\PasswordBroker;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
@ -14,14 +14,13 @@ class WebauthnCredentialBroker extends PasswordBroker
* *
* @param array $credentials * @param array $credentials
* @param \Closure|null $callback * @param \Closure|null $callback
*
* @return string * @return string
*/ */
public function sendResetLink(array $credentials, Closure $callback = null): string public function sendResetLink(array $credentials, Closure $callback = null) : string
{ {
$user = $this->getUser($credentials); $user = $this->getUser($credentials);
if (!$user instanceof WebAuthnAuthenticatable) { if (! $user instanceof WebAuthnAuthenticatable) {
return static::INVALID_USER; return static::INVALID_USER;
} }
@ -40,20 +39,18 @@ class WebauthnCredentialBroker extends PasswordBroker
return static::RESET_LINK_SENT; return static::RESET_LINK_SENT;
} }
/** /**
* Reset the password for the given token. * Reset the password for the given token.
* *
* @param array $credentials * @param array $credentials
* @param \Closure $callback * @param \Closure $callback
*
* @return \Illuminate\Contracts\Auth\CanResetPassword|string * @return \Illuminate\Contracts\Auth\CanResetPassword|string
*/ */
public function reset(array $credentials, Closure $callback) public function reset(array $credentials, Closure $callback)
{ {
$user = $this->validateReset($credentials); $user = $this->validateReset($credentials);
if (!$user instanceof CanResetPasswordContract || !$user instanceof WebAuthnAuthenticatable) { if (! $user instanceof CanResetPasswordContract || ! $user instanceof WebAuthnAuthenticatable) {
return $user; return $user;
} }

@ -2,16 +2,16 @@
namespace App\Factories; namespace App\Factories;
use App\Services\Migrators\GoogleAuthMigrator; use App\Exceptions\EncryptedMigrationException;
use App\Exceptions\UnsupportedMigrationException;
use App\Services\Migrators\AegisMigrator; use App\Services\Migrators\AegisMigrator;
use App\Services\Migrators\GoogleAuthMigrator;
use App\Services\Migrators\Migrator; use App\Services\Migrators\Migrator;
use App\Services\Migrators\PlainTextMigrator; use App\Services\Migrators\PlainTextMigrator;
use App\Services\Migrators\TwoFASMigrator; use App\Services\Migrators\TwoFASMigrator;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use App\Exceptions\UnsupportedMigrationException;
use App\Exceptions\EncryptedMigrationException;
class MigratorFactory implements MigratorFactoryInterface class MigratorFactory implements MigratorFactoryInterface
{ {
@ -25,21 +25,17 @@ class MigratorFactory implements MigratorFactoryInterface
{ {
if ($this->isAegisJSON($migrationPayload)) { if ($this->isAegisJSON($migrationPayload)) {
return App::make(AegisMigrator::class); return App::make(AegisMigrator::class);
} } elseif ($this->is2FASv2($migrationPayload)) {
else if ($this->is2FASv2($migrationPayload)) {
return App::make(TwoFASMigrator::class); return App::make(TwoFASMigrator::class);
} } elseif ($this->isGoogleAuth($migrationPayload)) {
else if ($this->isGoogleAuth($migrationPayload)) {
return App::make(GoogleAuthMigrator::class); return App::make(GoogleAuthMigrator::class);
} } elseif ($this->isPlainText($migrationPayload)) {
else if ($this->isPlainText($migrationPayload)) {
return App::make(PlainTextMigrator::class); return App::make(PlainTextMigrator::class);
} else {
throw new UnsupportedMigrationException();
} }
else throw new UnsupportedMigrationException();
} }
/** /**
* Determine if a payload comes from Google Authenticator * Determine if a payload comes from Google Authenticator
* *
@ -50,15 +46,15 @@ class MigratorFactory implements MigratorFactoryInterface
{ {
// - Google Auth migration URI : a string starting with otpauth-migration://offline?data= on a single line // - Google Auth migration URI : a string starting with otpauth-migration://offline?data= on a single line
$lines = preg_split('~\R~', $migrationPayload, -1 , PREG_SPLIT_NO_EMPTY); $lines = preg_split('~\R~', $migrationPayload, -1, PREG_SPLIT_NO_EMPTY);
if (!$lines || count($lines) != 1) if (! $lines || count($lines) != 1) {
return false; return false;
}
return preg_match('/^otpauth-migration:\/\/offline\?data=.+$/', $lines[0]) == 1; return preg_match('/^otpauth-migration:\/\/offline\?data=.+$/', $lines[0]) == 1;
} }
/** /**
* Determine if a payload is a plain text content * Determine if a payload is a plain text content
* *
@ -70,14 +66,13 @@ class MigratorFactory implements MigratorFactoryInterface
// - Plain text : one or more otpauth URIs (otpauth://[t|h]otp/...), one per line // - Plain text : one or more otpauth URIs (otpauth://[t|h]otp/...), one per line
return Validator::make( return Validator::make(
preg_split('~\R~', $migrationPayload, -1 , PREG_SPLIT_NO_EMPTY), preg_split('~\R~', $migrationPayload, -1, PREG_SPLIT_NO_EMPTY),
[ [
'*' => 'regex:/^otpauth:\/\/[h,t]otp\//i', '*' => 'regex:/^otpauth:\/\/[h,t]otp\//i',
] ]
)->passes(); )->passes();
} }
/** /**
* Determine if a payload comes from Aegis Authenticator in JSON format * Determine if a payload comes from Aegis Authenticator in JSON format
* *
@ -107,15 +102,14 @@ class MigratorFactory implements MigratorFactoryInterface
if (Arr::has($json, 'db')) { if (Arr::has($json, 'db')) {
if (is_string($json['db']) && is_array(Arr::get($json, 'header.slots'))) { if (is_string($json['db']) && is_array(Arr::get($json, 'header.slots'))) {
throw new EncryptedMigrationException(); throw new EncryptedMigrationException();
} } else {
else {
return count(Validator::validate( return count(Validator::validate(
$json, $json,
[ [
'db.entries.*.type' => 'required', 'db.entries.*.type' => 'required',
'db.entries.*.name' => 'required', 'db.entries.*.name' => 'required',
'db.entries.*.issuer' => 'required', 'db.entries.*.issuer' => 'required',
'db.entries.*.info' => 'required' 'db.entries.*.info' => 'required',
] ]
)) > 0; )) > 0;
} }
@ -124,7 +118,6 @@ class MigratorFactory implements MigratorFactoryInterface
return false; return false;
} }
/** /**
* Determine if a payload comes from 2FAS Authenticator * Determine if a payload comes from 2FAS Authenticator
* *
@ -159,14 +152,13 @@ class MigratorFactory implements MigratorFactoryInterface
if (Arr::get($json, 'schemaVersion') == 2 && (Arr::has($json, 'services') || Arr::has($json, 'servicesEncrypted'))) { if (Arr::get($json, 'schemaVersion') == 2 && (Arr::has($json, 'services') || Arr::has($json, 'servicesEncrypted'))) {
if (Arr::has($json, 'servicesEncrypted')) { if (Arr::has($json, 'servicesEncrypted')) {
throw new EncryptedMigrationException(); throw new EncryptedMigrationException();
} } else {
else {
return count(Validator::validate( return count(Validator::validate(
$json, $json,
[ [
'services.*.secret' => 'required', 'services.*.secret' => 'required',
'services.*.name' => 'required', 'services.*.name' => 'required',
'services.*.otp' => 'required' 'services.*.otp' => 'required',
] ]
)) > 0; )) > 0;
} }
@ -174,5 +166,4 @@ class MigratorFactory implements MigratorFactoryInterface
return false; return false;
} }
} }

@ -12,13 +12,18 @@ class Helpers
* @param string $extension * @param string $extension
* @return string The filename * @return string The filename
*/ */
public static function getUniqueFilename(string $extension): string public static function getUniqueFilename(string $extension) : string
{ {
return Str::random(40).'.'.$extension; return Str::random(40) . '.' . $extension;
} }
/**
public static function cleanVersionNumber(?string $release): string|false * Clean a version number string
*
* @param string|null $release
* @return string|false
*/
public static function cleanVersionNumber(?string $release) : string|false
{ {
return preg_match('/([[0-9][0-9\.]*[0-9])/', $release, $version) ? $version[0] : false; return preg_match('/([[0-9][0-9\.]*[0-9])/', $release, $version) ? $version[0] : false;
} }

@ -2,9 +2,9 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
class ForgotPasswordController extends Controller class ForgotPasswordController extends Controller
{ {
@ -21,7 +21,6 @@ class ForgotPasswordController extends Controller
use SendsPasswordResetEmails; use SendsPasswordResetEmails;
/** /**
* Validate the email for the given request. * Validate the email for the given request.
* *

@ -2,17 +2,16 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\LoginRequest;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Lang;
use App\Http\Requests\LoginRequest;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class LoginController extends Controller class LoginController extends Controller
{ {
/* /*
@ -28,7 +27,6 @@ class LoginController extends Controller
use AuthenticatesUsers; use AuthenticatesUsers;
/** /**
* Handle a login request to the application. * Handle a login request to the application.
* *
@ -65,9 +63,9 @@ class LoginController extends Controller
return $this->sendFailedLoginResponse($request); return $this->sendFailedLoginResponse($request);
} }
/** /**
* log out current user * log out current user
*
* @param Request $request * @param Request $request
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
@ -79,7 +77,6 @@ class LoginController extends Controller
return response()->json(['message' => 'signed out'], Response::HTTP_OK); return response()->json(['message' => 'signed out'], Response::HTTP_OK);
} }
/** /**
* Send the response after the user was authenticated. * Send the response after the user was authenticated.
* *
@ -96,11 +93,10 @@ class LoginController extends Controller
return response()->json([ return response()->json([
'message' => 'authenticated', 'message' => 'authenticated',
'name' => $name 'name' => $name,
], Response::HTTP_OK); ], Response::HTTP_OK);
} }
/** /**
* Get the failed login response instance. * Get the failed login response instance.
* *
@ -112,7 +108,6 @@ class LoginController extends Controller
return response()->json(['message' => 'unauthorised'], Response::HTTP_UNAUTHORIZED); return response()->json(['message' => 'unauthorised'], Response::HTTP_UNAUTHORIZED);
} }
/** /**
* Redirect the user after determining they are locked out. * Redirect the user after determining they are locked out.
* *
@ -128,7 +123,6 @@ class LoginController extends Controller
return response()->json(['message' => Lang::get('auth.throttle', ['seconds' => $seconds])], Response::HTTP_TOO_MANY_REQUESTS); return response()->json(['message' => Lang::get('auth.throttle', ['seconds' => $seconds])], Response::HTTP_TOO_MANY_REQUESTS);
} }
/** /**
* Get the needed authorization credentials from the request. * Get the needed authorization credentials from the request.
* *
@ -145,7 +139,6 @@ class LoginController extends Controller
return $credentials; return $credentials;
} }
/** /**
* The user has been authenticated. * The user has been authenticated.
* *

@ -2,8 +2,8 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Requests\UserPatchPwdRequest;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\UserPatchPwdRequest;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -20,12 +20,13 @@ class PasswordController extends Controller
{ {
$validated = $request->validated(); $validated = $request->validated();
if (!Hash::check( $validated['currentPassword'], Auth::user()->password) ) { if (! Hash::check($validated['currentPassword'], Auth::user()->password)) {
Log::notice('Password update failed: wrong password provided'); Log::notice('Password update failed: wrong password provided');
return response()->json(['message' => __('errors.wrong_current_password')], 400); return response()->json(['message' => __('errors.wrong_current_password')], 400);
} }
if (!config('2fauth.config.isDemoApp') ) { if (! config('2fauth.config.isDemoApp')) {
$request->user()->update([ $request->user()->update([
'password' => bcrypt($validated['password']), 'password' => bcrypt($validated['password']),
]); ]);

@ -2,12 +2,12 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Models\User;
use App\Http\Requests\UserStoreRequest;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash; use App\Http\Requests\UserStoreRequest;
use App\Models\User;
use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Events\Registered;
use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class RegisterController extends Controller class RegisterController extends Controller
@ -25,7 +25,6 @@ class RegisterController extends Controller
use RegistersUsers; use RegistersUsers;
/** /**
* Handle a registration request for the application. * Handle a registration request for the application.
* *
@ -46,7 +45,6 @@ class RegisterController extends Controller
], 201); ], 201);
} }
/** /**
* Create a new user instance after a valid registration. * Create a new user instance after a valid registration.
* *

@ -19,5 +19,4 @@ class ResetPasswordController extends Controller
*/ */
use ResetsPasswords; use ResetsPasswords;
} }

@ -2,14 +2,14 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Requests\UserUpdateRequest;
use App\Http\Requests\UserDeleteRequest;
use App\Api\v1\Resources\UserResource; use App\Api\v1\Resources\UserResource;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth; use App\Http\Requests\UserDeleteRequest;
use Illuminate\Support\Facades\Hash; use App\Http\Requests\UserUpdateRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class UserController extends Controller class UserController extends Controller
@ -25,12 +25,13 @@ class UserController extends Controller
$user = $request->user(); $user = $request->user();
$validated = $request->validated(); $validated = $request->validated();
if (!Hash::check( $request->password, Auth::user()->password) ) { if (! Hash::check($request->password, Auth::user()->password)) {
Log::notice('Account update failed: wrong password provided'); Log::notice('Account update failed: wrong password provided');
return response()->json(['message' => __('errors.wrong_current_password')], 400); return response()->json(['message' => __('errors.wrong_current_password')], 400);
} }
if (!config('2fauth.config.isDemoApp') ) { if (! config('2fauth.config.isDemoApp')) {
$user->update([ $user->update([
'name' => $validated['name'], 'name' => $validated['name'],
'email' => $validated['email'], 'email' => $validated['email'],
@ -41,7 +42,6 @@ class UserController extends Controller
return new UserResource($user); return new UserResource($user);
} }
/** /**
* Delete the user's account. * Delete the user's account.
* *
@ -53,7 +53,7 @@ class UserController extends Controller
Log::info('User deletion requested'); Log::info('User deletion requested');
$validated = $request->validated(); $validated = $request->validated();
if (!Hash::check( $validated['password'], Auth::user()->password) ) { if (! Hash::check($validated['password'], Auth::user()->password)) {
return response()->json(['message' => __('errors.wrong_current_password')], 400); return response()->json(['message' => __('errors.wrong_current_password')], 400);
} }
@ -79,6 +79,7 @@ class UserController extends Controller
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
catch (\Throwable $e) { catch (\Throwable $e) {
Log::error('User deletion failed'); Log::error('User deletion failed');
return response()->json(['message' => __('errors.user_deletion_failed')], 400); return response()->json(['message' => __('errors.user_deletion_failed')], 400);
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd

@ -2,26 +2,25 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use App\Extensions\WebauthnCredentialBroker; use App\Extensions\WebauthnCredentialBroker;
use Illuminate\Foundation\Auth\ResetsPasswords; use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Password;
use App\Http\Requests\WebauthnDeviceLostRequest; use App\Http\Requests\WebauthnDeviceLostRequest;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;
class WebAuthnDeviceLostController extends Controller class WebAuthnDeviceLostController extends Controller
{ {
use ResetsPasswords; use ResetsPasswords;
/** /**
* Send a recovery email to the user. * Send a recovery email to the user.
* *
* @param \App\Http\Requests\WebauthnDeviceLostRequest $request * @param \App\Http\Requests\WebauthnDeviceLostRequest $request
* @param \App\Extensions\WebauthnCredentialBroker $broker * @param \App\Extensions\WebauthnCredentialBroker $broker
*
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
public function sendRecoveryEmail(WebauthnDeviceLostRequest $request, WebauthnCredentialBroker $broker) public function sendRecoveryEmail(WebauthnDeviceLostRequest $request, WebauthnCredentialBroker $broker)
@ -35,14 +34,13 @@ class WebAuthnDeviceLostController extends Controller
: $this->sendRecoveryLinkFailedResponse($request, $response); : $this->sendRecoveryLinkFailedResponse($request, $response);
} }
/** /**
* Get the response for a failed account recovery link. * Get the response for a failed account recovery link.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param string $response * @param string $response
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
protected function sendRecoveryLinkFailedResponse(Request $request, string $response) protected function sendRecoveryLinkFailedResponse(Request $request, string $response)
@ -56,13 +54,11 @@ class WebAuthnDeviceLostController extends Controller
->withErrors(['email' => trans($response)]); ->withErrors(['email' => trans($response)]);
} }
/** /**
* Get the response for a successful account recovery link. * Get the response for a successful account recovery link.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param string $response * @param string $response
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/ */
protected function sendRecoveryLinkResponse(Request $request, string $response) protected function sendRecoveryLinkResponse(Request $request, string $response)

@ -2,14 +2,14 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\User;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Laragear\WebAuthn\Http\Requests\AssertionRequest;
use Laragear\WebAuthn\Http\Requests\AssertedRequest;
use Illuminate\Contracts\Support\Responsable; use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Laragear\WebAuthn\Http\Requests\AssertedRequest;
use Laragear\WebAuthn\Http\Requests\AssertionRequest;
use Laragear\WebAuthn\WebAuthn; use Laragear\WebAuthn\WebAuthn;
class WebAuthnLoginController extends Controller class WebAuthnLoginController extends Controller
@ -31,7 +31,7 @@ class WebAuthnLoginController extends Controller
* @param \Laragear\WebAuthn\Http\Requests\AssertionRequest $request * @param \Laragear\WebAuthn\Http\Requests\AssertionRequest $request
* @return \Illuminate\Contracts\Support\Responsable|\Illuminate\Http\JsonResponse * @return \Illuminate\Contracts\Support\Responsable|\Illuminate\Http\JsonResponse
*/ */
public function options(AssertionRequest $request): Responsable|JsonResponse public function options(AssertionRequest $request) : Responsable|JsonResponse
{ {
switch (env('WEBAUTHN_USER_VERIFICATION')) { switch (env('WEBAUTHN_USER_VERIFICATION')) {
case WebAuthn::USER_VERIFICATION_DISCOURAGED: case WebAuthn::USER_VERIFICATION_DISCOURAGED:
@ -50,11 +50,10 @@ class WebAuthnLoginController extends Controller
return $user return $user
? $request->toVerify($user) ? $request->toVerify($user)
: response()->json([ : response()->json([
'message' => 'no registered user' 'message' => 'no registered user',
], 400); ], 400);
} }
/** /**
* Log the user in. * Log the user in.
* *
@ -70,7 +69,7 @@ class WebAuthnLoginController extends Controller
// Some authenticators do not send a userHandle so we hack the response to be compliant // Some authenticators do not send a userHandle so we hack the response to be compliant
// with Larapass/webauthn-lib implementation that waits for a userHandle // with Larapass/webauthn-lib implementation that waits for a userHandle
if(!$response['userHandle']) { if (! $response['userHandle']) {
$response['userHandle'] = User::getFromCredentialId($request->id)?->userHandle(); $response['userHandle'] = User::getFromCredentialId($request->id)?->userHandle();
$request->merge(['response' => $response]); $request->merge(['response' => $response]);
} }
@ -80,18 +79,17 @@ class WebAuthnLoginController extends Controller
if ($user) { if ($user) {
$this->authenticated($user); $this->authenticated($user);
return response()->noContent(); return response()->noContent();
} }
return response()->noContent(422); return response()->noContent(422);
} }
/** /**
* The user has been authenticated. * The user has been authenticated.
* *
* @param mixed $user * @param mixed $user
*
* @return void|\Illuminate\Http\JsonResponse * @return void|\Illuminate\Http\JsonResponse
*/ */
protected function authenticated($user) protected function authenticated($user)

@ -4,13 +4,12 @@ namespace App\Http\Controllers\Auth;
use App\Facades\Settings; use App\Facades\Settings;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests\WebauthnRenameRequest; use App\Http\Requests\WebauthnRenameRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class WebAuthnManageController extends Controller class WebAuthnManageController extends Controller
{ {
/** /**
* List all WebAuthn registered credentials * List all WebAuthn registered credentials
* *
@ -23,7 +22,6 @@ class WebAuthnManageController extends Controller
return response()->json($allUserCredentials, 200); return response()->json($allUserCredentials, 200);
} }
/** /**
* Rename a WebAuthn credential * Rename a WebAuthn credential
* *
@ -42,13 +40,11 @@ class WebAuthnManageController extends Controller
], 200); ], 200);
} }
/** /**
* Remove the specified credential from storage. * Remove the specified credential from storage.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param string|array $credential * @param string|array $credential
*
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function delete(Request $request, $credential) public function delete(Request $request, $credential)

@ -2,18 +2,17 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\WebauthnRecoveryRequest;
use App\Extensions\WebauthnCredentialBroker; use App\Extensions\WebauthnCredentialBroker;
use App\Facades\Settings; use App\Facades\Settings;
use App\Http\Controllers\Controller;
use App\Http\Requests\WebauthnRecoveryRequest;
use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\App; use Illuminate\Validation\ValidationException;
class WebAuthnRecoveryController extends Controller class WebAuthnRecoveryController extends Controller
{ {
@ -25,8 +24,8 @@ class WebAuthnRecoveryController extends Controller
* *
* @param \App\Http\Requests\WebauthnRecoveryRequest $request * @param \App\Http\Requests\WebauthnRecoveryRequest $request
* @param \App\Extensions\WebauthnCredentialBroker $broker * @param \App\Extensions\WebauthnCredentialBroker $broker
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
public function recover(WebauthnRecoveryRequest $request, WebauthnCredentialBroker $broker) public function recover(WebauthnRecoveryRequest $request, WebauthnCredentialBroker $broker)
@ -54,66 +53,57 @@ class WebAuthnRecoveryController extends Controller
$user->flushCredentials(); $user->flushCredentials();
} }
Settings::delete('useWebauthnOnly'); Settings::delete('useWebauthnOnly');
} else {
throw new AuthenticationException();
} }
else throw new AuthenticationException();
} }
); );
return $response === Password::PASSWORD_RESET return $response === Password::PASSWORD_RESET
? $this->sendRecoveryResponse($request, $response) ? $this->sendRecoveryResponse($request, $response)
: $this->sendRecoveryFailedResponse($request, $response); : $this->sendRecoveryFailedResponse($request, $response);
} }
/** /**
* Check if the user has set to revoke all credentials. * Check if the user has set to revoke all credentials.
* *
* @param \App\Http\Requests\WebauthnRecoveryRequest $request * @param \App\Http\Requests\WebauthnRecoveryRequest $request
*
* @return bool|mixed * @return bool|mixed
*/ */
protected function shouldRevokeAllCredentials(WebauthnRecoveryRequest $request): mixed protected function shouldRevokeAllCredentials(WebauthnRecoveryRequest $request) : mixed
{ {
return filter_var($request->header('WebAuthn-Unique'), FILTER_VALIDATE_BOOLEAN) return filter_var($request->header('WebAuthn-Unique'), FILTER_VALIDATE_BOOLEAN)
?: $request->input('revokeAll', true); ?: $request->input('revokeAll', true);
} }
/** /**
* Get the response for a successful account recovery. * Get the response for a successful account recovery.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param string $response * @param string $response
*
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*
*/ */
protected function sendRecoveryResponse(Request $request, string $response): JsonResponse protected function sendRecoveryResponse(Request $request, string $response) : JsonResponse
{ {
return response()->json(['message' => __('auth.webauthn.webauthn_login_disabled')]); return response()->json(['message' => __('auth.webauthn.webauthn_login_disabled')]);
} }
/** /**
* Get the response for a failed account recovery. * Get the response for a failed account recovery.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param string $response * @param string $response
*
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Validation\ValidationException
* *
* @throws \Illuminate\Validation\ValidationException
*/ */
protected function sendRecoveryFailedResponse(Request $request, string $response): JsonResponse protected function sendRecoveryFailedResponse(Request $request, string $response) : JsonResponse
{ {
switch ($response) { switch ($response) {
case Password::INVALID_TOKEN: case Password::INVALID_TOKEN:
throw ValidationException::withMessages(['token' => [__('auth.webauthn.invalid_reset_token')]]); throw ValidationException::withMessages(['token' => [__('auth.webauthn.invalid_reset_token')]]);
default: default:
throw ValidationException::withMessages(['email' => [trans($response)]]); throw ValidationException::withMessages(['email' => [trans($response)]]);
} }
} }
} }

@ -17,7 +17,7 @@ class WebAuthnRegisterController extends Controller
* @param \Laragear\WebAuthn\Http\Requests\AttestationRequest $request * @param \Laragear\WebAuthn\Http\Requests\AttestationRequest $request
* @return \Illuminate\Contracts\Support\Responsable * @return \Illuminate\Contracts\Support\Responsable
*/ */
public function options(AttestationRequest $request): Responsable public function options(AttestationRequest $request) : Responsable
{ {
switch (env('WEBAUTHN_USER_VERIFICATION')) { switch (env('WEBAUTHN_USER_VERIFICATION')) {
case WebAuthn::USER_VERIFICATION_DISCOURAGED: case WebAuthn::USER_VERIFICATION_DISCOURAGED:
@ -34,14 +34,13 @@ class WebAuthnRegisterController extends Controller
->toCreate(); ->toCreate();
} }
/** /**
* Registers a device for further WebAuthn authentication. * Registers a device for further WebAuthn authentication.
* *
* @param \Laragear\WebAuthn\Http\Requests\AttestedRequest $request * @param \Laragear\WebAuthn\Http\Requests\AttestedRequest $request
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function register(AttestedRequest $request): Response public function register(AttestedRequest $request) : Response
{ {
$request->save(); $request->save();

@ -2,10 +2,10 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController class Controller extends BaseController
{ {

@ -2,16 +2,15 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Events\ScanForNewReleaseCalled;
use App\Facades\Settings; use App\Facades\Settings;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use App\Events\ScanForNewReleaseCalled;
class SinglePageController extends Controller class SinglePageController extends Controller
{ {
/** /**
* return the main view * return the main view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/ */
public function index() public function index()
@ -21,13 +20,13 @@ class SinglePageController extends Controller
return view('landing')->with([ return view('landing')->with([
'appSettings' => Settings::all()->toJson(), 'appSettings' => Settings::all()->toJson(),
'appConfig' => collect([ 'appConfig' => collect([
'proxyAuth' => config("auth.defaults.guard") === 'reverse-proxy-guard' ? true : false, 'proxyAuth' => config('auth.defaults.guard') === 'reverse-proxy-guard' ? true : false,
'proxyLogoutUrl' => config("2fauth.config.proxyLogoutUrl") ? config("2fauth.config.proxyLogoutUrl") : false, 'proxyLogoutUrl' => config('2fauth.config.proxyLogoutUrl') ? config('2fauth.config.proxyLogoutUrl') : false,
])->toJson(), ])->toJson(),
'lang' => App::currentLocale(), 'lang' => App::currentLocale(),
'isDemoApp' => config("2fauth.config.isDemoApp") ? 'true' : 'false', 'isDemoApp' => config('2fauth.config.isDemoApp') ? 'true' : 'false',
'isTestingApp' => config("2fauth.config.isTestingApp") ? 'true' : 'false', 'isTestingApp' => config('2fauth.config.isTestingApp') ? 'true' : 'false',
'locales' => collect(config("2fauth.locales"))->toJson() /** @phpstan-ignore-line */ 'locales' => collect(config('2fauth.locales'))->toJson(), /** @phpstan-ignore-line */
]); ]);
} }
} }

@ -2,9 +2,8 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Services\ReleaseRadarService;
use App\Http\Controllers\Controller;
use App\Facades\Settings; use App\Facades\Settings;
use App\Services\ReleaseRadarService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -17,7 +16,7 @@ class SystemController extends Controller
*/ */
public function infos(Request $request) public function infos(Request $request)
{ {
$infos = array(); $infos = [];
$infos['Date'] = date(DATE_RFC2822); $infos['Date'] = date(DATE_RFC2822);
$infos['userAgent'] = $request->header('user-agent'); $infos['userAgent'] = $request->header('user-agent');
// App info // App info
@ -50,7 +49,6 @@ class SystemController extends Controller
return response()->json($infos); return response()->json($infos);
} }
/** /**
* Get latest release * Get latest release
* *

@ -20,8 +20,7 @@ class Authenticate extends Middleware
if (empty($guards)) { if (empty($guards)) {
// Will retreive the default guard // Will retreive the default guard
$guards = [null]; $guards = [null];
} } else {
else {
// We replace routes guard by the reverse proxy guard if necessary // We replace routes guard by the reverse proxy guard if necessary
$proxyGuard = 'reverse-proxy-guard'; $proxyGuard = 'reverse-proxy-guard';
@ -33,11 +32,11 @@ class Authenticate extends Middleware
foreach ($guards as $guard) { foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) { if ($this->auth->guard($guard)->check()) {
$this->auth->shouldUse($guard); $this->auth->shouldUse($guard);
return; return;
} }
} }
$this->unauthenticated($request, $guards); $this->unauthenticated($request, $guards);
} }
} }

@ -6,7 +6,6 @@ use Laravel\Passport\Http\Middleware\CreateFreshApiToken as CreateFreshApiToken;
class CustomCreateFreshApiToken extends CreateFreshApiToken class CustomCreateFreshApiToken extends CreateFreshApiToken
{ {
/** /**
* Determine if the request should receive a fresh token. * Determine if the request should receive a fresh token.
* *
@ -15,6 +14,6 @@ class CustomCreateFreshApiToken extends CreateFreshApiToken
*/ */
protected function requestShouldReceiveFreshToken($request) protected function requestShouldReceiveFreshToken($request)
{ {
return !is_null($request->user($this->guard)); return ! is_null($request->user($this->guard));
} }
} }

@ -2,12 +2,12 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure; use App\Facades\Settings;
use Carbon\Carbon; use Carbon\Carbon;
use Closure;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Facades\Settings;
class KickOutInactiveUser class KickOutInactiveUser
{ {
@ -38,7 +38,6 @@ class KickOutInactiveUser
// If user has been inactive longer than the allowed inactivity period // If user has been inactive longer than the allowed inactivity period
if ($kickUserAfterXSecond > 0 && $inactiveFor > $kickUserAfterXSecond) { if ($kickUserAfterXSecond > 0 && $inactiveFor > $kickUserAfterXSecond) {
$user->last_seen_at = $now->format('Y-m-d H:i:s'); $user->last_seen_at = $now->format('Y-m-d H:i:s');
$user->save(); $user->save();

@ -2,8 +2,8 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure;
use Carbon\Carbon; use Carbon\Carbon;
use Closure;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
class LogUserLastSeen class LogUserLastSeen
@ -25,7 +25,7 @@ class LogUserLastSeen
// - Guest // - Guest
// - User authenticated against a bearer token // - User authenticated against a bearer token
// - User authenticated via a reverse-proxy // - User authenticated via a reverse-proxy
if (Auth::guard($guard)->check() && !$request->bearerToken() && config('auth.defaults.guard') !== 'reverse-proxy-guard') { if (Auth::guard($guard)->check() && ! $request->bearerToken() && config('auth.defaults.guard') !== 'reverse-proxy-guard') {
Auth::guard($guard)->user()->last_seen_at = Carbon::now()->format('Y-m-d H:i:s'); Auth::guard($guard)->user()->last_seen_at = Carbon::now()->format('Y-m-d H:i:s');
Auth::guard($guard)->user()->save(); Auth::guard($guard)->user()->save();
break; break;

@ -17,8 +17,7 @@ class RejectIfDemoMode
*/ */
public function handle($request, Closure $next) public function handle($request, Closure $next)
{ {
if (config('2fauth.config.isDemoApp')) {
if( config('2fauth.config.isDemoApp') ) {
Log::info('Cannot request this action in Demo mode'); Log::info('Cannot request this action in Demo mode');
return response()->json(['message' => __('auth.forms.disabled_in_demo')], Response::HTTP_UNAUTHORIZED); return response()->json(['message' => __('auth.forms.disabled_in_demo')], Response::HTTP_UNAUTHORIZED);

@ -20,7 +20,8 @@ class RejectIfReverseProxy
Log::info('Cannot request this action in Demo mode'); Log::info('Cannot request this action in Demo mode');
return response()->json([ return response()->json([
'message' => __('errors.unsupported_with_reverseproxy')], 400); 'message' => __('errors.unsupported_with_reverseproxy'),
], 400);
} }
return $next($request); return $next($request);

@ -2,9 +2,9 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\Facades\Settings;
use Closure; use Closure;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use App\Facades\Settings;
class SetLanguage class SetLanguage
{ {
@ -26,16 +26,17 @@ class SetLanguage
// FI: Settings::get() always returns a fallback value // FI: Settings::get() always returns a fallback value
$lang = Settings::get('lang'); $lang = Settings::get('lang');
if($lang === 'browser') { if ($lang === 'browser') {
$lang = config('app.fallback_locale'); $lang = config('app.fallback_locale');
$accepted = str_replace(' ', '', $request->header("Accept-Language")); $accepted = str_replace(' ', '', $request->header('Accept-Language'));
if ($accepted && $accepted !== '*') { if ($accepted && $accepted !== '*') {
$prefLocales = array_reduce( $prefLocales = array_reduce(
array_diff(explode(',', $accepted), ['*']), array_diff(explode(',', $accepted), ['*']),
function ($res, $el) { function ($res, $el) {
list($l, $q) = array_merge(explode(';q=', $el), [1]); [$l, $q] = array_merge(explode(';q=', $el), [1]);
$res[$l] = (float) $q; $res[$l] = (float) $q;
return $res; return $res;
}, },
[] []

@ -26,7 +26,7 @@ class SkipIfAuthenticated
return response()->json([ return response()->json([
'message' => 'authenticated', 'message' => 'authenticated',
'name' => $user 'name' => $user,
], 200); ], 200);
} }
} }

@ -19,8 +19,7 @@ class TrustProxies extends Middleware
* *
* @var int * @var int
*/ */
protected $headers = protected $headers = Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_PROTO |

@ -2,10 +2,7 @@
namespace App\Http\Requests; namespace App\Http\Requests;
use Illuminate\Support\Facades\DB;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class LoginRequest extends FormRequest class LoginRequest extends FormRequest
{ {
@ -30,7 +27,7 @@ class LoginRequest extends FormRequest
'email' => [ 'email' => [
'required', 'required',
'email', 'email',
new \App\Rules\CaseInsensitiveEmailExists new \App\Rules\CaseInsensitiveEmailExists,
], ],
'password' => 'required|string', 'password' => 'required|string',
]; ];

@ -5,7 +5,6 @@ namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
class UserDeleteRequest extends FormRequest class UserDeleteRequest extends FormRequest
{ {
/** /**

@ -3,7 +3,6 @@
namespace App\Http\Requests; namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
class WebauthnDeviceLostRequest extends FormRequest class WebauthnDeviceLostRequest extends FormRequest
{ {
@ -28,7 +27,7 @@ class WebauthnDeviceLostRequest extends FormRequest
'email' => [ 'email' => [
'required', 'required',
'email', 'email',
new \App\Rules\CaseInsensitiveEmailExists new \App\Rules\CaseInsensitiveEmailExists,
], ],
]; ];
} }

@ -3,7 +3,6 @@
namespace App\Http\Requests; namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
class WebauthnRecoveryRequest extends FormRequest class WebauthnRecoveryRequest extends FormRequest
{ {

@ -2,8 +2,8 @@
namespace App\Listeners; namespace App\Listeners;
use App\Models\TwoFAccount;
use App\Events\GroupDeleting; use App\Events\GroupDeleting;
use App\Models\TwoFAccount;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class DissociateTwofaccountFromGroup class DissociateTwofaccountFromGroup
@ -28,7 +28,7 @@ class DissociateTwofaccountFromGroup
{ {
TwoFAccount::where('group_id', $event->group->id) TwoFAccount::where('group_id', $event->group->id)
->update( ->update(
['group_id' => NULL] ['group_id' => null]
); );
Log::info(sprintf('TwoFAccounts dissociated from group #%d', $event->group->id)); Log::info(sprintf('TwoFAccounts dissociated from group #%d', $event->group->id));

@ -4,22 +4,19 @@ namespace App\Listeners;
use App\Events\ScanForNewReleaseCalled; use App\Events\ScanForNewReleaseCalled;
use App\Services\ReleaseRadarService; use App\Services\ReleaseRadarService;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class ReleaseRadar class ReleaseRadar
{ {
/** /**
* @var ReleaseRadarService $releaseRadar * @var ReleaseRadarService
*/ */
protected $releaseRadar; protected $releaseRadar;
/** /**
* Create the event listener. * Create the event listener.
* *
* @param \App\Services\ReleaseRadarService $releaseRadar * @param \App\Services\ReleaseRadarService $releaseRadar
*
* @return void * @return void
*/ */
public function __construct(ReleaseRadarService $releaseRadar) public function __construct(ReleaseRadarService $releaseRadar)
@ -27,7 +24,6 @@ class ReleaseRadar
$this->releaseRadar = $releaseRadar; $this->releaseRadar = $releaseRadar;
} }
/** /**
* Handle the event. * Handle the event.
* *

@ -3,16 +3,15 @@
namespace App\Models; namespace App\Models;
use App\Events\GroupDeleting; use App\Events\GroupDeleting;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/** /**
* @property int $twofaccounts_count * @property int $twofaccounts_count
*/ */
class Group extends Model class Group extends Model
{ {
use HasFactory; use HasFactory;
/** /**
@ -22,7 +21,6 @@ class Group extends Model
*/ */
protected $fillable = ['name']; protected $fillable = ['name'];
/** /**
* The accessors to append to the model's array form. * The accessors to append to the model's array form.
* *
@ -30,7 +28,6 @@ class Group extends Model
*/ */
protected $appends = []; protected $appends = [];
/** /**
* The attributes that should be hidden for arrays. * The attributes that should be hidden for arrays.
* *
@ -38,7 +35,6 @@ class Group extends Model
*/ */
protected $hidden = ['created_at', 'updated_at']; protected $hidden = ['created_at', 'updated_at'];
/** /**
* The attributes that should be cast. * The attributes that should be cast.
* *
@ -48,7 +44,6 @@ class Group extends Model
'twofaccounts_count' => 'integer', 'twofaccounts_count' => 'integer',
]; ];
/** /**
* The event map for the model. * The event map for the model.
* *
@ -58,7 +53,6 @@ class Group extends Model
'deleting' => GroupDeleting::class, 'deleting' => GroupDeleting::class,
]; ];
/** /**
* Override The "booting" method of the model * Override The "booting" method of the model
* *
@ -75,7 +69,6 @@ class Group extends Model
}); });
} }
/** /**
* Get the TwoFAccounts of the group. * Get the TwoFAccounts of the group.
* *

@ -4,7 +4,6 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Option extends Model class Option extends Model
{ {
/** /**
@ -17,7 +16,6 @@ class Option extends Model
'value', 'value',
]; ];
/** /**
* Indicates if the model should be timestamped. * Indicates if the model should be timestamped.
* *
@ -25,12 +23,10 @@ class Option extends Model
*/ */
public $timestamps = false; public $timestamps = false;
/** /**
* Casts. * Casts.
* *
* @var array<string, string> * @var array<string, string>
*/ */
protected $casts = []; protected $casts = [];
} }

@ -2,9 +2,9 @@
namespace App\Models\Traits; namespace App\Models\Traits;
use Illuminate\Support\Str;
use App\Notifications\WebauthnRecoveryNotification; use App\Notifications\WebauthnRecoveryNotification;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Str;
/** /**
* @see \App\Models\WebAuthnAuthenticatable * @see \App\Models\WebAuthnAuthenticatable
@ -17,7 +17,7 @@ trait WebAuthnManageCredentials
* *
* @return string * @return string
*/ */
public function userHandle(): string public function userHandle() : string
{ {
// Laragear\WebAuthn uses Ramsey\Uuid\Uuid::fromString()->getHex()->toString() // Laragear\WebAuthn uses Ramsey\Uuid\Uuid::fromString()->getHex()->toString()
// to obtain a UUID v4 with dashes removed and uses it as user_id (aka userHandle) // to obtain a UUID v4 with dashes removed and uses it as user_id (aka userHandle)
@ -28,7 +28,6 @@ trait WebAuthnManageCredentials
?? str_replace('-', '', Str::uuid()->toString()); ?? str_replace('-', '', Str::uuid()->toString());
} }
/** /**
* Saves a new alias for a given WebAuthn credential. * Saves a new alias for a given WebAuthn credential.
* *
@ -36,19 +35,18 @@ trait WebAuthnManageCredentials
* @param string $alias * @param string $alias
* @return bool * @return bool
*/ */
public function renameCredential(string $id, string $alias): bool public function renameCredential(string $id, string $alias) : bool
{ {
return boolval($this->webAuthnCredentials()->whereKey($id)->update(['alias' => $alias])); return boolval($this->webAuthnCredentials()->whereKey($id)->update(['alias' => $alias]));
} }
/** /**
* Removes one or more credentials previously registered. * Removes one or more credentials previously registered.
* *
* @param string|array $id * @param string|array $id
* @return void * @return void
*/ */
public function flushCredential($id): void public function flushCredential($id) : void
{ {
if (! $this->relationLoaded('webAuthnCredentials')) { if (! $this->relationLoaded('webAuthnCredentials')) {
$this->webAuthnCredentials()->whereKey($id)->delete(); $this->webAuthnCredentials()->whereKey($id)->delete();
@ -63,15 +61,13 @@ trait WebAuthnManageCredentials
} }
} }
/** /**
* Sends a webauthn recovery email to the user. * Sends a webauthn recovery email to the user.
* *
* @param string $token * @param string $token
*
* @return void * @return void
*/ */
public function sendWebauthnRecoveryNotification(string $token): void public function sendWebauthnRecoveryNotification(string $token) : void
{ {
// $accountRecoveryNotification = new WebauthnRecoveryNotification($token); // $accountRecoveryNotification = new WebauthnRecoveryNotification($token);
// $accountRecoveryNotification->toMailUsing(null); // $accountRecoveryNotification->toMailUsing(null);
@ -92,6 +88,5 @@ trait WebAuthnManageCredentials
// }); // });
$this->notify(new WebauthnRecoveryNotification($token)); $this->notify(new WebauthnRecoveryNotification($token));
} }
} }

@ -2,55 +2,62 @@
namespace App\Models; namespace App\Models;
use Exception;
use App\Services\LogoService;
use App\Facades\Settings;
use App\Models\Dto\TotpDto;
use App\Models\Dto\HotpDto;
use App\Events\TwoFAccountDeleted; use App\Events\TwoFAccountDeleted;
use App\Exceptions\InvalidSecretException;
use App\Exceptions\InvalidOtpParameterException; use App\Exceptions\InvalidOtpParameterException;
use App\Exceptions\UnsupportedOtpTypeException; use App\Exceptions\InvalidSecretException;
use App\Exceptions\UndecipherableException; use App\Exceptions\UndecipherableException;
use Illuminate\Validation\ValidationException; use App\Exceptions\UnsupportedOtpTypeException;
use Spatie\EloquentSortable\Sortable; use App\Facades\Settings;
use Spatie\EloquentSortable\SortableTrait; use App\Helpers\Helpers;
use OTPHP\TOTP; use App\Models\Dto\HotpDto;
use OTPHP\HOTP; use App\Models\Dto\TotpDto;
use OTPHP\Factory; use App\Services\LogoService;
use SteamTotp\SteamTotp; use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Validation\ValidationException;
use OTPHP\Factory;
use OTPHP\HOTP;
use OTPHP\TOTP;
use ParagonIE\ConstantTime\Base32; use ParagonIE\ConstantTime\Base32;
use Illuminate\Support\Facades\App; use Spatie\EloquentSortable\Sortable;
use Illuminate\Support\Facades\Http; use Spatie\EloquentSortable\SortableTrait;
use App\Helpers\Helpers; use SteamTotp\SteamTotp;
class TwoFAccount extends Model implements Sortable class TwoFAccount extends Model implements Sortable
{ {
use SortableTrait, HasFactory; use SortableTrait, HasFactory;
const TOTP = 'totp'; const TOTP = 'totp';
const HOTP = 'hotp'; const HOTP = 'hotp';
const STEAM_TOTP = 'steamtotp'; const STEAM_TOTP = 'steamtotp';
const SHA1 = 'sha1'; const SHA1 = 'sha1';
const MD5 = 'md5'; const MD5 = 'md5';
const SHA256 = 'sha256'; const SHA256 = 'sha256';
const SHA512 = 'sha512'; const SHA512 = 'sha512';
const DEFAULT_PERIOD = 30; const DEFAULT_PERIOD = 30;
const DEFAULT_COUNTER = 0; const DEFAULT_COUNTER = 0;
const DEFAULT_DIGITS = 6; const DEFAULT_DIGITS = 6;
const DEFAULT_ALGORITHM = self::SHA1; const DEFAULT_ALGORITHM = self::SHA1;
const DUPLICATE_ID = -1; const DUPLICATE_ID = -1;
const FAKE_ID = -2; const FAKE_ID = -2;
private const IMAGELINK_STORAGE_PATH = 'imagesLink/'; private const IMAGELINK_STORAGE_PATH = 'imagesLink/';
@ -80,7 +87,6 @@ class TwoFAccount extends Model implements Sortable
// 'icon' // 'icon'
]; ];
/** /**
* The table associated with the model. * The table associated with the model.
* *
@ -88,7 +94,6 @@ class TwoFAccount extends Model implements Sortable
*/ */
protected $table = 'twofaccounts'; protected $table = 'twofaccounts';
/** /**
* The accessors to append to the model's array form. * The accessors to append to the model's array form.
* *
@ -96,7 +101,6 @@ class TwoFAccount extends Model implements Sortable
*/ */
public $appends = []; public $appends = [];
/** /**
* The model's default values for attributes. * The model's default values for attributes.
* *
@ -107,7 +111,6 @@ class TwoFAccount extends Model implements Sortable
'algorithm' => self::SHA1, 'algorithm' => self::SHA1,
]; ];
/** /**
* The attributes that should be hidden for arrays. * The attributes that should be hidden for arrays.
* *
@ -115,7 +118,6 @@ class TwoFAccount extends Model implements Sortable
*/ */
protected $hidden = []; protected $hidden = [];
/** /**
* The attributes that should be cast. * The attributes that should be cast.
* *
@ -123,7 +125,6 @@ class TwoFAccount extends Model implements Sortable
*/ */
protected $casts = []; protected $casts = [];
/** /**
* The event map for the model. * The event map for the model.
* *
@ -133,7 +134,6 @@ class TwoFAccount extends Model implements Sortable
'deleted' => TwoFAccountDeleted::class, 'deleted' => TwoFAccountDeleted::class,
]; ];
/** /**
* Override The "booting" method of the model * Override The "booting" method of the model
* *
@ -144,9 +144,15 @@ class TwoFAccount extends Model implements Sortable
parent::boot(); parent::boot();
static::saving(function (TwoFAccount $twofaccount) { static::saving(function (TwoFAccount $twofaccount) {
if (!$twofaccount->legacy_uri) $twofaccount->legacy_uri = $twofaccount->getURI(); if (! $twofaccount->legacy_uri) {
if ($twofaccount->otp_type == TwoFAccount::TOTP && !$twofaccount->period) $twofaccount->period = TwoFAccount::DEFAULT_PERIOD; $twofaccount->legacy_uri = $twofaccount->getURI();
if ($twofaccount->otp_type == TwoFAccount::HOTP && !$twofaccount->counter) $twofaccount->counter = TwoFAccount::DEFAULT_COUNTER; }
if ($twofaccount->otp_type == TwoFAccount::TOTP && ! $twofaccount->period) {
$twofaccount->period = TwoFAccount::DEFAULT_PERIOD;
}
if ($twofaccount->otp_type == TwoFAccount::HOTP && ! $twofaccount->counter) {
$twofaccount->counter = TwoFAccount::DEFAULT_COUNTER;
}
}); });
// static::deleted(function ($model) { // static::deleted(function ($model) {
@ -154,7 +160,6 @@ class TwoFAccount extends Model implements Sortable
// }); // });
} }
/** /**
* Settings for @spatie/eloquent-sortable package * Settings for @spatie/eloquent-sortable package
* *
@ -165,7 +170,6 @@ class TwoFAccount extends Model implements Sortable
'sort_when_creating' => true, 'sort_when_creating' => true,
]; ];
/** /**
* The OTP generator. * The OTP generator.
* Instanciated as null to keep the model light * Instanciated as null to keep the model light
@ -174,7 +178,6 @@ class TwoFAccount extends Model implements Sortable
*/ */
protected $generator = null; protected $generator = null;
/** /**
* Get legacy_uri attribute * Get legacy_uri attribute
* *
@ -183,9 +186,9 @@ class TwoFAccount extends Model implements Sortable
*/ */
public function getLegacyUriAttribute($value) public function getLegacyUriAttribute($value)
{ {
return $this->decryptOrReturn($value); return $this->decryptOrReturn($value);
} }
/** /**
* Set legacy_uri attribute * Set legacy_uri attribute
* *
@ -198,7 +201,6 @@ class TwoFAccount extends Model implements Sortable
$this->attributes['legacy_uri'] = $this->encryptOrReturn($value); $this->attributes['legacy_uri'] = $this->encryptOrReturn($value);
} }
/** /**
* Get account attribute * Get account attribute
* *
@ -207,9 +209,9 @@ class TwoFAccount extends Model implements Sortable
*/ */
public function getAccountAttribute($value) public function getAccountAttribute($value)
{ {
return $this->decryptOrReturn($value); return $this->decryptOrReturn($value);
} }
/** /**
* Set account attribute * Set account attribute
* *
@ -222,7 +224,6 @@ class TwoFAccount extends Model implements Sortable
$this->attributes['account'] = $this->encryptOrReturn($value); $this->attributes['account'] = $this->encryptOrReturn($value);
} }
/** /**
* Get secret attribute * Get secret attribute
* *
@ -231,9 +232,9 @@ class TwoFAccount extends Model implements Sortable
*/ */
public function getSecretAttribute($value) public function getSecretAttribute($value)
{ {
return $this->decryptOrReturn($value); return $this->decryptOrReturn($value);
} }
/** /**
* Set secret attribute * Set secret attribute
* *
@ -246,7 +247,6 @@ class TwoFAccount extends Model implements Sortable
$this->attributes['secret'] = $this->encryptOrReturn($value); $this->attributes['secret'] = $this->encryptOrReturn($value);
} }
/** /**
* Set digits attribute * Set digits attribute
* *
@ -255,10 +255,9 @@ class TwoFAccount extends Model implements Sortable
*/ */
public function setDigitsAttribute($value) public function setDigitsAttribute($value)
{ {
$this->attributes['digits'] = !$value ? 6 : $value; $this->attributes['digits'] = ! $value ? 6 : $value;
} }
/** /**
* Set algorithm attribute * Set algorithm attribute
* *
@ -267,10 +266,9 @@ class TwoFAccount extends Model implements Sortable
*/ */
public function setAlgorithmAttribute($value) public function setAlgorithmAttribute($value)
{ {
$this->attributes['algorithm'] = !$value ? self::SHA1 : strtolower($value); $this->attributes['algorithm'] = ! $value ? self::SHA1 : strtolower($value);
} }
/** /**
* Set period attribute * Set period attribute
* *
@ -279,10 +277,9 @@ class TwoFAccount extends Model implements Sortable
*/ */
public function setPeriodAttribute($value) public function setPeriodAttribute($value)
{ {
$this->attributes['period'] = !$value && $this->otp_type === self::TOTP ? self::DEFAULT_PERIOD : $value; $this->attributes['period'] = ! $value && $this->otp_type === self::TOTP ? self::DEFAULT_PERIOD : $value;
} }
/** /**
* Set counter attribute * Set counter attribute
* *
@ -294,19 +291,19 @@ class TwoFAccount extends Model implements Sortable
$this->attributes['counter'] = blank($value) && $this->otp_type === self::HOTP ? self::DEFAULT_COUNTER : $value; $this->attributes['counter'] = blank($value) && $this->otp_type === self::HOTP ? self::DEFAULT_COUNTER : $value;
} }
/** /**
* Returns a One-Time Password with its parameters * Returns a One-Time Password with its parameters
* *
* @return TotpDto|HotpDto
*
* @throws InvalidSecretException The secret is not a valid base32 encoded string * @throws InvalidSecretException The secret is not a valid base32 encoded string
* @throws UndecipherableException The secret cannot be deciphered * @throws UndecipherableException The secret cannot be deciphered
* @throws UnsupportedOtpTypeException The defined OTP type is not supported * @throws UnsupportedOtpTypeException The defined OTP type is not supported
* @throws InvalidOtpParameterException One OTP parameter is invalid * @throws InvalidOtpParameterException One OTP parameter is invalid
* @return TotpDto|HotpDto
*/ */
public function getOTP() public function getOTP()
{ {
Log::info(sprintf('OTP requested for TwoFAccount (%s)', $this->id ? 'id:'.$this->id: 'preview')); Log::info(sprintf('OTP requested for TwoFAccount (%s)', $this->id ? 'id:' . $this->id : 'preview'));
// Early exit if the model has an undecipherable secret // Early exit if the model has an undecipherable secret
if (strtolower($this->secret) === __('errors.indecipherable')) { if (strtolower($this->secret) === __('errors.indecipherable')) {
@ -318,8 +315,7 @@ class TwoFAccount extends Model implements Sortable
$this->initGenerator(); $this->initGenerator();
try { try {
if ( $this->otp_type === self::HOTP ) { if ($this->otp_type === self::HOTP) {
$OtpDto = new HotpDto(); $OtpDto = new HotpDto();
$OtpDto->otp_type = $this->otp_type; $OtpDto->otp_type = $this->otp_type;
$counter = $this->generator->getParameter('counter'); $counter = $this->generator->getParameter('counter');
@ -330,9 +326,7 @@ class TwoFAccount extends Model implements Sortable
if ($this->id) { if ($this->id) {
$this->save(); $this->save();
} }
} } else {
else {
$OtpDto = new TotpDto(); $OtpDto = new TotpDto();
$OtpDto->otp_type = $this->otp_type; $OtpDto->otp_type = $this->otp_type;
$OtpDto->generated_at = time(); $OtpDto->generated_at = time();
@ -342,12 +336,10 @@ class TwoFAccount extends Model implements Sortable
$OtpDto->period = $this->period; $OtpDto->period = $this->period;
} }
Log::info(sprintf('New OTP generated for TwoFAccount (%s)', $this->id ? 'id:'.$this->id: 'preview')); Log::info(sprintf('New OTP generated for TwoFAccount (%s)', $this->id ? 'id:' . $this->id : 'preview'));
return $OtpDto; return $OtpDto;
} catch (\Exception|\Throwable $ex) {
}
catch (\Exception|\Throwable $ex) {
Log::error('An error occured, OTP generation aborted'); Log::error('An error occured, OTP generation aborted');
// Currently a secret issue is the only possible exception thrown by OTPHP for this stack // Currently a secret issue is the only possible exception thrown by OTPHP for this stack
// so it is Ok to send the corresponding 2FAuth exception. // so it is Ok to send the corresponding 2FAuth exception.
@ -356,7 +348,6 @@ class TwoFAccount extends Model implements Sortable
} }
} }
/** /**
* Fill the model using an array of OTP parameters. * Fill the model using an array of OTP parameters.
* Missing parameters will be set with default values * Missing parameters will be set with default values
@ -385,11 +376,11 @@ class TwoFAccount extends Model implements Sortable
$this->enforceAsSteam(); $this->enforceAsSteam();
} }
if (!$this->icon && $skipIconFetching) { if (! $this->icon && $skipIconFetching) {
$this->icon = $this->getDefaultIcon(); $this->icon = $this->getDefaultIcon();
} }
if (!$this->icon && Settings::get('getOfficialIcons') && !$skipIconFetching) { if (! $this->icon && Settings::get('getOfficialIcons') && ! $skipIconFetching) {
$this->icon = $this->getDefaultIcon(); $this->icon = $this->getDefaultIcon();
} }
@ -398,7 +389,6 @@ class TwoFAccount extends Model implements Sortable
return $this; return $this;
} }
/** /**
* Fill the model by parsing an otpauth URI * Fill the model by parsing an otpauth URI
* *
@ -409,20 +399,19 @@ class TwoFAccount extends Model implements Sortable
// First we instanciate the OTP generator // First we instanciate the OTP generator
try { try {
$this->generator = Factory::loadFromProvisioningUri($uri); $this->generator = Factory::loadFromProvisioningUri($uri);
} } catch (\Assert\AssertionFailedException|\Assert\InvalidArgumentException|\Exception|\Throwable $ex) {
catch (\Assert\AssertionFailedException|\Assert\InvalidArgumentException|\Exception|\Throwable $ex) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'uri' => __('validation.custom.uri.regex', ['attribute' => 'uri']) 'uri' => __('validation.custom.uri.regex', ['attribute' => 'uri']),
]); ]);
} }
// As loadFromProvisioningUri() accept URI without label (nor account nor service) we check // As loadFromProvisioningUri() accept URI without label (nor account nor service) we check
// that the account is set // that the account is set
if ( ! $this->generator->getLabel() ) { if (! $this->generator->getLabel()) {
Log::error('URI passed to fillWithURI() must contain a label'); Log::error('URI passed to fillWithURI() must contain a label');
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'label' => __('validation.custom.label.required') 'label' => __('validation.custom.label.required'),
]); ]);
} }
@ -443,7 +432,7 @@ class TwoFAccount extends Model implements Sortable
$this->icon = $this->storeImageAsIcon($this->generator->getParameter('image')); $this->icon = $this->storeImageAsIcon($this->generator->getParameter('image'));
} }
if (!$this->icon && Settings::get('getOfficialIcons') && !$skipIconFetching) { if (! $this->icon && Settings::get('getOfficialIcons') && ! $skipIconFetching) {
$this->icon = $this->getDefaultIcon(); $this->icon = $this->getDefaultIcon();
} }
@ -452,7 +441,6 @@ class TwoFAccount extends Model implements Sortable
return $this; return $this;
} }
/** /**
* Sets model attributes to STEAM values * Sets model attributes to STEAM values
*/ */
@ -466,7 +454,6 @@ class TwoFAccount extends Model implements Sortable
Log::info(sprintf('TwoFAccount configured as Steam account')); Log::info(sprintf('TwoFAccount configured as Steam account'));
} }
/** /**
* Returns the OTP type of the instanciated OTP generator * Returns the OTP type of the instanciated OTP generator
* *
@ -477,7 +464,6 @@ class TwoFAccount extends Model implements Sortable
return Arr::get($this->generatorClassMap, get_class($this->generator)); return Arr::get($this->generatorClassMap, get_class($this->generator));
} }
/** /**
* Returns an otpauth URI built with model attribute values * Returns an otpauth URI built with model attribute values
*/ */
@ -488,9 +474,9 @@ class TwoFAccount extends Model implements Sortable
return $this->generator->getProvisioningUri(); return $this->generator->getProvisioningUri();
} }
/** /**
* Instanciates the OTP generator with model attribute values * Instanciates the OTP generator with model attribute values
*
* @throws UnsupportedOtpTypeException The defined OTP type is not supported * @throws UnsupportedOtpTypeException The defined OTP type is not supported
* @throws InvalidOtpParameterException One OTP parameter is invalid * @throws InvalidOtpParameterException One OTP parameter is invalid
*/ */
@ -524,14 +510,16 @@ class TwoFAccount extends Model implements Sortable
throw new UnsupportedOtpTypeException(); throw new UnsupportedOtpTypeException();
} }
if ($this->service) $this->generator->setIssuer($this->service); if ($this->service) {
if ($this->account) $this->generator->setLabel($this->account); $this->generator->setIssuer($this->service);
} }
catch (UnsupportedOtpTypeException $exception) { if ($this->account) {
$this->generator->setLabel($this->account);
}
} catch (UnsupportedOtpTypeException $exception) {
Log::error(sprintf('%s is not an OTP type supported by the current generator', $this->otp_type)); Log::error(sprintf('%s is not an OTP type supported by the current generator', $this->otp_type));
throw $exception; throw $exception;
} } catch (\Exception|\Throwable $exception) {
catch (\Exception|\Throwable $exception) {
throw new InvalidOtpParameterException($exception->getMessage()); throw new InvalidOtpParameterException($exception->getMessage());
} }
} }
@ -545,7 +533,7 @@ class TwoFAccount extends Model implements Sortable
{ {
try { try {
$path_parts = pathinfo($url); $path_parts = pathinfo($url);
$newFilename = Helpers::getUniqueFilename($path_parts['extension']); //Str::random(40).'.'.$path_parts['extension']; $newFilename = Helpers::getUniqueFilename($path_parts['extension']);
$imageFile = self::IMAGELINK_STORAGE_PATH . $newFilename; $imageFile = self::IMAGELINK_STORAGE_PATH . $newFilename;
try { try {
@ -554,22 +542,19 @@ class TwoFAccount extends Model implements Sortable
if ($response->successful()) { if ($response->successful()) {
Storage::disk('imagesLink')->put($newFilename, $response->body()); Storage::disk('imagesLink')->put($newFilename, $response->body());
} }
} } catch (\Exception $exception) {
catch (\Exception $exception) {
Log::error(sprintf('Cannot fetch imageLink at "%s"', $url)); Log::error(sprintf('Cannot fetch imageLink at "%s"', $url));
} }
if ( in_array(Storage::mimeType($imageFile), ['image/png', 'image/jpeg', 'image/webp', 'image/bmp']) if (in_array(Storage::mimeType($imageFile), ['image/png', 'image/jpeg', 'image/webp', 'image/bmp'])
&& getimagesize(storage_path() . '/app/' . $imageFile) ) && getimagesize(storage_path() . '/app/' . $imageFile)) {
{
// Should be a valid image, we move it to the icons disk // Should be a valid image, we move it to the icons disk
if (Storage::disk('icons')->put($newFilename, Storage::disk('imagesLink')->get($newFilename))) { if (Storage::disk('icons')->put($newFilename, Storage::disk('imagesLink')->get($newFilename))) {
Storage::disk('imagesLink')->delete($newFilename); Storage::disk('imagesLink')->delete($newFilename);
} }
Log::info(sprintf('Icon file %s stored', $newFilename)); Log::info(sprintf('Icon file %s stored', $newFilename));
} } else {
else {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
Storage::disk('imagesLink')->delete($newFilename); Storage::disk('imagesLink')->delete($newFilename);
throw new \Exception('Unsupported mimeType or missing image on storage'); throw new \Exception('Unsupported mimeType or missing image on storage');
@ -581,12 +566,12 @@ class TwoFAccount extends Model implements Sortable
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
catch (\Exception|\Throwable $ex) { catch (\Exception|\Throwable $ex) {
Log::error(sprintf('Icon storage failed: %s', $ex->getMessage())); Log::error(sprintf('Icon storage failed: %s', $ex->getMessage()));
return null; return null;
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
/** /**
* Fetch a logo in the tfa directory and store it as a new stand alone icon * Fetch a logo in the tfa directory and store it as a new stand alone icon
* *
@ -599,28 +584,23 @@ class TwoFAccount extends Model implements Sortable
return Settings::get('getOfficialIcons') ? $logoService->getIcon($this->service) : null; return Settings::get('getOfficialIcons') ? $logoService->getIcon($this->service) : null;
} }
/** /**
* Returns an acceptable value * Returns an acceptable value
*/ */
private function decryptOrReturn(mixed $value) : mixed private function decryptOrReturn(mixed $value) : mixed
{ {
// Decipher when needed // Decipher when needed
if ( Settings::get('useEncryption') && $value ) if (Settings::get('useEncryption') && $value) {
{
try { try {
return Crypt::decryptString($value); return Crypt::decryptString($value);
} } catch (Exception $ex) {
catch (Exception $ex) {
return __('errors.indecipherable'); return __('errors.indecipherable');
} }
} } else {
else {
return $value; return $value;
} }
} }
/** /**
* Encrypt a value * Encrypt a value
*/ */
@ -629,5 +609,4 @@ class TwoFAccount extends Model implements Sortable
// should be replaced by laravel 8 attribute encryption casting // should be replaced by laravel 8 attribute encryption casting
return Settings::get('useEncryption') ? Crypt::encryptString($value) : $value; return Settings::get('useEncryption') ? Crypt::encryptString($value) : $value;
} }
} }

@ -2,14 +2,14 @@
namespace App\Models; namespace App\Models;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Laragear\WebAuthn\WebAuthnAuthentication;
use App\Models\Traits\WebAuthnManageCredentials; use App\Models\Traits\WebAuthnManageCredentials;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Log;
use Laragear\WebAuthn\WebAuthnAuthentication;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable implements WebAuthnAuthenticatable class User extends Authenticatable implements WebAuthnAuthenticatable
{ {
@ -59,6 +59,7 @@ class User extends Authenticatable implements WebAuthnAuthenticatable
/** /**
* set Email attribute * set Email attribute
*
* @param string $value * @param string $value
*/ */
public function setEmailAttribute($value) : void public function setEmailAttribute($value) : void
@ -66,14 +67,13 @@ class User extends Authenticatable implements WebAuthnAuthenticatable
$this->attributes['email'] = strtolower($value); $this->attributes['email'] = strtolower($value);
} }
/** /**
* Returns an WebAuthnAuthenticatable user from a given Credential ID. * Returns an WebAuthnAuthenticatable user from a given Credential ID.
* *
* @param string $id * @param string $id
* @return WebAuthnAuthenticatable|null * @return WebAuthnAuthenticatable|null
*/ */
public static function getFromCredentialId(string $id): ?WebAuthnAuthenticatable public static function getFromCredentialId(string $id) : ?WebAuthnAuthenticatable
{ {
return static::whereHas( return static::whereHas(
'webauthnCredentials', 'webauthnCredentials',

@ -11,8 +11,7 @@ interface WebAuthnAuthenticatable extends Authenticatable
* *
* @return string * @return string
*/ */
public function userHandle(): string; public function userHandle() : string;
/** /**
* Saves a new alias for a given WebAuthn credential. * Saves a new alias for a given WebAuthn credential.
@ -21,8 +20,7 @@ interface WebAuthnAuthenticatable extends Authenticatable
* @param string $alias * @param string $alias
* @return bool * @return bool
*/ */
public function renameCredential(string $id, string $alias): bool; public function renameCredential(string $id, string $alias) : bool;
/** /**
* Removes one or more credentials previously registered. * Removes one or more credentials previously registered.
@ -30,8 +28,7 @@ interface WebAuthnAuthenticatable extends Authenticatable
* @param string|array $id * @param string|array $id
* @return void * @return void
*/ */
public function flushCredential($id): void; public function flushCredential($id) : void;
/** /**
* Sends a webauthn recovery email to the user. * Sends a webauthn recovery email to the user.
@ -39,5 +36,5 @@ interface WebAuthnAuthenticatable extends Authenticatable
* @param string $token * @param string $token
* @return void * @return void
*/ */
public function sendWebauthnRecoveryNotification(string $token): void; public function sendWebauthnRecoveryNotification(string $token) : void;
} }

@ -2,15 +2,14 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Resources\Json\JsonResource;
use Laravel\Passport\Console\ClientCommand; use Laravel\Passport\Console\ClientCommand;
use Laravel\Passport\Console\InstallCommand; use Laravel\Passport\Console\InstallCommand;
use Laravel\Passport\Console\KeysCommand; use Laravel\Passport\Console\KeysCommand;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
/** /**

@ -2,16 +2,15 @@
namespace App\Providers; namespace App\Providers;
use App\Extensions\RemoteUserProvider;
use App\Extensions\WebauthnCredentialBroker;
use App\Facades\Settings;
use App\Services\Auth\ReverseProxyGuard;
use Illuminate\Auth\Passwords\DatabaseTokenRepository;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use App\Services\Auth\ReverseProxyGuard;
use App\Extensions\RemoteUserProvider;
use App\Facades\Settings;
use Illuminate\Support\Facades\Config;
use RuntimeException;
use App\Extensions\WebauthnCredentialBroker;
use Illuminate\Auth\Passwords\DatabaseTokenRepository;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use RuntimeException;
class AuthServiceProvider extends ServiceProvider class AuthServiceProvider extends ServiceProvider
{ {
@ -24,20 +23,19 @@ class AuthServiceProvider extends ServiceProvider
// 'App\Models\Model' => 'App\Policies\ModelPolicy', // 'App\Models\Model' => 'App\Policies\ModelPolicy',
]; ];
/** /**
* Register the service provider. * Register the service provider.
* *
* @return void * @return void
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function register(): void public function register() : void
{ {
$this->app->singleton( $this->app->singleton(
WebauthnCredentialBroker::class, WebauthnCredentialBroker::class,
static function ($app) { static function ($app) {
if (!$config = $app['config']['auth.passwords.webauthn']) { if (! $config = $app['config']['auth.passwords.webauthn']) {
throw new RuntimeException('You must set the [webauthn] key broker in [auth] config.'); throw new RuntimeException('You must set the [webauthn] key broker in [auth] config.');
} }
@ -62,7 +60,6 @@ class AuthServiceProvider extends ServiceProvider
); );
} }
/** /**
* Register any authentication / authorization services. * Register any authentication / authorization services.
* *
@ -86,7 +83,6 @@ class AuthServiceProvider extends ServiceProvider
return new ReverseProxyGuard(Auth::createUserProvider($config['provider'])); return new ReverseProxyGuard(Auth::createUserProvider($config['provider']));
}); });
// Previously we were using a custom user provider derived from the Larapass user provider // Previously we were using a custom user provider derived from the Larapass user provider
// in order to honor the "useWebauthnOnly" user option. // in order to honor the "useWebauthnOnly" user option.
// Since Laragear\WebAuthn now replaces DarkGhostHunter\Larapass, the new approach is // Since Laragear\WebAuthn now replaces DarkGhostHunter\Larapass, the new approach is
@ -94,7 +90,7 @@ class AuthServiceProvider extends ServiceProvider
// with a custom closure that uses the "useWebauthnOnly" user option // with a custom closure that uses the "useWebauthnOnly" user option
Auth::provider( Auth::provider(
'eloquent-webauthn', 'eloquent-webauthn',
static function (\Illuminate\Contracts\Foundation\Application $app, array $config): \Laragear\WebAuthn\Auth\WebAuthnUserProvider { static function (\Illuminate\Contracts\Foundation\Application $app, array $config) : \Laragear\WebAuthn\Auth\WebAuthnUserProvider {
return new \Laragear\WebAuthn\Auth\WebAuthnUserProvider( return new \Laragear\WebAuthn\Auth\WebAuthnUserProvider(
$app->make('hash'), $app->make('hash'),
$config['model'], $config['model'],
@ -104,7 +100,6 @@ class AuthServiceProvider extends ServiceProvider
} }
); );
// Normally we should set the Passport routes here using Passport::routes(). // Normally we should set the Passport routes here using Passport::routes().
// If so the passport routes would be set for both 'web' and 'api' middlewares without // If so the passport routes would be set for both 'web' and 'api' middlewares without
// possibility to exclude the web middleware (we can only pass additional middlewares to Passport::routes()) // possibility to exclude the web middleware (we can only pass additional middlewares to Passport::routes())

@ -2,8 +2,8 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast; use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider class BroadcastServiceProvider extends ServiceProvider
{ {

@ -3,11 +3,11 @@
namespace App\Providers; namespace App\Providers;
use App\Events\GroupDeleting; use App\Events\GroupDeleting;
use App\Events\TwoFAccountDeleted;
use App\Events\ScanForNewReleaseCalled; use App\Events\ScanForNewReleaseCalled;
use App\Listeners\ReleaseRadar; use App\Events\TwoFAccountDeleted;
use App\Listeners\CleanIconStorage; use App\Listeners\CleanIconStorage;
use App\Listeners\DissociateTwofaccountFromGroup; use App\Listeners\DissociateTwofaccountFromGroup;
use App\Listeners\ReleaseRadar;
use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

@ -4,8 +4,8 @@ namespace App\Providers;
use App\Factories\MigratorFactory; use App\Factories\MigratorFactory;
use App\Factories\MigratorFactoryInterface; use App\Factories\MigratorFactoryInterface;
use App\Services\Migrators\GoogleAuthMigrator;
use App\Services\Migrators\AegisMigrator; use App\Services\Migrators\AegisMigrator;
use App\Services\Migrators\GoogleAuthMigrator;
use App\Services\Migrators\PlainTextMigrator; use App\Services\Migrators\PlainTextMigrator;
use App\Services\Migrators\TwoFASMigrator; use App\Services\Migrators\TwoFASMigrator;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;

@ -2,13 +2,13 @@
namespace App\Providers; namespace App\Providers;
use App\Services\LogoService;
use App\Services\SettingService;
use App\Services\ReleaseRadarService;
use App\Services\TwoFAccountService;
use App\Factories\MigratorFactoryInterface; use App\Factories\MigratorFactoryInterface;
use Illuminate\Support\ServiceProvider; use App\Services\LogoService;
use App\Services\ReleaseRadarService;
use App\Services\SettingService;
use App\Services\TwoFAccountService;
use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class TwoFAuthServiceProvider extends ServiceProvider implements DeferrableProvider class TwoFAuthServiceProvider extends ServiceProvider implements DeferrableProvider
{ {
@ -46,7 +46,6 @@ class TwoFAuthServiceProvider extends ServiceProvider implements DeferrableProvi
// //
} }
/** /**
* Get the services provided by the provider. * Get the services provided by the provider.
* *

@ -30,12 +30,14 @@ class CaseInsensitiveEmailExists implements Rule
->whereRaw('email = \'' . strtolower($value) . '\'' . ('sqlite' === config('database.default') ? ' COLLATE NOCASE' : '')) ->whereRaw('email = \'' . strtolower($value) . '\'' . ('sqlite' === config('database.default') ? ' COLLATE NOCASE' : ''))
->first(); ->first();
return !$user ? false : true; return ! $user ? false : true;
} }
/** /**
* Get the validation error message. * Get the validation error message.
*
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @return array|string * @return array|string
*/ */
public function message() public function message()

@ -5,9 +5,9 @@
namespace App\Services\Auth; namespace App\Services\Auth;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class ReverseProxyGuard implements Guard class ReverseProxyGuard implements Guard
@ -33,7 +33,7 @@ class ReverseProxyGuard implements Guard
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function user() public function user()
{ {
@ -47,17 +47,17 @@ class ReverseProxyGuard implements Guard
// Get the user identifier from $_SERVER or apache filtered headers // Get the user identifier from $_SERVER or apache filtered headers
$remoteUserHeader = config('auth.auth_proxy_headers.user'); $remoteUserHeader = config('auth.auth_proxy_headers.user');
$remoteUserHeader = $remoteUserHeader ?: 'REMOTE_USER'; $remoteUserHeader = $remoteUserHeader ?: 'REMOTE_USER';
$identifier = array(); $identifier = [];
try { try {
$identifier['user'] = request()->server($remoteUserHeader) ?? apache_request_headers()[$remoteUserHeader] ?? null; $identifier['user'] = request()->server($remoteUserHeader) ?? apache_request_headers()[$remoteUserHeader] ?? null;
} } catch (\Throwable $e) {
catch (\Throwable $e) {
$identifier['user'] = null; $identifier['user'] = null;
} }
if (!$identifier['user'] || is_array($identifier['user'])) { if (! $identifier['user'] || is_array($identifier['user'])) {
Log::error(sprintf('Proxy remote-user header "%s" is empty or missing.', $remoteUserHeader)); Log::error(sprintf('Proxy remote-user header "%s" is empty or missing.', $remoteUserHeader));
return $this->user = null; return $this->user = null;
} }
@ -66,9 +66,8 @@ class ReverseProxyGuard implements Guard
if ($remoteEmailHeader) { if ($remoteEmailHeader) {
try { try {
$remoteEmail = (string)(request()->server($remoteEmailHeader) ?? apache_request_headers()[$remoteEmailHeader] ?? null); $remoteEmail = (string) (request()->server($remoteEmailHeader) ?? apache_request_headers()[$remoteEmailHeader] ?? null);
} } catch (\Throwable $e) {
catch (\Throwable $e) {
$remoteEmail = null; $remoteEmail = null;
} }

@ -2,12 +2,11 @@
namespace App\Services; namespace App\Services;
use App\Facades\Settings;
use App\Models\Group; use App\Models\Group;
use App\Models\TwoFAccount; use App\Models\TwoFAccount;
use App\Facades\Settings;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\App;
class GroupService class GroupService
{ {
@ -29,7 +28,7 @@ class GroupService
// Create the pseudo group // Create the pseudo group
$allGroup = new Group([ $allGroup = new Group([
'name' => __('commons.all') 'name' => __('commons.all'),
]); ]);
$allGroup->id = 0; $allGroup->id = 0;
@ -38,7 +37,6 @@ class GroupService
return $groups->prepend($allGroup); return $groups->prepend($allGroup);
} }
/** /**
* Creates a group * Creates a group
* *
@ -58,7 +56,6 @@ class GroupService
return $group; return $group;
} }
/** /**
* Updates a group using a list of parameters * Updates a group using a list of parameters
* *
@ -77,7 +74,6 @@ class GroupService
return $group; return $group;
} }
/** /**
* Deletes one or more groups * Deletes one or more groups
* *
@ -112,7 +108,6 @@ class GroupService
return $deleted; return $deleted;
} }
/** /**
* Assign one or more accounts to a group * Assign one or more accounts to a group
* *
@ -122,15 +117,15 @@ class GroupService
*/ */
public static function assign($ids, Group $group = null) : void public static function assign($ids, Group $group = null) : void
{ {
if (!$group) { if (! $group) {
$group = self::defaultGroup(); $group = self::defaultGroup();
} }
if ($group) { if ($group) {
// saveMany() expect an iterable so we pass an array to // saveMany() expect an iterable so we pass an array to
// find() to always obtain a list of TwoFAccount // find() to always obtain a list of TwoFAccount
if (!is_array($ids)) { if (! is_array($ids)) {
$ids = array($ids); $ids = [$ids];
} }
$twofaccounts = TwoFAccount::find($ids); $twofaccounts = TwoFAccount::find($ids);
@ -138,11 +133,11 @@ class GroupService
$group->loadCount('twofaccounts'); $group->loadCount('twofaccounts');
Log::info(sprintf('Twofaccounts #%s assigned to groups %s', implode(',#', $ids), var_export($group->name, true))); Log::info(sprintf('Twofaccounts #%s assigned to groups %s', implode(',#', $ids), var_export($group->name, true)));
} else {
Log::info('Cannot find a group to assign the TwoFAccounts to');
} }
else Log::info('Cannot find a group to assign the TwoFAccounts to');
} }
/** /**
* Finds twofaccounts assigned to the group * Finds twofaccounts assigned to the group
* *
@ -156,7 +151,6 @@ class GroupService
return $twofaccounts; return $twofaccounts;
} }
/** /**
* Determines the destination group * Determines the destination group
* *

@ -4,8 +4,8 @@ namespace App\Services;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class LogoService class LogoService
@ -25,13 +25,11 @@ class LogoService
*/ */
const TFA_URL = 'https://2fa.directory/api/v3/tfa.json'; const TFA_URL = 'https://2fa.directory/api/v3/tfa.json';
public function __construct() public function __construct()
{ {
$this->setTfaCollection(); $this->setTfaCollection();
} }
/** /**
* Fetch a logo for the given service and save it as an icon * Fetch a logo for the given service and save it as an icon
* *
@ -43,12 +41,13 @@ class LogoService
$logoFilename = $this->getLogo(strval($serviceName)); $logoFilename = $this->getLogo(strval($serviceName));
if ($logoFilename) { if ($logoFilename) {
$iconFilename = Str::random(40).'.svg'; $iconFilename = Str::random(40) . '.svg';
return $this->copyToIcons($logoFilename, $iconFilename) ? $iconFilename : null;
}
else return null;
}
return $this->copyToIcons($logoFilename, $iconFilename) ? $iconFilename : null;
} else {
return null;
}
}
/** /**
* Return the logo's filename for a given service * Return the logo's filename for a given service
@ -59,16 +58,15 @@ class LogoService
protected function getLogo($serviceName) protected function getLogo($serviceName)
{ {
$domain = $this->tfas->get($this->cleanDomain(strval($serviceName))); $domain = $this->tfas->get($this->cleanDomain(strval($serviceName)));
$logoFilename = $domain.'.svg'; $logoFilename = $domain . '.svg';
if ($domain && !Storage::disk('logos')->exists($logoFilename)) { if ($domain && ! Storage::disk('logos')->exists($logoFilename)) {
$this->fetchLogo($logoFilename); $this->fetchLogo($logoFilename);
} }
return Storage::disk('logos')->exists($logoFilename) ? $logoFilename : null; return Storage::disk('logos')->exists($logoFilename) ? $logoFilename : null;
} }
/** /**
* Build and set the TFA directoy collection * Build and set the TFA directoy collection
* *
@ -90,7 +88,6 @@ class LogoService
: collect([]); : collect([]);
} }
/** /**
* Fetch and cache fresh TFA.Directory data using the https://2fa.directory API * Fetch and cache fresh TFA.Directory data using the https://2fa.directory API
* *
@ -104,22 +101,18 @@ class LogoService
$coll = collect(json_decode(htmlspecialchars_decode($response->body()), true)) /** @phpstan-ignore-line */ $coll = collect(json_decode(htmlspecialchars_decode($response->body()), true)) /** @phpstan-ignore-line */
->mapWithKeys(function ($item, $key) { ->mapWithKeys(function ($item, $key) {
return [ return [
strtolower(head($item)) => $item[1]["domain"] strtolower(head($item)) => $item[1]['domain'],
]; ];
}); });
Storage::disk('logos')->put(self::TFA_JSON, $coll->toJson()) Storage::disk('logos')->put(self::TFA_JSON, $coll->toJson())
? Log::info('Fresh tfa.json saved to logos dir') ? Log::info('Fresh tfa.json saved to logos dir')
: Log::notice('Cannot save tfa.json to logos dir'); : Log::notice('Cannot save tfa.json to logos dir');
} catch (\Exception $e) {
}
catch (\Exception $e) {
Log::error('Caching of tfa.json failed'); Log::error('Caching of tfa.json failed');
} }
} }
/** /**
* Fetch and cache a logo from 2fa.Directory repository * Fetch and cache a logo from 2fa.Directory repository
* *
@ -130,20 +123,18 @@ class LogoService
{ {
try { try {
$response = Http::retry(3, 100) $response = Http::retry(3, 100)
->get('https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/'.$logoFile[0].'/'.$logoFile); ->get('https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/' . $logoFile[0] . '/' . $logoFile);
if ($response->successful()) { if ($response->successful()) {
Storage::disk('logos')->put($logoFile, $response->body()) Storage::disk('logos')->put($logoFile, $response->body())
? Log::info(sprintf('Logo "%s" saved to logos dir.', $logoFile)) ? Log::info(sprintf('Logo "%s" saved to logos dir.', $logoFile))
: Log::notice(sprintf('Cannot save logo "%s" to logos dir', $logoFile)); : Log::notice(sprintf('Cannot save logo "%s" to logos dir', $logoFile));
} }
} } catch (\Exception $exception) {
catch (\Exception $exception) {
Log::error(sprintf('Fetching of logo "%s" failed.', $logoFile)); Log::error(sprintf('Fetching of logo "%s" failed.', $logoFile));
} }
} }
/** /**
* Prepare and make some replacement to optimize logo fetching * Prepare and make some replacement to optimize logo fetching
* *
@ -155,7 +146,6 @@ class LogoService
return strtolower(str_replace(['+'], ['plus'], $domain)); return strtolower(str_replace(['+'], ['plus'], $domain));
} }
/** /**
* Copy a logo file to the icons disk with a new name * Copy a logo file to the icons disk with a new name
* *

@ -2,15 +2,14 @@
namespace App\Services\Migrators; namespace App\Services\Migrators;
use App\Services\Migrators\Migrator;
use Illuminate\Support\Collection;
use App\Models\TwoFAccount;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Arr;
use App\Exceptions\InvalidMigrationDataException; use App\Exceptions\InvalidMigrationDataException;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helpers;
use App\Facades\TwoFAccounts; use App\Facades\TwoFAccounts;
use App\Helpers\Helpers;
use App\Models\TwoFAccount;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class AegisMigrator extends Migrator class AegisMigrator extends Migrator
{ {
@ -33,7 +32,6 @@ class AegisMigrator extends Migrator
// } // }
// } // }
/** /**
* Convert migration data to a TwoFAccounts collection. * Convert migration data to a TwoFAccounts collection.
* *
@ -49,11 +47,10 @@ class AegisMigrator extends Migrator
throw new InvalidMigrationDataException('Aegis'); throw new InvalidMigrationDataException('Aegis');
} }
$twofaccounts = array(); $twofaccounts = [];
foreach ($json['db']['entries'] as $key => $otp_parameters) { foreach ($json['db']['entries'] as $key => $otp_parameters) {
$parameters = [];
$parameters = array();
$parameters['otp_type'] = $otp_parameters['type'] == 'steam' ? TwoFAccount::STEAM_TOTP : $otp_parameters['type']; $parameters['otp_type'] = $otp_parameters['type'] == 'steam' ? TwoFAccount::STEAM_TOTP : $otp_parameters['type'];
$parameters['service'] = $otp_parameters['issuer']; $parameters['service'] = $otp_parameters['issuer'];
$parameters['account'] = $otp_parameters['name']; $parameters['account'] = $otp_parameters['name'];
@ -92,17 +89,14 @@ class AegisMigrator extends Migrator
Log::info(sprintf('Image %s successfully stored for import', $filename)); Log::info(sprintf('Image %s successfully stored for import', $filename));
} }
} }
} } catch (\Exception) {
catch (\Exception) {
// we do nothing // we do nothing
} }
try { try {
$twofaccounts[$key] = new TwoFAccount; $twofaccounts[$key] = new TwoFAccount;
$twofaccounts[$key]->fillWithOtpParameters($parameters); $twofaccounts[$key]->fillWithOtpParameters($parameters);
} } catch (\Exception $exception) {
catch (\Exception $exception) {
Log::error(sprintf('Cannot instanciate a TwoFAccount object with OTP parameters from imported item #%s', $key)); Log::error(sprintf('Cannot instanciate a TwoFAccount object with OTP parameters from imported item #%s', $key));
Log::error($exception->getMessage()); Log::error($exception->getMessage());

@ -2,23 +2,21 @@
namespace App\Services\Migrators; namespace App\Services\Migrators;
use Exception; use App\Exceptions\InvalidMigrationDataException;
use App\Models\TwoFAccount; use App\Models\TwoFAccount;
use App\Services\Migrators\Migrator;
use Illuminate\Support\Collection;
use ParagonIE\ConstantTime\Base32;
use App\Protobuf\GAuthValueMapping; use App\Protobuf\GAuthValueMapping;
use App\Protobuf\GoogleAuth\Payload; use App\Protobuf\GoogleAuth\Payload;
use App\Protobuf\GoogleAuth\Payload\OtpType;
use App\Protobuf\GoogleAuth\Payload\Algorithm; use App\Protobuf\GoogleAuth\Payload\Algorithm;
use App\Protobuf\GoogleAuth\Payload\DigitCount; use App\Protobuf\GoogleAuth\Payload\DigitCount;
use App\Exceptions\InvalidMigrationDataException; use App\Protobuf\GoogleAuth\Payload\OtpType;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use ParagonIE\ConstantTime\Base32;
class GoogleAuthMigrator extends Migrator class GoogleAuthMigrator extends Migrator
{ {
/** /**
* Convert Google Authenticator migration URI to a set of TwoFAccount objects. * Convert Google Authenticator migration URI to a set of TwoFAccount objects.
* *
@ -32,23 +30,21 @@ class GoogleAuthMigrator extends Migrator
$protobuf = new Payload(); $protobuf = new Payload();
$protobuf->mergeFromString($migrationData); $protobuf->mergeFromString($migrationData);
$otpParameters = $protobuf->getOtpParameters(); $otpParameters = $protobuf->getOtpParameters();
} } catch (Exception $ex) {
catch (Exception $ex) { Log::error('Protobuf failed to get OTP parameters from provided migration URI');
Log::error("Protobuf failed to get OTP parameters from provided migration URI");
Log::error($ex->getMessage()); Log::error($ex->getMessage());
throw new InvalidMigrationDataException('Google Authenticator'); throw new InvalidMigrationDataException('Google Authenticator');
} }
$twofaccounts = array(); $twofaccounts = [];
foreach ($otpParameters->getIterator() as $key => $otp_parameters) { foreach ($otpParameters->getIterator() as $key => $otp_parameters) {
try { try {
$parameters = array(); $parameters = [];
$parameters['otp_type'] = GAuthValueMapping::OTP_TYPE[OtpType::name($otp_parameters->getType())]; $parameters['otp_type'] = GAuthValueMapping::OTP_TYPE[OtpType::name($otp_parameters->getType())];
$parameters['service'] = $otp_parameters->getIssuer(); $parameters['service'] = $otp_parameters->getIssuer();
$parameters['account'] = str_replace($parameters['service'].':', '', $otp_parameters->getName()); $parameters['account'] = str_replace($parameters['service'] . ':', '', $otp_parameters->getName());
$parameters['secret'] = Base32::encodeUpper($otp_parameters->getSecret()); $parameters['secret'] = Base32::encodeUpper($otp_parameters->getSecret());
$parameters['algorithm'] = GAuthValueMapping::ALGORITHM[Algorithm::name($otp_parameters->getAlgorithm())]; $parameters['algorithm'] = GAuthValueMapping::ALGORITHM[Algorithm::name($otp_parameters->getAlgorithm())];
$parameters['digits'] = GAuthValueMapping::DIGIT_COUNT[DigitCount::name($otp_parameters->getDigits())]; $parameters['digits'] = GAuthValueMapping::DIGIT_COUNT[DigitCount::name($otp_parameters->getDigits())];
@ -57,9 +53,7 @@ class GoogleAuthMigrator extends Migrator
$twofaccounts[$key] = new TwoFAccount; $twofaccounts[$key] = new TwoFAccount;
$twofaccounts[$key]->fillWithOtpParameters($parameters); $twofaccounts[$key]->fillWithOtpParameters($parameters);
} } catch (Exception $exception) {
catch (Exception $exception) {
Log::error(sprintf('Cannot instanciate a TwoFAccount object with OTP parameters from imported item #%s', $key)); Log::error(sprintf('Cannot instanciate a TwoFAccount object with OTP parameters from imported item #%s', $key));
Log::error($exception->getMessage()); Log::error($exception->getMessage());

@ -14,7 +14,6 @@ abstract class Migrator
*/ */
abstract public function migrate(mixed $migrationPayload) : Collection; abstract public function migrate(mixed $migrationPayload) : Collection;
/** /**
* Pad a string to 8 chars min * Pad a string to 8 chars min
* *
@ -25,5 +24,4 @@ abstract class Migrator
{ {
return str_pad($string, 8, '='); return str_pad($string, 8, '=');
} }
} }

@ -2,17 +2,15 @@
namespace App\Services\Migrators; namespace App\Services\Migrators;
use App\Services\Migrators\Migrator;
use Illuminate\Support\Collection;
use App\Models\TwoFAccount;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use App\Exceptions\InvalidMigrationDataException; use App\Exceptions\InvalidMigrationDataException;
use App\Models\TwoFAccount;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class PlainTextMigrator extends Migrator class PlainTextMigrator extends Migrator
{ {
/** /**
* Convert migration data to a TwoFAccounts collection. * Convert migration data to a TwoFAccounts collection.
* *
@ -32,13 +30,10 @@ class PlainTextMigrator extends Migrator
} }
foreach ($otpauthURIs as $key => $uri) { foreach ($otpauthURIs as $key => $uri) {
try { try {
$twofaccounts[$key] = new TwoFAccount; $twofaccounts[$key] = new TwoFAccount;
$twofaccounts[$key]->fillWithURI($uri); $twofaccounts[$key]->fillWithURI($uri);
} } catch (\Exception $exception) {
catch (\Exception $exception) {
Log::error(sprintf('Cannot instanciate a TwoFAccount object with OTP parameters from imported item #%s', $key)); Log::error(sprintf('Cannot instanciate a TwoFAccount object with OTP parameters from imported item #%s', $key));
Log::error($exception->getMessage()); Log::error($exception->getMessage());

@ -2,12 +2,11 @@
namespace App\Services\Migrators; namespace App\Services\Migrators;
use App\Services\Migrators\Migrator;
use Illuminate\Support\Collection;
use App\Models\TwoFAccount;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Arr;
use App\Exceptions\InvalidMigrationDataException; use App\Exceptions\InvalidMigrationDataException;
use App\Models\TwoFAccount;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class TwoFASMigrator extends Migrator class TwoFASMigrator extends Migrator
{ {
@ -65,7 +64,6 @@ class TwoFASMigrator extends Migrator
// "appVersionName": "3.20.1" // "appVersionName": "3.20.1"
// } // }
/** /**
* Convert migration data to a TwoFAccounts collection. * Convert migration data to a TwoFAccounts collection.
* *
@ -81,11 +79,10 @@ class TwoFASMigrator extends Migrator
throw new InvalidMigrationDataException('2FAS Auth'); throw new InvalidMigrationDataException('2FAS Auth');
} }
$twofaccounts = array(); $twofaccounts = [];
foreach ($json['services'] as $key => $otp_parameters) { foreach ($json['services'] as $key => $otp_parameters) {
$parameters = [];
$parameters = array();
$parameters['otp_type'] = $otp_parameters['otp']['tokenType']; $parameters['otp_type'] = $otp_parameters['otp']['tokenType'];
$parameters['service'] = $otp_parameters['name']; $parameters['service'] = $otp_parameters['name'];
$parameters['account'] = $otp_parameters['otp']['account'] ?? $parameters['service']; $parameters['account'] = $otp_parameters['otp']['account'] ?? $parameters['service'];
@ -98,9 +95,7 @@ class TwoFASMigrator extends Migrator
try { try {
$twofaccounts[$key] = new TwoFAccount; $twofaccounts[$key] = new TwoFAccount;
$twofaccounts[$key]->fillWithOtpParameters($parameters); $twofaccounts[$key]->fillWithOtpParameters($parameters);
} } catch (\Exception $exception) {
catch (\Exception $exception) {
Log::error(sprintf('Cannot instanciate a TwoFAccount object with 2FAS imported item #%s', $key)); Log::error(sprintf('Cannot instanciate a TwoFAccount object with 2FAS imported item #%s', $key));
Log::error($exception->getMessage()); Log::error($exception->getMessage());

@ -2,9 +2,10 @@
namespace App\Services; namespace App\Services;
use Zxing\QrReader; use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use chillerlan\QRCode\{QRCode, QROptions}; use Zxing\QrReader;
class QrCodeService class QrCodeService
{ {
@ -12,7 +13,6 @@ class QrCodeService
* Encode a string into a QR code image * Encode a string into a QR code image
* *
* @param string $data The string to encode * @param string $data The string to encode
*
* @return mixed * @return mixed
*/ */
public static function encode(string $data) public static function encode(string $data)
@ -29,7 +29,6 @@ class QrCodeService
return $qrcode->render($data); return $qrcode->render($data);
} }
/** /**
* Decode an uploaded QR code image * Decode an uploaded QR code image
* *
@ -41,7 +40,7 @@ class QrCodeService
$qrcode = new QrReader($file->get(), QrReader::SOURCE_TYPE_BLOB); $qrcode = new QrReader($file->get(), QrReader::SOURCE_TYPE_BLOB);
$data = urldecode($qrcode->text()); $data = urldecode($qrcode->text());
if(!$data) { if (! $data) {
throw new \App\Exceptions\InvalidQrCodeException; throw new \App\Exceptions\InvalidQrCodeException;
} }

@ -21,7 +21,6 @@ class ReleaseRadarService
} }
} }
/** /**
* Run a manual release scan * Run a manual release scan
* *
@ -39,8 +38,7 @@ class ReleaseRadarService
*/ */
protected function newRelease() : false|string protected function newRelease() : false|string
{ {
if ($latestReleaseData = json_decode($this->getLatestReleaseData())) if ($latestReleaseData = json_decode($this->getLatestReleaseData())) {
{
$githubVersion = Helpers::cleanVersionNumber($latestReleaseData->tag_name); $githubVersion = Helpers::cleanVersionNumber($latestReleaseData->tag_name);
$installedVersion = Helpers::cleanVersionNumber(config('2fauth.version')); $installedVersion = Helpers::cleanVersionNumber(config('2fauth.version'));
@ -48,8 +46,7 @@ class ReleaseRadarService
Settings::set('latestRelease', $latestReleaseData->tag_name); Settings::set('latestRelease', $latestReleaseData->tag_name);
return $latestReleaseData->tag_name; return $latestReleaseData->tag_name;
} } else {
else {
Settings::delete('latestRelease'); Settings::delete('latestRelease');
} }
@ -59,7 +56,6 @@ class ReleaseRadarService
return false; return false;
} }
/** /**
* Fetch releases on Github * Fetch releases on Github
* *
@ -74,8 +70,7 @@ class ReleaseRadarService
if ($response->successful()) { if ($response->successful()) {
return $response->body(); return $response->body();
} }
} } catch (\Exception $exception) {
catch (\Exception $exception) {
Log::error('cannot reach latestReleaseUrl endpoint'); Log::error('cannot reach latestReleaseUrl endpoint');
} }

@ -2,20 +2,19 @@
namespace App\Services; namespace App\Services;
use Throwable; use App\Exceptions\DbEncryptionException;
use Exception;
use App\Models\Option; use App\Models\Option;
use Exception;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Crypt; use Throwable;
use Illuminate\Support\Facades\App;
use App\Exceptions\DbEncryptionException;
class SettingService class SettingService
{ {
/** /**
* All user settings * All user settings
* *
@ -23,7 +22,6 @@ class SettingService
*/ */
private Collection $settings; private Collection $settings;
/** /**
* Constructor * Constructor
*/ */
@ -32,7 +30,6 @@ class SettingService
self::build(); self::build();
} }
/** /**
* Get a setting * Get a setting
* *
@ -44,7 +41,6 @@ class SettingService
return $this->settings->get($setting); return $this->settings->get($setting);
} }
/** /**
* Get all settings * Get all settings
* *
@ -55,20 +51,18 @@ class SettingService
return $this->settings; return $this->settings;
} }
/** /**
* Set a setting * Set a setting
* *
* @param string|array $setting A single setting name or an associative array of name:value settings * @param string|array $setting A single setting name or an associative array of name:value settings
* @param string|int|boolean|null $value The value for single setting * @param string|int|bool|null $value The value for single setting
*/ */
public function set($setting, $value = null) : void public function set($setting, $value = null) : void
{ {
$settings = is_array($setting) ? $setting : [$setting => $value]; $settings = is_array($setting) ? $setting : [$setting => $value];
foreach ($settings as $setting => $value) { foreach ($settings as $setting => $value) {
if( $setting === 'useEncryption') if ($setting === 'useEncryption') {
{
$this->setEncryptionTo($value); $this->setEncryptionTo($value);
} }
@ -83,7 +77,6 @@ class SettingService
self::build(); self::build();
} }
/** /**
* Delete a setting * Delete a setting
* *
@ -95,7 +88,6 @@ class SettingService
Log::info(sprintf('Setting %s deleted', var_export($name, true))); Log::info(sprintf('Setting %s deleted', var_export($name, true)));
} }
/** /**
* Determine if the given setting has been customized by the user * Determine if the given setting has been customized by the user
* *
@ -107,7 +99,6 @@ class SettingService
return DB::table('options')->where('key', $key)->exists(); return DB::table('options')->where('key', $key)->exists();
} }
/** /**
* Set the settings collection * Set the settings collection
* *
@ -123,15 +114,13 @@ class SettingService
// Merge 2fauth/app config values as fallback values // Merge 2fauth/app config values as fallback values
$settings = collect(config('2fauth.options'))->merge($userOptions); /** @phpstan-ignore-line */ $settings = collect(config('2fauth.options'))->merge($userOptions); /** @phpstan-ignore-line */
if (! Arr::has($settings, 'lang')) {
if(!Arr::has($settings, 'lang')) {
$settings['lang'] = 'browser'; $settings['lang'] = 'browser';
} }
$this->settings = $settings; $this->settings = $settings;
} }
/** /**
* Replaces boolean by a patterned string as appstrack/laravel-options package does not support var type * Replaces boolean by a patterned string as appstrack/laravel-options package does not support var type
* *
@ -143,7 +132,6 @@ class SettingService
return is_bool($value) ? '{{' . $value . '}}' : $value; return is_bool($value) ? '{{' . $value . '}}' : $value;
} }
/** /**
* Replaces patterned string that represent booleans with real booleans * Replaces patterned string that represent booleans with real booleans
* *
@ -154,22 +142,20 @@ class SettingService
{ {
$value = is_numeric($value) ? (int) $value : $value; $value = is_numeric($value) ? (int) $value : $value;
if( $value === '{{}}' ) { if ($value === '{{}}') {
return false; return false;
} } elseif ($value === '{{1}}') {
else if( $value === '{{1}}' ) {
return true; return true;
} } else {
else {
return $value; return $value;
} }
} }
/** /**
* Enable or Disable encryption of 2FAccounts sensible data * Enable or Disable encryption of 2FAccounts sensible data
* *
* @return void * @return void
*
* @throws DbEncryptionException Something failed, everything have been rolled back * @throws DbEncryptionException Something failed, everything have been rolled back
*/ */
private function setEncryptionTo(bool $state) : void private function setEncryptionTo(bool $state) : void
@ -177,39 +163,37 @@ class SettingService
// We don't want the records to be encrypted/decrypted multiple successive times // We don't want the records to be encrypted/decrypted multiple successive times
$isInUse = $this->get('useEncryption'); $isInUse = $this->get('useEncryption');
if ($isInUse === !$state) { if ($isInUse === ! $state) {
if ($this->updateRecords($state)) { if ($this->updateRecords($state)) {
if ($state) { if ($state) {
Log::notice('Sensible data are now encrypted'); Log::notice('Sensible data are now encrypted');
} else {
Log::notice('Sensible data are now decrypted');
} }
else Log::notice('Sensible data are now decrypted'); } else {
}
else {
Log::warning('Some data cannot be encrypted/decrypted, the useEncryption setting remain unchanged'); Log::warning('Some data cannot be encrypted/decrypted, the useEncryption setting remain unchanged');
throw new DbEncryptionException($state === true ? __('errors.error_during_encryption') : __('errors.error_during_decryption')); throw new DbEncryptionException($state === true ? __('errors.error_during_encryption') : __('errors.error_during_decryption'));
} }
} }
} }
/** /**
* Encrypt/Decrypt accounts in database * Encrypt/Decrypt accounts in database
* *
* @param boolean $encrypted Whether the record should be encrypted or not * @param bool $encrypted Whether the record should be encrypted or not
* @return boolean Whether the operation completed successfully * @return bool Whether the operation completed successfully
*/ */
private function updateRecords(bool $encrypted) : bool private function updateRecords(bool $encrypted) : bool
{ {
$success = true; $success = true;
$twofaccounts = DB::table('twofaccounts')->get(); $twofaccounts = DB::table('twofaccounts')->get();
$twofaccounts->each(function ($item, $key) use(&$success, $encrypted) { $twofaccounts->each(function ($item, $key) use (&$success, $encrypted) {
try { try {
$item->legacy_uri = $encrypted ? Crypt::encryptString($item->legacy_uri) : Crypt::decryptString($item->legacy_uri); $item->legacy_uri = $encrypted ? Crypt::encryptString($item->legacy_uri) : Crypt::decryptString($item->legacy_uri);
$item->account = $encrypted ? Crypt::encryptString($item->account) : Crypt::decryptString($item->account); $item->account = $encrypted ? Crypt::encryptString($item->account) : Crypt::decryptString($item->account);
$item->secret = $encrypted ? Crypt::encryptString($item->secret) : Crypt::decryptString($item->secret); $item->secret = $encrypted ? Crypt::encryptString($item->secret) : Crypt::decryptString($item->secret);
} } catch (Exception $ex) {
catch (Exception $ex) {
$success = false; $success = false;
// Exit the each iteration // Exit the each iteration
return false; return false;
@ -228,20 +212,23 @@ class SettingService
->update([ ->update([
'legacy_uri' => $item->legacy_uri, 'legacy_uri' => $item->legacy_uri,
'account' => $item->account, 'account' => $item->account,
'secret' => $item->secret 'secret' => $item->secret,
]); ]);
}); });
DB::commit(); DB::commit();
return true; return true;
} }
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
catch (Throwable $ex) { catch (Throwable $ex) {
DB::rollBack(); DB::rollBack();
return false; return false;
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} else {
return false;
} }
else return false;
} }
} }

@ -2,19 +2,18 @@
namespace App\Services; namespace App\Services;
use App\Models\TwoFAccount;
use App\Factories\MigratorFactoryInterface; use App\Factories\MigratorFactoryInterface;
use Illuminate\Support\Facades\Log; use App\Models\TwoFAccount;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class TwoFAccountService class TwoFAccountService
{ {
/** /**
* @var MigratorFactoryInterface $migratorFactory The Migration service * @var MigratorFactoryInterface The Migration service
*/ */
protected $migratorFactory; protected $migratorFactory;
/** /**
* Constructor * Constructor
*/ */
@ -23,7 +22,6 @@ class TwoFAccountService
$this->migratorFactory = $migratorFactory; $this->migratorFactory = $migratorFactory;
} }
/** /**
* Withdraw one or more twofaccounts from their group * Withdraw one or more twofaccounts from their group
* *
@ -40,13 +38,12 @@ class TwoFAccountService
TwoFAccount::whereIn('id', $ids) TwoFAccount::whereIn('id', $ids)
->update( ->update(
['group_id' => NULL] ['group_id' => null]
); );
Log::info(sprintf('TwoFAccounts #%s withdrawn', implode(',#', $ids))); Log::info(sprintf('TwoFAccounts #%s withdrawn', implode(',#', $ids)));
} }
/** /**
* Convert a migration payload to a set of TwoFAccount objects * Convert a migration payload to a set of TwoFAccount objects
* *
@ -61,7 +58,6 @@ class TwoFAccountService
return self::markAsDuplicate($twofaccounts); return self::markAsDuplicate($twofaccounts);
} }
/** /**
* Delete one or more twofaccounts * Delete one or more twofaccounts
* *
@ -73,13 +69,12 @@ class TwoFAccountService
// $ids as string could be a comma-separated list of ids // $ids as string could be a comma-separated list of ids
// so in this case we explode the string to an array // so in this case we explode the string to an array
$ids = self::commaSeparatedToArray($ids); $ids = self::commaSeparatedToArray($ids);
Log::info(sprintf('Deletion of TwoFAccounts #%s requested', is_array($ids) ? implode(',#', $ids) : $ids )); Log::info(sprintf('Deletion of TwoFAccounts #%s requested', is_array($ids) ? implode(',#', $ids) : $ids));
$deleted = TwoFAccount::destroy($ids); $deleted = TwoFAccount::destroy($ids);
return $deleted; return $deleted;
} }
/** /**
* Return the given collection with items marked as Duplicates (using id=-1) if a similar record exists in database * Return the given collection with items marked as Duplicates (using id=-1) if a similar record exists in database
* *
@ -108,7 +103,6 @@ class TwoFAccountService
return $twofaccounts; return $twofaccounts;
} }
/** /**
* Explode a comma separated list of IDs to an array of IDs * Explode a comma separated list of IDs to an array of IDs
* *
@ -116,8 +110,7 @@ class TwoFAccountService
*/ */
private static function commaSeparatedToArray($ids) : mixed private static function commaSeparatedToArray($ids) : mixed
{ {
if(is_string($ids)) if (is_string($ids)) {
{
$regex = "/^\d+(,{1}\d+)*$/"; $regex = "/^\d+(,{1}\d+)*$/";
if (preg_match($regex, $ids)) { if (preg_match($regex, $ids)) {
$ids = explode(',', $ids); $ids = explode(',', $ids);

@ -1,3 +1,26 @@
{ {
"preset": "laravel" "preset": "laravel",
"exclude": [
"app/Protobuf",
"bootstrap",
"config",
"database",
"public",
"resources"
],
"rules": {
"binary_operator_spaces": {
"default": "single_space",
"operators": {
"=>": "align_single_space_minimal",
"=": "align_single_space_minimal"
}
},
"concat_space": {
"spacing": "one"
},
"return_type_declaration": {
"space_before": "one"
}
}
} }

@ -12,18 +12,16 @@ class UserControllerTest extends FeatureTestCase
*/ */
protected $user; protected $user;
/** /**
* @test * @test
*/ */
public function setUp(): void public function setUp() : void
{ {
parent::setUp(); parent::setUp();
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -39,7 +37,6 @@ class UserControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -52,7 +49,6 @@ class UserControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -69,5 +65,4 @@ class UserControllerTest extends FeatureTestCase
'email' => $this->user->email, 'email' => $this->user->email,
]); ]);
} }
} }

@ -2,11 +2,10 @@
namespace Tests\Api\v1\Controllers; namespace Tests\Api\v1\Controllers;
use App\Models\User;
use App\Models\Group; use App\Models\Group;
use Tests\FeatureTestCase;
use App\Models\TwoFAccount; use App\Models\TwoFAccount;
use App\Models\User;
use Tests\FeatureTestCase;
/** /**
* @covers \App\Api\v1\Controllers\GroupController * @covers \App\Api\v1\Controllers\GroupController
@ -19,18 +18,16 @@ class GroupControllerTest extends FeatureTestCase
*/ */
protected $user; protected $user;
/** /**
* @test * @test
*/ */
public function setUp(): void public function setUp() : void
{ {
parent::setUp(); parent::setUp();
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -47,7 +44,7 @@ class GroupControllerTest extends FeatureTestCase
'id', 'id',
'name', 'name',
'twofaccounts_count', 'twofaccounts_count',
] ],
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -56,7 +53,6 @@ class GroupControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -73,7 +69,6 @@ class GroupControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -86,7 +81,6 @@ class GroupControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -105,7 +99,6 @@ class GroupControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -115,11 +108,10 @@ class GroupControllerTest extends FeatureTestCase
->json('GET', '/api/v1/groups/1000') ->json('GET', '/api/v1/groups/1000')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -138,7 +130,6 @@ class GroupControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -150,11 +141,10 @@ class GroupControllerTest extends FeatureTestCase
]) ])
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -169,7 +159,6 @@ class GroupControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -190,7 +179,6 @@ class GroupControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -204,11 +192,10 @@ class GroupControllerTest extends FeatureTestCase
]) ])
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -224,7 +211,6 @@ class GroupControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -252,12 +238,11 @@ class GroupControllerTest extends FeatureTestCase
'digits', 'digits',
'algorithm', 'algorithm',
'period', 'period',
'counter' 'counter',
] ],
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -286,12 +271,11 @@ class GroupControllerTest extends FeatureTestCase
'digits', 'digits',
'algorithm', 'algorithm',
'period', 'period',
'counter' 'counter',
] ],
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -301,11 +285,10 @@ class GroupControllerTest extends FeatureTestCase
->json('GET', '/api/v1/groups/1000/twofaccounts') ->json('GET', '/api/v1/groups/1000/twofaccounts')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* test Group deletion via API * test Group deletion via API
* *
@ -320,7 +303,6 @@ class GroupControllerTest extends FeatureTestCase
->assertNoContent(); ->assertNoContent();
} }
/** /**
* test Group deletion via API * test Group deletion via API
* *
@ -332,7 +314,7 @@ class GroupControllerTest extends FeatureTestCase
->json('DELETE', '/api/v1/groups/1000') ->json('DELETE', '/api/v1/groups/1000')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
} }

@ -2,21 +2,17 @@
namespace Tests\Api\v1\Controllers; namespace Tests\Api\v1\Controllers;
use Illuminate\Http\UploadedFile;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Http\UploadedFile;
use Tests\FeatureTestCase; use Tests\FeatureTestCase;
use App\Models\TwoFAccount;
/** /**
* @covers \App\Api\v1\Controllers\IconController * @covers \App\Api\v1\Controllers\IconController
*/ */
class IconControllerTest extends FeatureTestCase class IconControllerTest extends FeatureTestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
* @test * @test
*/ */
@ -29,11 +25,10 @@ class IconControllerTest extends FeatureTestCase
]) ])
->assertCreated() ->assertCreated()
->assertJsonStructure([ ->assertJsonStructure([
'filename' 'filename',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -45,7 +40,6 @@ class IconControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -55,7 +49,6 @@ class IconControllerTest extends FeatureTestCase
->assertNoContent(204); ->assertNoContent(204);
} }
/** /**
* @test * @test
*/ */
@ -63,7 +56,5 @@ class IconControllerTest extends FeatureTestCase
{ {
$response = $this->json('DELETE', '/api/v1/icons/null') $response = $this->json('DELETE', '/api/v1/icons/null')
->assertNoContent(204); ->assertNoContent(204);
} }
} }

@ -2,35 +2,31 @@
namespace Tests\Api\v1\Controllers; namespace Tests\Api\v1\Controllers;
use App\Models\User;
use Tests\FeatureTestCase;
use App\Models\TwoFAccount; use App\Models\TwoFAccount;
use App\Models\User;
use Tests\Classes\LocalFile; use Tests\Classes\LocalFile;
use Tests\FeatureTestCase;
/** /**
* @covers \App\Api\v1\Controllers\QrCodeController * @covers \App\Api\v1\Controllers\QrCodeController
*/ */
class QrCodeControllerTest extends FeatureTestCase class QrCodeControllerTest extends FeatureTestCase
{ {
/** /**
* @var \App\Models\User * @var \App\Models\User
*/ */
protected $user; protected $user;
/** /**
* @test * @test
*/ */
public function setUp(): void public function setUp() : void
{ {
parent::setUp(); parent::setUp();
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -57,7 +53,6 @@ class QrCodeControllerTest extends FeatureTestCase
$this->assertStringStartsWith('data:image/png;base64', $response->getData()->qrcode); $this->assertStringStartsWith('data:image/png;base64', $response->getData()->qrcode);
} }
/** /**
* @test * @test
*/ */
@ -67,11 +62,10 @@ class QrCodeControllerTest extends FeatureTestCase
->json('GET', '/api/v1/twofaccounts/1000/qrcode') ->json('GET', '/api/v1/twofaccounts/1000/qrcode')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -83,7 +77,7 @@ class QrCodeControllerTest extends FeatureTestCase
->actingAs($this->user, 'api-guard') ->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/qrcode/decode', [ ->json('POST', '/api/v1/qrcode/decode', [
'qrcode' => $file, 'qrcode' => $file,
'inputFormat' => 'fileUpload' 'inputFormat' => 'fileUpload',
]) ])
->assertOk() ->assertOk()
->assertExactJson([ ->assertExactJson([
@ -91,7 +85,6 @@ class QrCodeControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -104,7 +97,6 @@ class QrCodeControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -116,7 +108,7 @@ class QrCodeControllerTest extends FeatureTestCase
->actingAs($this->user, 'api-guard') ->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/qrcode/decode', [ ->json('POST', '/api/v1/qrcode/decode', [
'qrcode' => $file, 'qrcode' => $file,
'inputFormat' => 'fileUpload' 'inputFormat' => 'fileUpload',
]) ])
->assertStatus(400) ->assertStatus(400)
->assertJsonStructure([ ->assertJsonStructure([

@ -2,10 +2,9 @@
namespace Tests\Api\v1\Controllers; namespace Tests\Api\v1\Controllers;
use App\Facades\Settings;
use App\Models\User; use App\Models\User;
use Tests\FeatureTestCase; use Tests\FeatureTestCase;
use App\Facades\Settings;
/** /**
* @covers \App\Api\v1\Controllers\SettingController * @covers \App\Api\v1\Controllers\SettingController
@ -19,26 +18,31 @@ class SettingControllerTest extends FeatureTestCase
private const SETTING_JSON_STRUCTURE = [ private const SETTING_JSON_STRUCTURE = [
'key', 'key',
'value' 'value',
]; ];
private const TWOFAUTH_NATIVE_SETTING = 'showTokenAsDot'; private const TWOFAUTH_NATIVE_SETTING = 'showTokenAsDot';
private const TWOFAUTH_NATIVE_SETTING_DEFAULT_VALUE = false; private const TWOFAUTH_NATIVE_SETTING_DEFAULT_VALUE = false;
private const TWOFAUTH_NATIVE_SETTING_CHANGED_VALUE = true; private const TWOFAUTH_NATIVE_SETTING_CHANGED_VALUE = true;
private const USER_DEFINED_SETTING = 'mySetting'; private const USER_DEFINED_SETTING = 'mySetting';
private const USER_DEFINED_SETTING_VALUE = 'mySetting'; private const USER_DEFINED_SETTING_VALUE = 'mySetting';
private const USER_DEFINED_SETTING_CHANGED_VALUE = 'mySetting'; private const USER_DEFINED_SETTING_CHANGED_VALUE = 'mySetting';
/** /**
* @test * @test
*/ */
public function setUp(): void public function setUp() : void
{ {
parent::setUp(); parent::setUp();
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -48,11 +52,10 @@ class SettingControllerTest extends FeatureTestCase
->json('GET', '/api/v1/settings') ->json('GET', '/api/v1/settings')
->assertOk() ->assertOk()
->assertJsonStructure([ ->assertJsonStructure([
'*' => self::SETTING_JSON_STRUCTURE '*' => self::SETTING_JSON_STRUCTURE,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -67,7 +70,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -84,7 +86,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -101,7 +102,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -112,7 +112,6 @@ class SettingControllerTest extends FeatureTestCase
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -130,7 +129,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -144,7 +142,6 @@ class SettingControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -160,7 +157,6 @@ class SettingControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -177,7 +173,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -196,7 +191,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -213,7 +207,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -226,7 +219,6 @@ class SettingControllerTest extends FeatureTestCase
->assertNoContent(); ->assertNoContent();
} }
/** /**
* @test * @test
*/ */
@ -241,7 +233,6 @@ class SettingControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -251,6 +242,4 @@ class SettingControllerTest extends FeatureTestCase
->json('DELETE', '/api/v1/settings/' . self::USER_DEFINED_SETTING) ->json('DELETE', '/api/v1/settings/' . self::USER_DEFINED_SETTING)
->assertNotFound(); ->assertNotFound();
} }
} }

@ -2,16 +2,15 @@
namespace Tests\Api\v1\Controllers; namespace Tests\Api\v1\Controllers;
use App\Models\User;
use App\Models\Group;
use App\Facades\Settings; use App\Facades\Settings;
use Tests\FeatureTestCase; use App\Models\Group;
use Tests\Classes\OtpTestData;
use App\Models\TwoFAccount; use App\Models\TwoFAccount;
use App\Models\User;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Tests\Classes\LocalFile; use Tests\Classes\LocalFile;
use Tests\Classes\OtpTestData;
use Tests\FeatureTestCase;
/** /**
* @covers \App\Api\v1\Controllers\TwoFAccountController * @covers \App\Api\v1\Controllers\TwoFAccountController
@ -30,8 +29,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
*/ */
protected $group; protected $group;
private const VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET = [ private const VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET = [
'id', 'id',
'group_id', 'group_id',
@ -42,8 +39,9 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits', 'digits',
'algorithm', 'algorithm',
'period', 'period',
'counter' 'counter',
]; ];
private const VALID_RESOURCE_STRUCTURE_WITH_SECRET = [ private const VALID_RESOURCE_STRUCTURE_WITH_SECRET = [
'id', 'id',
'group_id', 'group_id',
@ -55,19 +53,22 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits', 'digits',
'algorithm', 'algorithm',
'period', 'period',
'counter' 'counter',
]; ];
private const VALID_OTP_RESOURCE_STRUCTURE_FOR_TOTP = [ private const VALID_OTP_RESOURCE_STRUCTURE_FOR_TOTP = [
'generated_at', 'generated_at',
'otp_type', 'otp_type',
'password', 'password',
'period', 'period',
]; ];
private const VALID_OTP_RESOURCE_STRUCTURE_FOR_HOTP = [ private const VALID_OTP_RESOURCE_STRUCTURE_FOR_HOTP = [
'otp_type', 'otp_type',
'password', 'password',
'counter', 'counter',
]; ];
private const JSON_FRAGMENTS_FOR_CUSTOM_TOTP = [ private const JSON_FRAGMENTS_FOR_CUSTOM_TOTP = [
'service' => OtpTestData::SERVICE, 'service' => OtpTestData::SERVICE,
'account' => OtpTestData::ACCOUNT, 'account' => OtpTestData::ACCOUNT,
@ -78,6 +79,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'period' => OtpTestData::PERIOD_CUSTOM, 'period' => OtpTestData::PERIOD_CUSTOM,
'counter' => null, 'counter' => null,
]; ];
private const JSON_FRAGMENTS_FOR_DEFAULT_TOTP = [ private const JSON_FRAGMENTS_FOR_DEFAULT_TOTP = [
'service' => null, 'service' => null,
'account' => OtpTestData::ACCOUNT, 'account' => OtpTestData::ACCOUNT,
@ -88,6 +90,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'period' => OtpTestData::PERIOD_DEFAULT, 'period' => OtpTestData::PERIOD_DEFAULT,
'counter' => null, 'counter' => null,
]; ];
private const JSON_FRAGMENTS_FOR_CUSTOM_HOTP = [ private const JSON_FRAGMENTS_FOR_CUSTOM_HOTP = [
'service' => OtpTestData::SERVICE, 'service' => OtpTestData::SERVICE,
'account' => OtpTestData::ACCOUNT, 'account' => OtpTestData::ACCOUNT,
@ -98,6 +101,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'period' => null, 'period' => null,
'counter' => OtpTestData::COUNTER_CUSTOM, 'counter' => OtpTestData::COUNTER_CUSTOM,
]; ];
private const JSON_FRAGMENTS_FOR_DEFAULT_HOTP = [ private const JSON_FRAGMENTS_FOR_DEFAULT_HOTP = [
'service' => null, 'service' => null,
'account' => OtpTestData::ACCOUNT, 'account' => OtpTestData::ACCOUNT,
@ -108,17 +112,17 @@ class TwoFAccountControllerTest extends FeatureTestCase
'period' => null, 'period' => null,
'counter' => OtpTestData::COUNTER_DEFAULT, 'counter' => OtpTestData::COUNTER_DEFAULT,
]; ];
private const ARRAY_OF_INVALID_PARAMETERS = [ private const ARRAY_OF_INVALID_PARAMETERS = [
'account' => null, 'account' => null,
'otp_type' => 'totp', 'otp_type' => 'totp',
'secret' => OtpTestData::SECRET, 'secret' => OtpTestData::SECRET,
]; ];
/** /**
* @test * @test
*/ */
public function setUp(): void public function setUp() : void
{ {
parent::setUp(); parent::setUp();
@ -126,7 +130,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
$this->group = Group::factory()->create(); $this->group = Group::factory()->create();
} }
/** /**
* @test * @test
* *
@ -137,15 +140,14 @@ class TwoFAccountControllerTest extends FeatureTestCase
TwoFAccount::factory()->count(3)->create(); TwoFAccount::factory()->count(3)->create();
$response = $this->actingAs($this->user, 'api-guard') $response = $this->actingAs($this->user, 'api-guard')
->json('GET', '/api/v1/twofaccounts'.$urlParameter) ->json('GET', '/api/v1/twofaccounts' . $urlParameter)
->assertOk() ->assertOk()
->assertJsonCount(3, $key = null) ->assertJsonCount(3, $key = null)
->assertJsonStructure([ ->assertJsonStructure([
'*' => $expected '*' => $expected,
]); ]);
} }
/** /**
* Provide data for index tests * Provide data for index tests
*/ */
@ -154,16 +156,15 @@ class TwoFAccountControllerTest extends FeatureTestCase
return [ return [
'VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET' => [ 'VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET' => [
'', '',
self::VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET self::VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET,
], ],
'VALID_RESOURCE_STRUCTURE_WITH_SECRET' => [ 'VALID_RESOURCE_STRUCTURE_WITH_SECRET' => [
'?withSecret=1', '?withSecret=1',
self::VALID_RESOURCE_STRUCTURE_WITH_SECRET self::VALID_RESOURCE_STRUCTURE_WITH_SECRET,
], ],
]; ];
} }
/** /**
* @test * @test
*/ */
@ -177,7 +178,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITH_SECRET); ->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITH_SECRET);
} }
/** /**
* @test * @test
*/ */
@ -191,7 +191,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET); ->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET);
} }
/** /**
* @test * @test
*/ */
@ -217,7 +216,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
// ]); // ]);
// } // }
/** /**
* @test * @test
*/ */
@ -227,11 +225,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
->json('GET', '/api/v1/twofaccounts/1000') ->json('GET', '/api/v1/twofaccounts/1000')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @dataProvider accountCreationProvider * @dataProvider accountCreationProvider
* @test * @test
@ -248,7 +245,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonFragment($expected); ->assertJsonFragment($expected);
} }
/** /**
* @dataProvider accountCreationProvider * @dataProvider accountCreationProvider
* @test * @test
@ -265,7 +261,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonFragment($expected); ->assertJsonFragment($expected);
} }
/** /**
* Provide data for TwoFAccount store tests * Provide data for TwoFAccount store tests
*/ */
@ -276,46 +271,45 @@ class TwoFAccountControllerTest extends FeatureTestCase
[ [
'uri' => OtpTestData::TOTP_FULL_CUSTOM_URI, 'uri' => OtpTestData::TOTP_FULL_CUSTOM_URI,
], ],
self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP,
], ],
'TOTP_SHORT_URI' => [ 'TOTP_SHORT_URI' => [
[ [
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
], ],
self::JSON_FRAGMENTS_FOR_DEFAULT_TOTP self::JSON_FRAGMENTS_FOR_DEFAULT_TOTP,
], ],
'ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP' => [ 'ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP' => [
OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP, OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP,
self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP,
], ],
'ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP' => [ 'ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP' => [
OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP, OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP,
self::JSON_FRAGMENTS_FOR_DEFAULT_TOTP self::JSON_FRAGMENTS_FOR_DEFAULT_TOTP,
], ],
'HOTP_FULL_CUSTOM_URI' => [ 'HOTP_FULL_CUSTOM_URI' => [
[ [
'uri' => OtpTestData::HOTP_FULL_CUSTOM_URI, 'uri' => OtpTestData::HOTP_FULL_CUSTOM_URI,
], ],
self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP,
], ],
'HOTP_SHORT_URI' => [ 'HOTP_SHORT_URI' => [
[ [
'uri' => OtpTestData::HOTP_SHORT_URI, 'uri' => OtpTestData::HOTP_SHORT_URI,
], ],
self::JSON_FRAGMENTS_FOR_DEFAULT_HOTP self::JSON_FRAGMENTS_FOR_DEFAULT_HOTP,
], ],
'ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP' => [ 'ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP' => [
OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP, OtpTestData::ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP,
self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP,
], ],
'ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP' => [ 'ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP' => [
OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP, OtpTestData::ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP,
self::JSON_FRAGMENTS_FOR_DEFAULT_HOTP self::JSON_FRAGMENTS_FOR_DEFAULT_HOTP,
], ],
]; ];
} }
/** /**
* @test * @test
*/ */
@ -328,7 +322,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -342,11 +335,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'group_id' => $this->group->id 'group_id' => $this->group->id,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -362,11 +354,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'group_id' => $this->group->id 'group_id' => $this->group->id,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -380,11 +371,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'group_id' => null 'group_id' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -398,11 +388,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'group_id' => null 'group_id' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -416,7 +405,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP); ->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP);
} }
/** /**
* @test * @test
*/ */
@ -430,7 +418,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP); ->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP);
} }
/** /**
* @test * @test
*/ */
@ -441,7 +428,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -454,7 +440,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -476,7 +461,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_DEFAULT, 'digits' => OtpTestData::DIGITS_DEFAULT,
'algorithm' => OtpTestData::ALGORITHM_DEFAULT, 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
'period' => OtpTestData::PERIOD_DEFAULT, 'period' => OtpTestData::PERIOD_DEFAULT,
'counter' => null 'counter' => null,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -487,11 +472,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_DEFAULT, 'digits' => OtpTestData::DIGITS_DEFAULT,
'algorithm' => OtpTestData::ALGORITHM_DEFAULT, 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
'period' => OtpTestData::PERIOD_DEFAULT, 'period' => OtpTestData::PERIOD_DEFAULT,
'counter' => null 'counter' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -504,7 +488,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -534,7 +517,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -546,11 +528,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
]) ])
->assertStatus(400) ->assertStatus(400)
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -575,7 +556,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_DEFAULT, 'digits' => OtpTestData::DIGITS_DEFAULT,
'algorithm' => OtpTestData::ALGORITHM_DEFAULT, 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
'period' => OtpTestData::PERIOD_DEFAULT, 'period' => OtpTestData::PERIOD_DEFAULT,
'counter' => null 'counter' => null,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -586,7 +567,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_CUSTOM, 'digits' => OtpTestData::DIGITS_CUSTOM,
'algorithm' => OtpTestData::ALGORITHM_CUSTOM, 'algorithm' => OtpTestData::ALGORITHM_CUSTOM,
'period' => OtpTestData::PERIOD_CUSTOM, 'period' => OtpTestData::PERIOD_CUSTOM,
'counter' => null 'counter' => null,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -597,7 +578,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_DEFAULT, 'digits' => OtpTestData::DIGITS_DEFAULT,
'algorithm' => OtpTestData::ALGORITHM_DEFAULT, 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
'period' => null, 'period' => null,
'counter' => OtpTestData::COUNTER_DEFAULT 'counter' => OtpTestData::COUNTER_DEFAULT,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -619,11 +600,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_STEAM, 'digits' => OtpTestData::DIGITS_STEAM,
'algorithm' => OtpTestData::ALGORITHM_DEFAULT, 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
'period' => OtpTestData::PERIOD_DEFAULT, 'period' => OtpTestData::PERIOD_DEFAULT,
'counter' => null 'counter' => null,
]); ]);
} }
/** /**
* @test * @test
* *
@ -639,7 +619,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(400); ->assertStatus(400);
} }
/** /**
* Provide invalid Aegis JSON files for import tests * Provide invalid Aegis JSON files for import tests
*/ */
@ -647,15 +626,14 @@ class TwoFAccountControllerTest extends FeatureTestCase
{ {
return [ return [
'validPlainTextFile' => [ 'validPlainTextFile' => [
LocalFile::fake()->encryptedAegisJsonFile() LocalFile::fake()->encryptedAegisJsonFile(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->invalidAegisJsonFile() LocalFile::fake()->invalidAegisJsonFile(),
], ],
]; ];
} }
/** /**
* @test * @test
* *
@ -680,7 +658,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_CUSTOM, 'digits' => OtpTestData::DIGITS_CUSTOM,
'algorithm' => OtpTestData::ALGORITHM_CUSTOM, 'algorithm' => OtpTestData::ALGORITHM_CUSTOM,
'period' => OtpTestData::PERIOD_CUSTOM, 'period' => OtpTestData::PERIOD_CUSTOM,
'counter' => null 'counter' => null,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -691,7 +669,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_CUSTOM, 'digits' => OtpTestData::DIGITS_CUSTOM,
'algorithm' => OtpTestData::ALGORITHM_CUSTOM, 'algorithm' => OtpTestData::ALGORITHM_CUSTOM,
'period' => null, 'period' => null,
'counter' => OtpTestData::COUNTER_CUSTOM 'counter' => OtpTestData::COUNTER_CUSTOM,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -702,11 +680,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
'digits' => OtpTestData::DIGITS_STEAM, 'digits' => OtpTestData::DIGITS_STEAM,
'algorithm' => OtpTestData::ALGORITHM_DEFAULT, 'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
'period' => OtpTestData::PERIOD_DEFAULT, 'period' => OtpTestData::PERIOD_DEFAULT,
'counter' => null 'counter' => null,
]); ]);
} }
/** /**
* Provide valid Plain Text files for import tests * Provide valid Plain Text files for import tests
*/ */
@ -714,15 +691,14 @@ class TwoFAccountControllerTest extends FeatureTestCase
{ {
return [ return [
'validPlainTextFile' => [ 'validPlainTextFile' => [
LocalFile::fake()->validPlainTextFile() LocalFile::fake()->validPlainTextFile(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->validPlainTextFileWithNewLines() LocalFile::fake()->validPlainTextFileWithNewLines(),
], ],
]; ];
} }
/** /**
* @test * @test
* *
@ -730,7 +706,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
*/ */
public function test_import_invalid_plain_text_file_returns_bad_request($file) public function test_import_invalid_plain_text_file_returns_bad_request($file)
{ {
$response = $this->withHeaders(['Content-Type' => 'multipart/form-data']) $response = $this->withHeaders(['Content-Type' => 'multipart/form-data'])
->actingAs($this->user, 'api-guard') ->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/twofaccounts/migration', [ ->json('POST', '/api/v1/twofaccounts/migration', [
@ -739,7 +714,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(400); ->assertStatus(400);
} }
/** /**
* Provide invalid Plain Text files for import tests * Provide invalid Plain Text files for import tests
*/ */
@ -747,21 +721,20 @@ class TwoFAccountControllerTest extends FeatureTestCase
{ {
return [ return [
'validPlainTextFile' => [ 'validPlainTextFile' => [
LocalFile::fake()->invalidPlainTextFileEmpty() LocalFile::fake()->invalidPlainTextFileEmpty(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->invalidPlainTextFileNoUri() LocalFile::fake()->invalidPlainTextFileNoUri(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->invalidPlainTextFileWithInvalidUri() LocalFile::fake()->invalidPlainTextFileWithInvalidUri(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->invalidPlainTextFileWithInvalidLine() LocalFile::fake()->invalidPlainTextFileWithInvalidLine(),
], ],
]; ];
} }
/** /**
* @test * @test
*/ */
@ -771,14 +744,13 @@ class TwoFAccountControllerTest extends FeatureTestCase
$response = $this->actingAs($this->user, 'api-guard') $response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/twofaccounts/reorder', [ ->json('POST', '/api/v1/twofaccounts/reorder', [
'orderedIds' => [3,2,1]]) 'orderedIds' => [3, 2, 1], ])
->assertStatus(200) ->assertStatus(200)
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -788,11 +760,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
$response = $this->actingAs($this->user, 'api-guard') $response = $this->actingAs($this->user, 'api-guard')
->json('POST', '/api/v1/twofaccounts/reorder', [ ->json('POST', '/api/v1/twofaccounts/reorder', [
'orderedIds' => '3,2,1']) 'orderedIds' => '3,2,1', ])
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -806,7 +777,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP); ->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP);
} }
/** /**
* @test * @test
*/ */
@ -819,7 +789,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -831,11 +800,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
]) ])
->assertOk() ->assertOk()
->assertJsonFragment([ ->assertJsonFragment([
'icon' => null 'icon' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -863,7 +831,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -881,7 +848,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -897,7 +863,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -925,7 +890,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -943,7 +907,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -959,7 +922,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -977,7 +939,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1001,7 +962,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1012,7 +972,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -1025,7 +984,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -1036,7 +994,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -1048,11 +1005,10 @@ class TwoFAccountControllerTest extends FeatureTestCase
->json('GET', '/api/v1/twofaccounts/count') ->json('GET', '/api/v1/twofaccounts/count')
->assertStatus(200) ->assertStatus(200)
->assertExactJson([ ->assertExactJson([
'count' => 3 'count' => 3,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1069,7 +1025,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1087,7 +1042,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1100,7 +1054,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertNoContent(); ->assertNoContent();
} }
/** /**
* @test * @test
*/ */
@ -1113,7 +1066,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -1127,7 +1079,6 @@ class TwoFAccountControllerTest extends FeatureTestCase
->assertNoContent(); ->assertNoContent();
} }
/** /**
* @test * @test
*/ */
@ -1144,5 +1095,4 @@ class TwoFAccountControllerTest extends FeatureTestCase
'reason', 'reason',
]); ]);
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\GroupAssignRequest; use App\Api\v1\Requests\GroupAssignRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class GroupAssignRequestTest extends TestCase class GroupAssignRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -46,8 +45,8 @@ class GroupAssignRequestTest extends TestCase
return [ return [
[[ [[
'ids' => [ 'ids' => [
1, 2, 3 1, 2, 3,
] ],
]], ]],
]; ];
} }
@ -70,22 +69,21 @@ class GroupAssignRequestTest extends TestCase
{ {
return [ return [
[[ [[
'ids' => null // required 'ids' => null, // required
]], ]],
[[ [[
'ids' => '1,2,3' // array 'ids' => '1,2,3', // array
]], ]],
[[ [[
'ids' => [ 'ids' => [
'a', 'b', 'c' // array of integers 'a', 'b', 'c', // array of integers
] ],
]], ]],
[[ [[
'ids' => [ 'ids' => [
true, false // array of integers true, false, // array of integers
] ],
]], ]],
]; ];
} }
} }

@ -2,21 +2,17 @@
namespace Tests\Api\v1\Requests; namespace Tests\Api\v1\Requests;
use App\Models\Group;
use App\Api\v1\Requests\GroupStoreRequest; use App\Api\v1\Requests\GroupStoreRequest;
use App\Models\Group;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\FeatureTestCase; use Tests\FeatureTestCase;
class GroupStoreRequestTest extends FeatureTestCase class GroupStoreRequestTest extends FeatureTestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/**
*
*/
protected String $uniqueGroupName = 'MyGroup'; protected String $uniqueGroupName = 'MyGroup';
/** /**
@ -51,7 +47,7 @@ class GroupStoreRequestTest extends FeatureTestCase
{ {
return [ return [
[[ [[
'name' => 'validWord' 'name' => 'validWord',
]], ]],
]; ];
} }
@ -80,21 +76,20 @@ class GroupStoreRequestTest extends FeatureTestCase
{ {
return [ return [
[[ [[
'name' => '' // required 'name' => '', // required
]], ]],
[[ [[
'name' => true // string 'name' => true, // string
]], ]],
[[ [[
'name' => 8 // string 'name' => 8, // string
]], ]],
[[ [[
'name' => 'mmmmmmoooooorrrrrreeeeeeettttttthhhhhhaaaaaaannnnnn32cccccchhhhhaaaaaarrrrrrsssssss' // max:32 'name' => 'mmmmmmoooooorrrrrreeeeeeettttttthhhhhhaaaaaaannnnnn32cccccchhhhhaaaaaarrrrrrsssssss', // max:32
]], ]],
[[ [[
'name' => $this->uniqueGroupName // unique 'name' => $this->uniqueGroupName, // unique
]], ]],
]; ];
} }
} }

@ -4,14 +4,13 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\QrCodeDecodeRequest; use App\Api\v1\Requests\QrCodeDecodeRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\Classes\LocalFile; use Tests\Classes\LocalFile;
use Tests\TestCase; use Tests\TestCase;
class QrCodeDecodeRequestTest extends TestCase class QrCodeDecodeRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -48,7 +47,7 @@ class QrCodeDecodeRequestTest extends TestCase
return [ return [
[[ [[
'qrcode' => $file 'qrcode' => $file,
]], ]],
]; ];
} }
@ -71,18 +70,17 @@ class QrCodeDecodeRequestTest extends TestCase
{ {
return [ return [
[[ [[
'qrcode' => null // required 'qrcode' => null, // required
]], ]],
[[ [[
'qrcode' => true // image 'qrcode' => true, // image
]], ]],
[[ [[
'qrcode' => 8 // image 'qrcode' => 8, // image
]], ]],
[[ [[
'qrcode' => 'string' // image 'qrcode' => 'string', // image
]], ]],
]; ];
} }
} }

@ -3,20 +3,16 @@
namespace Tests\Api\v1\Requests; namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\SettingStoreRequest; use App\Api\v1\Requests\SettingStoreRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use Tests\FeatureTestCase;
use App\Facades\Settings; use App\Facades\Settings;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\FeatureTestCase;
class SettingStoreRequestTest extends FeatureTestCase class SettingStoreRequestTest extends FeatureTestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/**
*
*/
protected String $uniqueKey = 'UniqueKey'; protected String $uniqueKey = 'UniqueKey';
/** /**
@ -52,15 +48,15 @@ class SettingStoreRequestTest extends FeatureTestCase
return [ return [
[[ [[
'key' => 'MyKey', 'key' => 'MyKey',
'value' => true 'value' => true,
]], ]],
[[ [[
'key' => 'MyKey', 'key' => 'MyKey',
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
[[ [[
'key' => 'MyKey', 'key' => 'MyKey',
'value' => 10 'value' => 10,
]], ]],
]; ];
} }
@ -86,25 +82,24 @@ class SettingStoreRequestTest extends FeatureTestCase
return [ return [
[[ [[
'key' => null, // required 'key' => null, // required
'value' => '' 'value' => '',
]], ]],
[[ [[
'key' => 'my-key', // alpha 'key' => 'my-key', // alpha
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
[[ [[
'key' => 10, // alpha 'key' => 10, // alpha
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
[[ [[
'key' => 'mmmmmmoooooorrrrrreeeeeeettttttthhhhhhaaaaaaannnnnn128cccccchhhhhaaaaaarrrrrraaaaaaaccccccttttttttteeeeeeeeerrrrrrrrsssssss', // max:128 'key' => 'mmmmmmoooooorrrrrreeeeeeettttttthhhhhhaaaaaaannnnnn128cccccchhhhhaaaaaarrrrrraaaaaaaccccccttttttttteeeeeeeeerrrrrrrrsssssss', // max:128
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
[[ [[
'key' => $this->uniqueKey, // unique 'key' => $this->uniqueKey, // unique
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
]; ];
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\SettingUpdateRequest; use App\Api\v1\Requests\SettingUpdateRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class SettingUpdateRequestTest extends TestCase class SettingUpdateRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -45,13 +44,13 @@ class SettingUpdateRequestTest extends TestCase
{ {
return [ return [
[[ [[
'value' => true 'value' => true,
]], ]],
[[ [[
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
[[ [[
'value' => 10 'value' => 10,
]], ]],
]; ];
} }
@ -74,12 +73,11 @@ class SettingUpdateRequestTest extends TestCase
{ {
return [ return [
[[ [[
'value' => '' // required 'value' => '', // required
]], ]],
[[ [[
'value' => null // required 'value' => null, // required
]], ]],
]; ];
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountBatchRequest; use App\Api\v1\Requests\TwoFAccountBatchRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountBatchRequestTest extends TestCase class TwoFAccountBatchRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -45,10 +44,10 @@ class TwoFAccountBatchRequestTest extends TestCase
{ {
return [ return [
[[ [[
'ids' => '1' 'ids' => '1',
]], ]],
[[ [[
'ids' => '1,2,5' 'ids' => '1,2,5',
]], ]],
]; ];
} }
@ -71,42 +70,41 @@ class TwoFAccountBatchRequestTest extends TestCase
{ {
return [ return [
[[ [[
'ids' => '' // required 'ids' => '', // required
]], ]],
[[ [[
'ids' => null // required 'ids' => null, // required
]], ]],
[[ [[
'ids' => true // string 'ids' => true, // string
]], ]],
[[ [[
'ids' => 10 // string 'ids' => 10, // string
]], ]],
[[ [[
'ids' => 'notaCommaSeparatedList' // regex 'ids' => 'notaCommaSeparatedList', // regex
]], ]],
[[ [[
'ids' => 'a,b' // regex 'ids' => 'a,b', // regex
]], ]],
[[ [[
'ids' => 'a,1' // regex 'ids' => 'a,1', // regex
]], ]],
[[ [[
'ids' => ',1,2' // regex 'ids' => ',1,2', // regex
]], ]],
[[ [[
'ids' => '1,,2' // regex 'ids' => '1,,2', // regex
]], ]],
[[ [[
'ids' => '1,2,' // regex 'ids' => '1,2,', // regex
]], ]],
[[ [[
'ids' => ',1,2,' // regex 'ids' => ',1,2,', // regex
]], ]],
[[ [[
'ids' => '1;2' // regex 'ids' => '1;2', // regex
]], ]],
]; ];
} }
} }

@ -3,16 +3,14 @@
namespace Tests\Api\v1\Requests; namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountDynamicRequest; use App\Api\v1\Requests\TwoFAccountDynamicRequest;
use App\Api\v1\Requests\TwoFAccountUriRequest;
use App\Api\v1\Requests\TwoFAccountStoreRequest; use App\Api\v1\Requests\TwoFAccountStoreRequest;
use App\Api\v1\Requests\TwoFAccountUriRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountDynamicRequestTest extends TestCase class TwoFAccountDynamicRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -51,5 +49,4 @@ class TwoFAccountDynamicRequestTest extends TestCase
$this->assertEquals($twofaccountStoreRequest->rules(), $request->rules()); $this->assertEquals($twofaccountStoreRequest->rules(), $request->rules());
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountImportRequest; use App\Api\v1\Requests\TwoFAccountImportRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountImportRequestTest extends TestCase class TwoFAccountImportRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -45,7 +44,7 @@ class TwoFAccountImportRequestTest extends TestCase
{ {
return [ return [
[[ [[
'payload' => 'otpauth-migration://offline?data=AEoATACEAEYASAA' 'payload' => 'otpauth-migration://offline?data=AEoATACEAEYASAA',
]], ]],
]; ];
} }
@ -68,18 +67,17 @@ class TwoFAccountImportRequestTest extends TestCase
{ {
return [ return [
[[ [[
'payload' => null // required 'payload' => null, // required
]], ]],
[[ [[
'payload' => '' // required 'payload' => '', // required
]], ]],
[[ [[
'payload' => true // string 'payload' => true, // string
]], ]],
[[ [[
'payload' => 8 // string 'payload' => 8, // string
]] ]],
]; ];
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountReorderRequest; use App\Api\v1\Requests\TwoFAccountReorderRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountReorderRequestTest extends TestCase class TwoFAccountReorderRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -45,10 +44,10 @@ class TwoFAccountReorderRequestTest extends TestCase
{ {
return [ return [
[[ [[
'orderedIds' => [1,2,5] 'orderedIds' => [1, 2, 5],
]], ]],
[[ [[
'orderedIds' => [5] 'orderedIds' => [5],
]], ]],
]; ];
} }
@ -71,21 +70,20 @@ class TwoFAccountReorderRequestTest extends TestCase
{ {
return [ return [
[[ [[
'orderedIds' => [] // required 'orderedIds' => [], // required
]], ]],
[[ [[
'orderedIds' => null // required 'orderedIds' => null, // required
]], ]],
[[ [[
'orderedIds' => 0 // array 'orderedIds' => 0, // array
]], ]],
[[ [[
'orderedIds' => 'string' // array 'orderedIds' => 'string', // array
]], ]],
[[ [[
'orderedIds' => true // array 'orderedIds' => true, // array
]], ]],
]; ];
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountStoreRequest; use App\Api\v1\Requests\TwoFAccountStoreRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountStoreRequestTest extends TestCase class TwoFAccountStoreRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -167,5 +166,4 @@ class TwoFAccountStoreRequestTest extends TestCase
]], ]],
]; ];
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountUpdateRequest; use App\Api\v1\Requests\TwoFAccountUpdateRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountUpdateRequestTest extends TestCase class TwoFAccountUpdateRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -218,5 +217,4 @@ class TwoFAccountUpdateRequestTest extends TestCase
]], ]],
]; ];
} }
} }

@ -4,13 +4,12 @@ namespace Tests\Api\v1\Requests;
use App\Api\v1\Requests\TwoFAccountUriRequest; use App\Api\v1\Requests\TwoFAccountUriRequest;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase; use Tests\TestCase;
class TwoFAccountUriRequestTest extends TestCase class TwoFAccountUriRequestTest extends TestCase
{ {
use WithoutMiddleware; use WithoutMiddleware;
/** /**
@ -45,14 +44,14 @@ class TwoFAccountUriRequestTest extends TestCase
{ {
return [ return [
[[ [[
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test' 'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
]], ]],
[[ [[
'uri' => 'otpauth://hotp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test' 'uri' => 'otpauth://hotp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
]], ]],
[[ [[
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test', 'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'custom_otp' => 'steamtotp' 'custom_otp' => 'steamtotp',
]], ]],
]; ];
} }
@ -75,33 +74,32 @@ class TwoFAccountUriRequestTest extends TestCase
{ {
return [ return [
[[ [[
'uri' => null // required 'uri' => null, // required
]], ]],
[[ [[
'uri' => '' // required 'uri' => '', // required
]], ]],
[[ [[
'uri' => true // string 'uri' => true, // string
]], ]],
[[ [[
'uri' => 8 // string 'uri' => 8, // string
]], ]],
[[ [[
'uri' => 'otpXauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test' // regex 'uri' => 'otpXauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test', // regex
]], ]],
[[ [[
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test', 'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'custom_otp' => 'notSteam' // not in 'custom_otp' => 'notSteam', // not in
]], ]],
[[ [[
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test', 'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'custom_otp' => 0 // string 'custom_otp' => 0, // string
]], ]],
[[ [[
'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test', 'uri' => 'otpauth://totp/test@test.com?secret=A4GRFHZVRBGY7UIW&issuer=test',
'custom_otp' => true // string 'custom_otp' => true, // string
]], ]],
]; ];
} }
} }

@ -6,7 +6,6 @@ use Symfony\Component\HttpFoundation\File\UploadedFile as SymfonyUploadedFile;
class LocalFile extends SymfonyUploadedFile class LocalFile extends SymfonyUploadedFile
{ {
/** /**
* Begin creating a new local file fake. * Begin creating a new local file fake.
* *
@ -16,5 +15,4 @@ class LocalFile extends SymfonyUploadedFile
{ {
return new LocalFileFactory; return new LocalFileFactory;
} }
} }

Some files were not shown because too many files have changed in this diff Show More