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

View File

@ -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 @@ public function index()
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 @@ public function store(GroupStoreRequest $request)
->setStatusCode(201); ->setStatusCode(201);
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
@ -56,7 +52,6 @@ public function show(Group $group)
return new GroupResource($group); return new GroupResource($group);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
@ -71,10 +66,8 @@ public function update(GroupStoreRequest $request, Group $group)
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 @@ public function assignAccounts(GroupAssignRequest $request, Group $group)
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 @@ public function accounts(Group $group)
$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 @@ public function destroy(Group $group)
return response()->json(null, 204); return response()->json(null, 204);
} }
} }

View File

@ -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 @@ public function upload(Request $request)
: 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 @@ public function fetch(Request $request)
: response()->json(null, 204); : response()->json(null, 204);
} }
/** /**
* delete an icon * delete an icon
* *

View File

@ -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 @@ public function show(TwoFAccount $twofaccount)
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 @@ public function decode(QrCodeDecodeRequest $request)
? 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);
} }
} }

View File

@ -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 @@ public function index()
$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 @@ public function show($settingName)
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 @@ public function store(SettingStoreRequest $request)
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 @@ public function update(SettingUpdateRequest $request, string $settingName)
return response()->json([ return response()->json([
'key' => $settingName, 'key' => $settingName,
'value' => $validated['value'] 'value' => $validated['value'],
], 200); ], 200);
} }
/** /**
* Delete a setting * Delete a setting
* *
@ -108,7 +102,7 @@ public function destroy(string $settingName)
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 @@ public function destroy(string $settingName)
return response()->json(null, 204); return response()->json(null, 204);
} }
} }

View File

@ -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 @@ public function index(Request $request)
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 @@ public function show(TwoFAccount $twofaccount)
return new TwoFAccountReadResource($twofaccount); return new TwoFAccountReadResource($twofaccount);
} }
/** /**
* Store a new 2FA account * Store a new 2FA account
* *
@ -65,8 +61,7 @@ public function store(TwoFAccountDynamicRequest $request)
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 @@ public function store(TwoFAccountDynamicRequest $request)
->setStatusCode(201); ->setStatusCode(201);
} }
/** /**
* Update a 2FA account * Update a 2FA account
* *
@ -98,10 +91,8 @@ public function update(TwoFAccountUpdateRequest $request, TwoFAccount $twofaccou
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 @@ public function migrate(TwoFAccountImportRequest $request)
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 @@ public function reorder(TwoFAccountReorderRequest $request)
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 @@ public function preview(TwoFAccountUriRequest $request)
return new TwoFAccountStoreResource($twofaccount); return new TwoFAccountStoreResource($twofaccount);
} }
/** /**
* Get a One-Time Password * Get a One-Time Password
* *
@ -178,10 +165,9 @@ public function otp(Request $request, $id = null)
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 @@ public function otp(Request $request, $id = null)
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.
* *
@ -210,9 +195,7 @@ 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,7 +208,7 @@ public function withdraw(TwoFAccountBatchRequest $request)
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);
} }
@ -234,7 +217,6 @@ public function withdraw(TwoFAccountBatchRequest $request)
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 @@ public function destroy(TwoFAccount $twofaccount)
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 @@ public function batchDestroy(TwoFAccountBatchRequest $request)
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 @@ public function batchDestroy(TwoFAccountBatchRequest $request)
return response()->json(null, 204); return response()->json(null, 204);
} }
/** /**
* Checks ids length * Checks ids length
* *
@ -285,5 +265,4 @@ private function tooManyIds(string $ids) : bool
return $nb > 99 ? true : false; return $nb > 99 ? true : false;
} }
} }

View File

@ -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 @@ public function show(Request $request)
return $user return $user
? new UserResource($user) ? new UserResource($user)
: response()->json(['name' => null], 200); : response()->json(['name' => null], 200);
} }
} }

View File

@ -26,7 +26,7 @@ public function rules()
{ {
return [ return [
'ids' => 'required|array', 'ids' => 'required|array',
'ids.*' => 'integer' 'ids.*' => 'integer',
]; ];
} }
} }

View File

@ -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 @@ public function rules()
return $rules; return $rules;
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

View File

@ -37,7 +37,6 @@ public function rules()
]; ];
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

View File

@ -37,7 +37,6 @@ public function rules()
]; ];
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

View File

@ -30,7 +30,6 @@ public function rules()
]; ];
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
* *

View File

@ -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.
* *

View File

@ -37,7 +37,7 @@ public function toArray($request)
'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,
]; ];
} }
} }

View File

@ -4,7 +4,6 @@
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 @@ public function handle() : int
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;

View File

@ -42,12 +42,13 @@ public function __construct()
*/ */
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 @@ public function handle()
if ($twofaccounts->count() == 0) { if ($twofaccounts->count() == 0) {
$this->info('Nothing to fix'); $this->info('Nothing to fix');
return; return;
} }
@ -69,16 +71,14 @@ public function handle()
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));
} }
} }

View File

@ -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
{ {
@ -42,13 +42,13 @@ 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 @@ public function handle()
$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');
} }
} }

View File

@ -42,13 +42,13 @@ 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 @@ public function handle()
$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

View File

@ -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 @@ protected function flushDB() : void
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');
} }
} }

View File

@ -44,64 +44,71 @@ public function register()
{ {
$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);
} }
}); });
} }

View File

@ -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 @@ protected function getInMemoryUser()
return $this->user; return $this->user;
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function retrieveById($identifier) public function retrieveById($identifier)
{ {
@ -67,7 +64,7 @@ public function retrieveById($identifier)
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
@ -77,7 +74,7 @@ public function retrieveByToken($identifier, $token)
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
@ -87,7 +84,7 @@ public function updateRememberToken(Authenticatable $user, $token)
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
@ -97,7 +94,7 @@ public function retrieveByCredentials(array $credentials)
} }
/** /**
* @inheritDoc * {@inheritDoc}
* *
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */

View File

@ -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,7 +14,6 @@ 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
@ -40,13 +39,11 @@ public function sendResetLink(array $credentials, Closure $callback = null): str
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)

View File

@ -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 @@ public function create(string $migrationPayload) : Migrator
{ {
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
* *
@ -52,13 +48,13 @@ private function isGoogleAuth(string $migrationPayload) : bool
$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
* *
@ -77,7 +73,6 @@ private function isPlainText(string $migrationPayload) : bool
)->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 @@ private function isAegisJSON(string $migrationPayload) : mixed
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 @@ private function isAegisJSON(string $migrationPayload) : mixed
return false; return false;
} }
/** /**
* Determine if a payload comes from 2FAS Authenticator * Determine if a payload comes from 2FAS Authenticator
* *
@ -159,14 +152,13 @@ private function is2FASv2(string $migrationPayload) : mixed
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 @@ private function is2FASv2(string $migrationPayload) : mixed
return false; return false;
} }
} }

View File

@ -17,7 +17,12 @@ public static function getUniqueFilename(string $extension): string
return Str::random(40) . '.' . $extension; return Str::random(40) . '.' . $extension;
} }
/**
* Clean a version number string
*
* @param string|null $release
* @return string|false
*/
public static function cleanVersionNumber(?string $release) : 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;

View File

@ -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.
* *

View File

@ -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 @@ public function login(LoginRequest $request)
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 @@ public function logout(Request $request)
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 @@ protected function sendLoginResponse(Request $request)
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 @@ protected function sendFailedLoginResponse(Request $request)
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 @@ protected function sendLockoutResponse(Request $request)
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 @@ protected function credentials(Request $request)
return $credentials; return $credentials;
} }
/** /**
* The user has been authenticated. * The user has been authenticated.
* *

View File

@ -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;
@ -22,6 +22,7 @@ public function update(UserPatchPwdRequest $request)
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);
} }

View File

@ -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 @@ public function register(UserStoreRequest $request)
], 201); ], 201);
} }
/** /**
* Create a new user instance after a valid registration. * Create a new user instance after a valid registration.
* *

View File

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

View File

@ -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
@ -27,6 +27,7 @@ public function update(UserUpdateRequest $request)
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);
} }
@ -41,7 +42,6 @@ public function update(UserUpdateRequest $request)
return new UserResource($user); return new UserResource($user);
} }
/** /**
* Delete the user's account. * Delete the user's account.
* *
@ -79,6 +79,7 @@ public function delete(UserDeleteRequest $request)
// @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

View File

@ -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 @@ public function sendRecoveryEmail(WebauthnDeviceLostRequest $request, WebauthnCr
: $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 @@ protected function sendRecoveryLinkFailedResponse(Request $request, string $resp
->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)

View File

@ -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
@ -50,11 +50,10 @@ public function options(AssertionRequest $request): Responsable|JsonResponse
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.
* *
@ -80,18 +79,17 @@ public function login(AssertedRequest $request)
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)

View File

@ -4,13 +4,12 @@
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 @@ public function index(Request $request)
return response()->json($allUserCredentials, 200); return response()->json($allUserCredentials, 200);
} }
/** /**
* Rename a WebAuthn credential * Rename a WebAuthn credential
* *
@ -42,13 +40,11 @@ public function rename(WebauthnRenameRequest $request, string $credential)
], 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)

View File

@ -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,23 +53,21 @@ function ($user) use ($request) {
$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
@ -79,41 +76,34 @@ protected function shouldRevokeAllCredentials(WebauthnRecoveryRequest $request):
?: $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)]]);
} }
} }
} }

View File

@ -34,7 +34,6 @@ public function options(AttestationRequest $request): Responsable
->toCreate(); ->toCreate();
} }
/** /**
* Registers a device for further WebAuthn authentication. * Registers a device for further WebAuthn authentication.
* *

View File

@ -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
{ {

View File

@ -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 @@ public function index()
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 */
]); ]);
} }
} }

View File

@ -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 @@ public function infos(Request $request)
return response()->json($infos); return response()->json($infos);
} }
/** /**
* Get latest release * Get latest release
* *

View File

@ -20,8 +20,7 @@ protected function authenticate($request, array $guards)
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 @@ protected function authenticate($request, array $guards)
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);
} }
} }

View File

@ -6,7 +6,6 @@
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.
* *

View File

@ -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 @@ public function handle($request, Closure $next, ...$guards)
// 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();

View File

@ -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

View File

@ -17,7 +17,6 @@ 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');

View File

@ -20,7 +20,8 @@ public function handle($request, Closure $next)
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);

View File

@ -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
{ {
@ -28,14 +28,15 @@ public function handle($request, Closure $next)
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;
}, },
[] []

View File

@ -26,7 +26,7 @@ public function handle(Request $request, Closure $next, ...$guards)
return response()->json([ return response()->json([
'message' => 'authenticated', 'message' => 'authenticated',
'name' => $user 'name' => $user,
], 200); ], 200);
} }
} }

View File

@ -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 |

View File

@ -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 @@ public function rules()
'email' => [ 'email' => [
'required', 'required',
'email', 'email',
new \App\Rules\CaseInsensitiveEmailExists new \App\Rules\CaseInsensitiveEmailExists,
], ],
'password' => 'required|string', 'password' => 'required|string',
]; ];

View File

@ -5,7 +5,6 @@
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
{ {
/** /**

View File

@ -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 @@ public function rules()
'email' => [ 'email' => [
'required', 'required',
'email', 'email',
new \App\Rules\CaseInsensitiveEmailExists new \App\Rules\CaseInsensitiveEmailExists,
], ],
]; ];
} }

View File

@ -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
{ {

View File

@ -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 @@ public function handle(GroupDeleting $event)
{ {
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));

View File

@ -4,22 +4,19 @@
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 @@ public function __construct(ReleaseRadarService $releaseRadar)
$this->releaseRadar = $releaseRadar; $this->releaseRadar = $releaseRadar;
} }
/** /**
* Handle the event. * Handle the event.
* *

View File

@ -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 @@ protected static function boot()
}); });
} }
/** /**
* Get the TwoFAccounts of the group. * Get the TwoFAccounts of the group.
* *

View File

@ -4,7 +4,6 @@
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 = [];
} }

View File

@ -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
@ -28,7 +28,6 @@ public function userHandle(): string
?? 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.
* *
@ -41,7 +40,6 @@ 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.
* *
@ -63,12 +61,10 @@ public function flushCredential($id): void
} }
} }
/** /**
* 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
@ -92,6 +88,5 @@ public function sendWebauthnRecoveryNotification(string $token): void
// }); // });
$this->notify(new WebauthnRecoveryNotification($token)); $this->notify(new WebauthnRecoveryNotification($token));
} }
} }

View File

@ -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 @@ protected static function boot()
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 @@ protected static function boot()
// }); // });
} }
/** /**
* Settings for @spatie/eloquent-sortable package * Settings for @spatie/eloquent-sortable package
* *
@ -165,7 +170,6 @@ protected static function boot()
'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 @@ protected static function boot()
*/ */
protected $generator = null; protected $generator = null;
/** /**
* Get legacy_uri attribute * Get legacy_uri attribute
* *
@ -183,9 +186,9 @@ protected static function boot()
*/ */
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 @@ public function setLegacyUriAttribute($value)
$this->attributes['legacy_uri'] = $this->encryptOrReturn($value); $this->attributes['legacy_uri'] = $this->encryptOrReturn($value);
} }
/** /**
* Get account attribute * Get account attribute
* *
@ -207,9 +209,9 @@ public function setLegacyUriAttribute($value)
*/ */
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 @@ public function setAccountAttribute($value)
$this->attributes['account'] = $this->encryptOrReturn($value); $this->attributes['account'] = $this->encryptOrReturn($value);
} }
/** /**
* Get secret attribute * Get secret attribute
* *
@ -231,9 +232,9 @@ public function setAccountAttribute($value)
*/ */
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 @@ public function setSecretAttribute($value)
$this->attributes['secret'] = $this->encryptOrReturn($value); $this->attributes['secret'] = $this->encryptOrReturn($value);
} }
/** /**
* Set digits attribute * Set digits attribute
* *
@ -258,7 +258,6 @@ public function setDigitsAttribute($value)
$this->attributes['digits'] = ! $value ? 6 : $value; $this->attributes['digits'] = ! $value ? 6 : $value;
} }
/** /**
* Set algorithm attribute * Set algorithm attribute
* *
@ -270,7 +269,6 @@ 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
* *
@ -282,7 +280,6 @@ 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,15 +291,15 @@ public function setCounterAttribute($value)
$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()
{ {
@ -319,7 +316,6 @@ public function getOTP()
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 @@ public function getOTP()
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();
@ -345,9 +339,7 @@ public function getOTP()
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 @@ public function getOTP()
} }
} }
/** /**
* 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
@ -398,7 +389,6 @@ public function fillWithOtpParameters(array $parameters, bool $skipIconFetching
return $this; return $this;
} }
/** /**
* Fill the model by parsing an otpauth URI * Fill the model by parsing an otpauth URI
* *
@ -409,10 +399,9 @@ public function fillWithURI(string $uri, bool $isSteamTotp = false, bool $skipIc
// 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']),
]); ]);
} }
@ -422,7 +411,7 @@ public function fillWithURI(string $uri, bool $isSteamTotp = false, bool $skipIc
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'),
]); ]);
} }
@ -452,7 +441,6 @@ public function fillWithURI(string $uri, bool $isSteamTotp = false, bool $skipIc
return $this; return $this;
} }
/** /**
* Sets model attributes to STEAM values * Sets model attributes to STEAM values
*/ */
@ -466,7 +454,6 @@ private function enforceAsSteam() : void
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 @@ private function getGeneratorOtpType()
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 @@ public function getURI() : string
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 @@ private function initGenerator() : void
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 @@ private function storeImageAsIcon(string $url)
{ {
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 @@ private function storeImageAsIcon(string $url)
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 @@ private function storeImageAsIcon(string $url)
// @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 @@ private function getDefaultIcon()
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 @@ private function encryptOrReturn(mixed $value) : mixed
// 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;
} }
} }

View File

@ -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 @@ public function sendPasswordResetNotification($token)
/** /**
* set Email attribute * set Email attribute
*
* @param string $value * @param string $value
*/ */
public function setEmailAttribute($value) : void public function setEmailAttribute($value) : void
@ -66,7 +67,6 @@ public function setEmailAttribute($value) : void
$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.
* *

View File

@ -13,7 +13,6 @@ interface WebAuthnAuthenticatable extends Authenticatable
*/ */
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.
* *
@ -23,7 +22,6 @@ public function userHandle(): string;
*/ */
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.
* *
@ -32,7 +30,6 @@ public function renameCredential(string $id, string $alias): bool;
*/ */
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.
* *

View File

@ -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
{ {
/** /**

View File

@ -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,16 +23,15 @@ 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) {
@ -62,7 +60,6 @@ static function ($app) {
); );
} }
/** /**
* Register any authentication / authorization services. * Register any authentication / authorization services.
* *
@ -86,7 +83,6 @@ public function boot()
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
@ -104,7 +100,6 @@ static function (\Illuminate\Contracts\Foundation\Application $app, array $confi
} }
); );
// 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())

View File

@ -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
{ {

View File

@ -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;

View File

@ -4,8 +4,8 @@
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;

View File

@ -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 @@ public function boot()
// //
} }
/** /**
* Get the services provided by the provider. * Get the services provided by the provider.
* *

View File

@ -35,7 +35,9 @@ public function passes($attribute, $value)
/** /**
* Get the validation error message. * Get the validation error message.
*
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @return array|string * @return array|string
*/ */
public function message() public function message()

View File

@ -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 @@ public function __construct(UserProvider $provider)
} }
/** /**
* @inheritDoc * {@inheritDoc}
*/ */
public function user() public function user()
{ {
@ -47,17 +47,17 @@ public function user()
// 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;
} }
@ -67,8 +67,7 @@ public function user()
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;
} }

View File

@ -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 @@ public static function getAll() : Collection
// 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 @@ public static function getAll() : Collection
return $groups->prepend($allGroup); return $groups->prepend($allGroup);
} }
/** /**
* Creates a group * Creates a group
* *
@ -58,7 +56,6 @@ public static function create(array $data) : Group
return $group; return $group;
} }
/** /**
* Updates a group using a list of parameters * Updates a group using a list of parameters
* *
@ -77,7 +74,6 @@ public static function update(Group $group, array $data) : Group
return $group; return $group;
} }
/** /**
* Deletes one or more groups * Deletes one or more groups
* *
@ -112,7 +108,6 @@ public static function delete($ids) : int
return $deleted; return $deleted;
} }
/** /**
* Assign one or more accounts to a group * Assign one or more accounts to a group
* *
@ -130,7 +125,7 @@ public static function assign($ids, Group $group = null) : void
// 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 @@ public static function assign($ids, Group $group = null) : void
$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 @@ public static function getAccounts(Group $group) : Collection
return $twofaccounts; return $twofaccounts;
} }
/** /**
* Determines the destination group * Determines the destination group
* *

View File

@ -4,8 +4,8 @@
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
* *
@ -44,11 +42,12 @@ public function getIcon($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
@ -68,7 +67,6 @@ protected function getLogo($serviceName)
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 @@ protected function setTfaCollection() : void
: 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 @@ protected function cacheTfaDirectorySource() : void
$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
* *
@ -137,13 +130,11 @@ protected function fetchLogo(string $logoFile) : void
? 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 @@ protected function cleanDomain(string $domain) : string
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
* *

View File

@ -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 @@ public function migrate(mixed $migrationPayload) : Collection
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 @@ public function migrate(mixed $migrationPayload) : Collection
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());

View File

@ -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,20 +30,18 @@ public function migrate(mixed $migrationPayload) : Collection
$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());
@ -57,9 +53,7 @@ public function migrate(mixed $migrationPayload) : Collection
$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());

View File

@ -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 @@ protected function padToValidBase32Secret(string $string)
{ {
return str_pad($string, 8, '='); return str_pad($string, 8, '=');
} }
} }

View File

@ -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 @@ public function migrate(mixed $migrationPayload) : Collection
} }
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());

View File

@ -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 @@ public function migrate(mixed $migrationPayload) : Collection
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 @@ public function migrate(mixed $migrationPayload) : Collection
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());

View File

@ -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 @@ public static function encode(string $data)
return $qrcode->render($data); return $qrcode->render($data);
} }
/** /**
* Decode an uploaded QR code image * Decode an uploaded QR code image
* *

View File

@ -21,7 +21,6 @@ public function scheduledScan() : void
} }
} }
/** /**
* Run a manual release scan * Run a manual release scan
* *
@ -39,8 +38,7 @@ public function manualScan() : false|string
*/ */
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 @@ protected function newRelease() : false|string
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 @@ protected function newRelease() : false|string
return false; return false;
} }
/** /**
* Fetch releases on Github * Fetch releases on Github
* *
@ -74,8 +70,7 @@ protected function getLatestReleaseData() : string|null
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');
} }

View File

@ -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 @@ public function __construct()
self::build(); self::build();
} }
/** /**
* Get a setting * Get a setting
* *
@ -44,7 +41,6 @@ public function get($setting)
return $this->settings->get($setting); return $this->settings->get($setting);
} }
/** /**
* Get all settings * Get all settings
* *
@ -55,20 +51,18 @@ public function all() : Collection
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 @@ public function set($setting, $value = null) : void
self::build(); self::build();
} }
/** /**
* Delete a setting * Delete a setting
* *
@ -95,7 +88,6 @@ public function delete(string $name) : void
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 @@ public function isUserDefined($key) : bool
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,7 +114,6 @@ private function build()
// 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';
} }
@ -131,7 +121,6 @@ private function build()
$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 @@ private function replaceBoolean(mixed $value)
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
* *
@ -156,20 +144,18 @@ private function restoreType(mixed $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
@ -181,22 +167,21 @@ private function setEncryptionTo(bool $state) : void
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
{ {
@ -208,8 +193,7 @@ private function updateRecords(bool $encrypted) : bool
$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 @@ private function updateRecords(bool $encrypted) : bool
->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 {
else return false; return false;
}
} }
} }

View File

@ -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 @@ public function __construct(MigratorFactoryInterface $migratorFactory)
$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 @@ public static function withdraw($ids) : void
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 @@ public function migrate(string $migrationPayload) : Collection
return self::markAsDuplicate($twofaccounts); return self::markAsDuplicate($twofaccounts);
} }
/** /**
* Delete one or more twofaccounts * Delete one or more twofaccounts
* *
@ -79,7 +75,6 @@ public static function delete($ids) : int
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 @@ private static function markAsDuplicate(Collection $twofaccounts) : Collection
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 @@ private static function markAsDuplicate(Collection $twofaccounts) : Collection
*/ */
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);

View File

@ -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"
}
}
} }

View File

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

View File

@ -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,7 +18,6 @@ class GroupControllerTest extends FeatureTestCase
*/ */
protected $user; protected $user;
/** /**
* @test * @test
*/ */
@ -30,7 +28,6 @@ public function setUp(): void
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -47,7 +44,7 @@ public function test_index_returns_group_collection_with_pseudo_group()
'id', 'id',
'name', 'name',
'twofaccounts_count', 'twofaccounts_count',
] ],
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'id' => 0, 'id' => 0,
@ -56,7 +53,6 @@ public function test_index_returns_group_collection_with_pseudo_group()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -73,7 +69,6 @@ public function test_store_returns_created_group_resource()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -86,7 +81,6 @@ public function test_store_invalid_data_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -105,7 +99,6 @@ public function test_show_returns_group_resource()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -115,11 +108,10 @@ public function test_show_missing_group_returns_not_found()
->json('GET', '/api/v1/groups/1000') ->json('GET', '/api/v1/groups/1000')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -138,7 +130,6 @@ public function test_update_returns_updated_group_resource()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -150,11 +141,10 @@ public function test_update_missing_group_returns_not_found()
]) ])
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -169,7 +159,6 @@ public function test_update_with_invalid_data_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -190,7 +179,6 @@ public function test_assign_accounts_returns_updated_group_resource()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -204,11 +192,10 @@ public function test_assign_accounts_to_missing_group_returns_not_found()
]) ])
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -224,7 +211,6 @@ public function test_assign_invalid_accounts_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -252,12 +238,11 @@ public function test_get_assigned_accounts_returns_twofaccounts_collection()
'digits', 'digits',
'algorithm', 'algorithm',
'period', 'period',
'counter' 'counter',
] ],
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -286,12 +271,11 @@ public function test_get_assigned_accounts_returns_twofaccounts_collection_with_
'digits', 'digits',
'algorithm', 'algorithm',
'period', 'period',
'counter' 'counter',
] ],
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -301,11 +285,10 @@ public function test_get_assigned_accounts_of_missing_group_returns_not_found()
->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 @@ public function test_destroy_group_returns_success()
->assertNoContent(); ->assertNoContent();
} }
/** /**
* test Group deletion via API * test Group deletion via API
* *
@ -332,7 +314,7 @@ public function test_destroy_missing_group_returns_not_found()
->json('DELETE', '/api/v1/groups/1000') ->json('DELETE', '/api/v1/groups/1000')
->assertNotFound() ->assertNotFound()
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
} }

View File

@ -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 @@ public function test_upload_icon_returns_filename()
]) ])
->assertCreated() ->assertCreated()
->assertJsonStructure([ ->assertJsonStructure([
'filename' 'filename',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -45,7 +40,6 @@ public function test_upload_with_invalid_data_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -55,7 +49,6 @@ public function test_delete_icon_returns_success()
->assertNoContent(204); ->assertNoContent(204);
} }
/** /**
* @test * @test
*/ */
@ -63,7 +56,5 @@ public function test_delete_invalid_icon_returns_success()
{ {
$response = $this->json('DELETE', '/api/v1/icons/null') $response = $this->json('DELETE', '/api/v1/icons/null')
->assertNoContent(204); ->assertNoContent(204);
} }
} }

View File

@ -2,24 +2,21 @@
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
*/ */
@ -30,7 +27,6 @@ public function setUp(): void
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -57,7 +53,6 @@ public function test_show_qrcode_returns_base64_image()
$this->assertStringStartsWith('data:image/png;base64', $response->getData()->qrcode); $this->assertStringStartsWith('data:image/png;base64', $response->getData()->qrcode);
} }
/** /**
* @test * @test
*/ */
@ -67,11 +62,10 @@ public function test_show_missing_qrcode_returns_not_found()
->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 @@ public function test_decode_qrcode_return_success()
->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 @@ public function test_decode_qrcode_return_success()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -104,7 +97,6 @@ public function test_decode_missing_qrcode_return_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -116,7 +108,7 @@ public function test_decode_invalid_qrcode_return_bad_request()
->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([

View File

@ -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,13 +18,19 @@ 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';
/** /**
@ -38,7 +43,6 @@ public function setUp(): void
$this->user = User::factory()->create(); $this->user = User::factory()->create();
} }
/** /**
* @test * @test
*/ */
@ -48,11 +52,10 @@ public function test_index_returns_setting_collection()
->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 @@ public function test_show_native_unchanged_setting_returns_consistent_value()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -84,7 +86,6 @@ public function test_show_native_changed_setting_returns_consistent_value()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -101,7 +102,6 @@ public function test_show_custom_user_setting_returns_consistent_value()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -112,7 +112,6 @@ public function test_show_missing_setting_returns_not_found()
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -130,7 +129,6 @@ public function test_store_custom_user_setting_returns_success()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -144,7 +142,6 @@ public function test_store_invalid_custom_user_setting_returns_validation_error(
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -160,7 +157,6 @@ public function test_store_existing_custom_user_setting_returns_validation_error
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -177,7 +173,6 @@ public function test_update_unchanged_native_setting_returns_updated_setting()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -196,7 +191,6 @@ public function test_update_custom_user_setting_returns_updated_setting()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -213,7 +207,6 @@ public function test_update_missing_user_setting_returns_created_setting()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -226,7 +219,6 @@ public function test_destroy_user_setting_returns_success()
->assertNoContent(); ->assertNoContent();
} }
/** /**
* @test * @test
*/ */
@ -241,7 +233,6 @@ public function test_destroy_native_setting_returns_bad_request()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -251,6 +242,4 @@ public function test_destroy_missing_user_setting_returns_not_found()
->json('DELETE', '/api/v1/settings/' . self::USER_DEFINED_SETTING) ->json('DELETE', '/api/v1/settings/' . self::USER_DEFINED_SETTING)
->assertNotFound(); ->assertNotFound();
} }
} }

View File

@ -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,13 +112,13 @@ 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
*/ */
@ -126,7 +130,6 @@ public function setUp(): void
$this->group = Group::factory()->create(); $this->group = Group::factory()->create();
} }
/** /**
* @test * @test
* *
@ -141,11 +144,10 @@ public function test_index_returns_twofaccount_collection($urlParameter, $expect
->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 @@ public function indexUrlParameterProvider()
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 @@ public function test_show_twofaccount_returns_twofaccount_resource_with_secret()
->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITH_SECRET); ->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITH_SECRET);
} }
/** /**
* @test * @test
*/ */
@ -191,7 +191,6 @@ public function test_show_twofaccount_returns_twofaccount_resource_without_secre
->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET); ->assertJsonStructure(self::VALID_RESOURCE_STRUCTURE_WITHOUT_SECRET);
} }
/** /**
* @test * @test
*/ */
@ -217,7 +216,6 @@ public function test_show_twofaccount_returns_twofaccount_resource_without_secre
// ]); // ]);
// } // }
/** /**
* @test * @test
*/ */
@ -227,11 +225,10 @@ public function test_show_missing_twofaccount_returns_not_found()
->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 @@ public function test_store_without_encryption_returns_success_with_consistent_re
->assertJsonFragment($expected); ->assertJsonFragment($expected);
} }
/** /**
* @dataProvider accountCreationProvider * @dataProvider accountCreationProvider
* @test * @test
@ -265,7 +261,6 @@ public function test_store_with_encryption_returns_success_with_consistent_resou
->assertJsonFragment($expected); ->assertJsonFragment($expected);
} }
/** /**
* Provide data for TwoFAccount store tests * Provide data for TwoFAccount store tests
*/ */
@ -276,46 +271,45 @@ public function accountCreationProvider()
[ [
'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 @@ public function test_store_with_invalid_uri_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -342,11 +335,10 @@ public function test_store_assigns_created_account_when_default_group_is_a_speci
'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 @@ public function test_store_assigns_created_account_when_default_group_is_the_act
'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 @@ public function test_store_assigns_created_account_when_default_group_is_no_grou
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'group_id' => null 'group_id' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -398,11 +388,10 @@ public function test_store_assigns_created_account_when_default_group_does_not_e
'uri' => OtpTestData::TOTP_SHORT_URI, 'uri' => OtpTestData::TOTP_SHORT_URI,
]) ])
->assertJsonFragment([ ->assertJsonFragment([
'group_id' => null 'group_id' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -416,7 +405,6 @@ public function test_update_totp_returns_success_with_updated_resource()
->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP); ->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP);
} }
/** /**
* @test * @test
*/ */
@ -430,7 +418,6 @@ public function test_update_hotp_returns_success_with_updated_resource()
->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP); ->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_HOTP);
} }
/** /**
* @test * @test
*/ */
@ -441,7 +428,6 @@ public function test_update_missing_twofaccount_returns_not_found()
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -454,7 +440,6 @@ public function test_update_twofaccount_with_invalid_data_returns_validation_err
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -476,7 +461,7 @@ public function test_import_valid_gauth_payload_returns_success_with_consistent_
'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 @@ public function test_import_valid_gauth_payload_returns_success_with_consistent_
'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 @@ public function test_import_with_invalid_gauth_payload_returns_validation_error(
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -534,7 +517,6 @@ public function test_import_gauth_payload_with_duplicates_returns_negative_ids()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -546,11 +528,10 @@ public function test_import_invalid_gauth_payload_returns_bad_request()
]) ])
->assertStatus(400) ->assertStatus(400)
->assertJsonStructure([ ->assertJsonStructure([
'message' 'message',
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -575,7 +556,7 @@ public function test_import_valid_aegis_json_file_returns_success()
'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 @@ public function test_import_valid_aegis_json_file_returns_success()
'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 @@ public function test_import_valid_aegis_json_file_returns_success()
'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 @@ public function test_import_valid_aegis_json_file_returns_success()
'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 @@ public function test_import_invalid_aegis_json_file_returns_bad_request($file)
->assertStatus(400); ->assertStatus(400);
} }
/** /**
* Provide invalid Aegis JSON files for import tests * Provide invalid Aegis JSON files for import tests
*/ */
@ -647,15 +626,14 @@ public function invalidAegisJsonFileProvider()
{ {
return [ return [
'validPlainTextFile' => [ 'validPlainTextFile' => [
LocalFile::fake()->encryptedAegisJsonFile() LocalFile::fake()->encryptedAegisJsonFile(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->invalidAegisJsonFile() LocalFile::fake()->invalidAegisJsonFile(),
], ],
]; ];
} }
/** /**
* @test * @test
* *
@ -680,7 +658,7 @@ public function test_import_valid_plain_text_file_returns_success($file)
'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 @@ public function test_import_valid_plain_text_file_returns_success($file)
'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 @@ public function test_import_valid_plain_text_file_returns_success($file)
'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 @@ public function validPlainTextFileProvider()
{ {
return [ return [
'validPlainTextFile' => [ 'validPlainTextFile' => [
LocalFile::fake()->validPlainTextFile() LocalFile::fake()->validPlainTextFile(),
], ],
'validPlainTextFileWithNewLines' => [ 'validPlainTextFileWithNewLines' => [
LocalFile::fake()->validPlainTextFileWithNewLines() LocalFile::fake()->validPlainTextFileWithNewLines(),
], ],
]; ];
} }
/** /**
* @test * @test
* *
@ -730,7 +706,6 @@ public function validPlainTextFileProvider()
*/ */
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 @@ public function test_import_invalid_plain_text_file_returns_bad_request($file)
->assertStatus(400); ->assertStatus(400);
} }
/** /**
* Provide invalid Plain Text files for import tests * Provide invalid Plain Text files for import tests
*/ */
@ -747,21 +721,20 @@ public function invalidPlainTextFileProvider()
{ {
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 @@ public function test_reorder_returns_success()
$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 @@ public function test_reorder_with_invalid_data_returns_validation_error()
$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 @@ public function test_preview_returns_success_with_resource()
->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP); ->assertJsonFragment(self::JSON_FRAGMENTS_FOR_CUSTOM_TOTP);
} }
/** /**
* @test * @test
*/ */
@ -819,7 +789,6 @@ public function test_preview_with_invalid_data_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -831,11 +800,10 @@ public function test_preview_with_unreachable_image_returns_success()
]) ])
->assertOk() ->assertOk()
->assertJsonFragment([ ->assertJsonFragment([
'icon' => null 'icon' => null,
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -863,7 +831,6 @@ public function test_get_otp_using_totp_twofaccount_id_returns_consistent_resour
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -881,7 +848,6 @@ public function test_get_otp_by_posting_totp_uri_returns_consistent_resource()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -897,7 +863,6 @@ public function test_get_otp_by_posting_totp_parameters_returns_consistent_resou
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -925,7 +890,6 @@ public function test_get_otp_using_hotp_twofaccount_id_returns_consistent_resour
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -943,7 +907,6 @@ public function test_get_otp_by_posting_hotp_uri_returns_consistent_resource()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -959,7 +922,6 @@ public function test_get_otp_by_posting_hotp_parameters_returns_consistent_resou
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -977,7 +939,6 @@ public function test_get_otp_by_posting_multiple_inputs_returns_bad_request()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1001,7 +962,6 @@ public function test_get_otp_using_indecipherable_twofaccount_id_returns_bad_req
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1012,7 +972,6 @@ public function test_get_otp_using_missing_twofaccount_id_returns_not_found()
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -1025,7 +984,6 @@ public function test_get_otp_by_posting_invalid_uri_returns_validation_error()
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -1036,7 +994,6 @@ public function test_get_otp_by_posting_invalid_parameters_returns_validation_er
->assertStatus(422); ->assertStatus(422);
} }
/** /**
* @test * @test
*/ */
@ -1048,11 +1005,10 @@ public function test_count_returns_right_number_of_twofaccount()
->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 @@ public function test_withdraw_returns_success()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1087,7 +1042,6 @@ public function test_withdraw_too_many_ids_returns_bad_request()
]); ]);
} }
/** /**
* @test * @test
*/ */
@ -1100,7 +1054,6 @@ public function test_destroy_twofaccount_returns_success()
->assertNoContent(); ->assertNoContent();
} }
/** /**
* @test * @test
*/ */
@ -1113,7 +1066,6 @@ public function test_destroy_missing_twofaccount_returns_not_found()
->assertNotFound(); ->assertNotFound();
} }
/** /**
* @test * @test
*/ */
@ -1127,7 +1079,6 @@ public function test_batch_destroy_twofaccount_returns_success()
->assertNoContent(); ->assertNoContent();
} }
/** /**
* @test * @test
*/ */
@ -1144,5 +1095,4 @@ public function test_batch_destroy_too_many_twofaccounts_returns_bad_request()
'reason', 'reason',
]); ]);
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideValidData() : array
return [ return [
[[ [[
'ids' => [ 'ids' => [
1, 2, 3 1, 2, 3,
] ],
]], ]],
]; ];
} }
@ -70,22 +69,21 @@ public function provideInvalidData() : array
{ {
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
] ],
]], ]],
]; ];
} }
} }

View File

@ -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 @@ public function provideValidData() : array
{ {
return [ return [
[[ [[
'name' => 'validWord' 'name' => 'validWord',
]], ]],
]; ];
} }
@ -80,21 +76,20 @@ public function provideInvalidData() : array
{ {
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
]], ]],
]; ];
} }
} }

View File

@ -4,14 +4,13 @@
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 @@ public function provideValidData() : array
return [ return [
[[ [[
'qrcode' => $file 'qrcode' => $file,
]], ]],
]; ];
} }
@ -71,18 +70,17 @@ public function provideInvalidData() : array
{ {
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
]], ]],
]; ];
} }
} }

View File

@ -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 @@ public function provideValidData() : array
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 @@ public function provideInvalidData() : array
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',
]], ]],
]; ];
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideValidData() : array
{ {
return [ return [
[[ [[
'value' => true 'value' => true,
]], ]],
[[ [[
'value' => 'MyValue' 'value' => 'MyValue',
]], ]],
[[ [[
'value' => 10 'value' => 10,
]], ]],
]; ];
} }
@ -74,12 +73,11 @@ public function provideInvalidData() : array
{ {
return [ return [
[[ [[
'value' => '' // required 'value' => '', // required
]], ]],
[[ [[
'value' => null // required 'value' => null, // required
]], ]],
]; ];
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideValidData() : array
{ {
return [ return [
[[ [[
'ids' => '1' 'ids' => '1',
]], ]],
[[ [[
'ids' => '1,2,5' 'ids' => '1,2,5',
]], ]],
]; ];
} }
@ -71,42 +70,41 @@ public function provideInvalidData() : array
{ {
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
]], ]],
]; ];
} }
} }

View File

@ -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 @@ public function test_returns_TwoFAccountStoreRequest_rules_otherwise()
$this->assertEquals($twofaccountStoreRequest->rules(), $request->rules()); $this->assertEquals($twofaccountStoreRequest->rules(), $request->rules());
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideValidData() : array
{ {
return [ return [
[[ [[
'payload' => 'otpauth-migration://offline?data=AEoATACEAEYASAA' 'payload' => 'otpauth-migration://offline?data=AEoATACEAEYASAA',
]], ]],
]; ];
} }
@ -68,18 +67,17 @@ public function provideInvalidData() : array
{ {
return [ return [
[[ [[
'payload' => null // required 'payload' => null, // required
]], ]],
[[ [[
'payload' => '' // required 'payload' => '', // required
]], ]],
[[ [[
'payload' => true // string 'payload' => true, // string
]], ]],
[[ [[
'payload' => 8 // string 'payload' => 8, // string
]] ]],
]; ];
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideValidData() : array
{ {
return [ return [
[[ [[
'orderedIds' => [1,2,5] 'orderedIds' => [1, 2, 5],
]], ]],
[[ [[
'orderedIds' => [5] 'orderedIds' => [5],
]], ]],
]; ];
} }
@ -71,21 +70,20 @@ public function provideInvalidData() : array
{ {
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
]], ]],
]; ];
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideInvalidData() : array
]], ]],
]; ];
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideInvalidData() : array
]], ]],
]; ];
} }
} }

View File

@ -4,13 +4,12 @@
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 @@ public function provideValidData() : array
{ {
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 @@ public function provideInvalidData() : array
{ {
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
]], ]],
]; ];
} }
} }

View File

@ -6,7 +6,6 @@
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 @@ public static function fake()
{ {
return new LocalFileFactory; return new LocalFileFactory;
} }
} }

View File

@ -3,10 +3,9 @@
namespace Tests\Classes; namespace Tests\Classes;
use Illuminate\Http\Testing\File; use Illuminate\Http\Testing\File;
use Tests\Classes\OtpTestData;
class LocalFileFactory {
class LocalFileFactory
{
/** /**
* Create a new local valid qrcode image. * Create a new local valid qrcode image.
* *
@ -28,7 +27,6 @@ public function validQrcode()
})); }));
} }
/** /**
* Create a new local invalid qrcode image. * Create a new local invalid qrcode image.
* *
@ -50,7 +48,6 @@ public function invalidQrcode()
})); }));
} }
/** /**
* Create a new local valid Aegis JSON file. * Create a new local valid Aegis JSON file.
* *
@ -67,7 +64,6 @@ public function validAegisJsonFile()
})); }));
} }
/** /**
* Create a new local invalid Aegis JSON file. * Create a new local invalid Aegis JSON file.
* *
@ -84,7 +80,6 @@ public function invalidAegisJsonFile()
})); }));
} }
/** /**
* Create a new local encrypted Aegis JSON file. * Create a new local encrypted Aegis JSON file.
* *
@ -101,7 +96,6 @@ public function encryptedAegisJsonFile()
})); }));
} }
/** /**
* Create a new local valid Plain Text file. * Create a new local valid Plain Text file.
* *
@ -122,7 +116,6 @@ public function validPlainTextFile()
})); }));
} }
/** /**
* Create a new local valid Plain Text file with new lines. * Create a new local valid Plain Text file with new lines.
* *
@ -147,7 +140,6 @@ public function validPlainTextFileWithNewLines()
})); }));
} }
/** /**
* Create a new local invalid Plain Text file with no URI. * Create a new local invalid Plain Text file with no URI.
* *
@ -166,7 +158,6 @@ public function invalidPlainTextFileNoUri()
})); }));
} }
/** /**
* Create a new local invalid Plain Text file with invalid line. * Create a new local invalid Plain Text file with invalid line.
* *
@ -187,7 +178,6 @@ public function invalidPlainTextFileWithInvalidLine()
})); }));
} }
/** /**
* Create a new local invalid Plain Text file with invalid URI. * Create a new local invalid Plain Text file with invalid URI.
* *
@ -208,7 +198,6 @@ public function invalidPlainTextFileWithInvalidUri()
})); }));
} }
/** /**
* Create a new local empty Plain Text file. * Create a new local empty Plain Text file.
* *
@ -224,6 +213,4 @@ public function invalidPlainTextFileEmpty()
fwrite($temp, ob_get_clean()); fwrite($temp, ob_get_clean());
})); }));
} }
} }

View File

@ -5,27 +5,49 @@
class OtpTestData class OtpTestData
{ {
const ACCOUNT = 'account'; const ACCOUNT = 'account';
const SERVICE = 'service'; const SERVICE = 'service';
const STEAM = 'Steam'; const STEAM = 'Steam';
const SECRET = 'A4GRFHVVRBGY7UIW'; const SECRET = 'A4GRFHVVRBGY7UIW';
const STEAM_SECRET = 'XJGTDRUUKZH3X7TQN2QZUGCGXZCC5LXE'; const STEAM_SECRET = 'XJGTDRUUKZH3X7TQN2QZUGCGXZCC5LXE';
const ALGORITHM_DEFAULT = 'sha1'; const ALGORITHM_DEFAULT = 'sha1';
const ALGORITHM_CUSTOM = 'sha256'; const ALGORITHM_CUSTOM = 'sha256';
const DIGITS_DEFAULT = 6; const DIGITS_DEFAULT = 6;
const DIGITS_CUSTOM = 7; const DIGITS_CUSTOM = 7;
const DIGITS_STEAM = 5; const DIGITS_STEAM = 5;
const PERIOD_DEFAULT = 30; const PERIOD_DEFAULT = 30;
const PERIOD_CUSTOM = 40; const PERIOD_CUSTOM = 40;
const COUNTER_DEFAULT = 0; const COUNTER_DEFAULT = 0;
const COUNTER_CUSTOM = 5; const COUNTER_CUSTOM = 5;
const IMAGE = 'https%3A%2F%2Fen.opensuse.org%2Fimages%2F4%2F44%2FButton-filled-colour.png'; const IMAGE = 'https%3A%2F%2Fen.opensuse.org%2Fimages%2F4%2F44%2FButton-filled-colour.png';
const ICON = 'test.png'; const ICON = 'test.png';
const TOTP_FULL_CUSTOM_URI = 'otpauth://totp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&period=' . self::PERIOD_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM . '&image=' . self::IMAGE; const TOTP_FULL_CUSTOM_URI = 'otpauth://totp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&period=' . self::PERIOD_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM . '&image=' . self::IMAGE;
const HOTP_FULL_CUSTOM_URI = 'otpauth://hotp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&counter=' . self::COUNTER_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM . '&image=' . self::IMAGE; const HOTP_FULL_CUSTOM_URI = 'otpauth://hotp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&counter=' . self::COUNTER_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM . '&image=' . self::IMAGE;
const TOTP_SHORT_URI = 'otpauth://totp/' . self::ACCOUNT . '?secret=' . self::SECRET; const TOTP_SHORT_URI = 'otpauth://totp/' . self::ACCOUNT . '?secret=' . self::SECRET;
const HOTP_SHORT_URI = 'otpauth://hotp/' . self::ACCOUNT . '?secret=' . self::SECRET; const HOTP_SHORT_URI = 'otpauth://hotp/' . self::ACCOUNT . '?secret=' . self::SECRET;
const TOTP_URI_WITH_UNREACHABLE_IMAGE = 'otpauth://totp/service:account?secret=A4GRFHVVRBGY7UIW&image=https%3A%2F%2Fen.opensuse.org%2Fimage.png'; const TOTP_URI_WITH_UNREACHABLE_IMAGE = 'otpauth://totp/service:account?secret=A4GRFHVVRBGY7UIW&image=https%3A%2F%2Fen.opensuse.org%2Fimage.png';
const INVALID_OTPAUTH_URI = 'otpauth://Xotp/' . self::ACCOUNT . '?secret=' . self::SECRET; const INVALID_OTPAUTH_URI = 'otpauth://Xotp/' . self::ACCOUNT . '?secret=' . self::SECRET;
const STEAM_TOTP_URI = 'otpauth://totp/' . self::STEAM . ':' . self::ACCOUNT . '?secret=' . self::STEAM_SECRET . '&issuer=' . self::STEAM . '&digits=' . self::DIGITS_STEAM . '&period=30&algorithm=' . self::ALGORITHM_DEFAULT; const STEAM_TOTP_URI = 'otpauth://totp/' . self::STEAM . ':' . self::ACCOUNT . '?secret=' . self::STEAM_SECRET . '&issuer=' . self::STEAM . '&digits=' . self::DIGITS_STEAM . '&period=30&algorithm=' . self::ALGORITHM_DEFAULT;
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP = [ const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP = [
@ -39,21 +61,25 @@ class OtpTestData
'period' => self::PERIOD_CUSTOM, 'period' => self::PERIOD_CUSTOM,
'counter' => null, 'counter' => null,
]; ];
const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP = [ const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP = [
'account' => self::ACCOUNT, 'account' => self::ACCOUNT,
'otp_type' => 'totp', 'otp_type' => 'totp',
'secret' => self::SECRET, 'secret' => self::SECRET,
]; ];
const ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE = [ const ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE = [
'account' => self::ACCOUNT, 'account' => self::ACCOUNT,
'otp_type' => 'Xotp', 'otp_type' => 'Xotp',
'secret' => self::SECRET, 'secret' => self::SECRET,
]; ];
const ARRAY_OF_INVALID_PARAMETERS_FOR_TOTP = [ const ARRAY_OF_INVALID_PARAMETERS_FOR_TOTP = [
'account' => self::ACCOUNT, 'account' => self::ACCOUNT,
'otp_type' => 'totp', 'otp_type' => 'totp',
'secret' => 0, 'secret' => 0,
]; ];
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP = [ const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP = [
'service' => self::SERVICE, 'service' => self::SERVICE,
'account' => self::ACCOUNT, 'account' => self::ACCOUNT,
@ -65,11 +91,13 @@ class OtpTestData
'period' => null, 'period' => null,
'counter' => self::COUNTER_CUSTOM, 'counter' => self::COUNTER_CUSTOM,
]; ];
const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP = [ const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP = [
'account' => self::ACCOUNT, 'account' => self::ACCOUNT,
'otp_type' => 'hotp', 'otp_type' => 'hotp',
'secret' => self::SECRET, 'secret' => self::SECRET,
]; ];
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_STEAM_TOTP = [ const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_STEAM_TOTP = [
'service' => self::STEAM, 'service' => self::STEAM,
'account' => self::ACCOUNT, 'account' => self::ACCOUNT,
@ -82,7 +110,9 @@ class OtpTestData
]; ];
const GOOGLE_AUTH_MIGRATION_URI = 'otpauth-migration://offline?data=CiQKCgcNEp61iE2P0RYSB2FjY291bnQaB3NlcnZpY2UgASgBMAIKLAoKBw0SnrWITY/RFhILYWNjb3VudF9iaXMaC3NlcnZpY2VfYmlzIAEoATACEAEYASAA'; const GOOGLE_AUTH_MIGRATION_URI = 'otpauth-migration://offline?data=CiQKCgcNEp61iE2P0RYSB2FjY291bnQaB3NlcnZpY2UgASgBMAIKLAoKBw0SnrWITY/RFhILYWNjb3VudF9iaXMaC3NlcnZpY2VfYmlzIAEoATACEAEYASAA';
const INVALID_GOOGLE_AUTH_MIGRATION_URI = 'otpauthmigration://offline?data=CiQKCgcNEp61iE2P0RYSB2FjY291bnQaB3NlcnZpY2UgASgBMAIKLAoKBw0SnrWITY/RFhILYWNjb3VudF9iaXMaC3NlcnZpY2VfYmlzIAEoATACEAEYASAA'; const INVALID_GOOGLE_AUTH_MIGRATION_URI = 'otpauthmigration://offline?data=CiQKCgcNEp61iE2P0RYSB2FjY291bnQaB3NlcnZpY2UgASgBMAIKLAoKBw0SnrWITY/RFhILYWNjb3VudF9iaXMaC3NlcnZpY2VfYmlzIAEoATACEAEYASAA';
const GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA = 'otpauth-migration://offline?data=CiQKCgcNEp61iE2P0RYSB2FjY291bnQaB3NlcnZpY'; const GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA = 'otpauth-migration://offline?data=CiQKCgcNEp61iE2P0RYSB2FjY291bnQaB3NlcnZpY';
const AEGIS_JSON_MIGRATION_PAYLOAD = ' const AEGIS_JSON_MIGRATION_PAYLOAD = '
@ -224,5 +254,4 @@ class OtpTestData
}, },
"db": "1rX0ajzsxNbhN2hvnNCMBNooLlzqwz\/LMT3bNEIJjPH+zIvIbA6GVVPHLpna+yvjxLPKVkt1OQig==" "db": "1rX0ajzsxNbhN2hvnNCMBNooLlzqwz\/LMT3bNEIJjPH+zIvIbA6GVVPHLpna+yvjxLPKVkt1OQig=="
}'; }';
} }

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