mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-03-26 22:16:06 +01:00
Update tests & minor fixes
This commit is contained in:
parent
7ce7067380
commit
05a39b6501
app
Api/v1/Requests
TwoFAccountDynamicRequest.phpTwoFAccountStoreRequest.phpTwoFAccountUpdateRequest.phpTwoFAccountUriRequest.php
Extensions
Helpers
Http
Models
Providers
Services
config
tests
Api/v1
Controllers
Requests
GroupAssignRequestTest.phpGroupStoreRequestTest.phpQrCodeDecodeRequestTest.phpSettingStoreRequestTest.phpSettingUpdateRequestTest.phpTwoFAccountBatchRequestTest.phpTwoFAccountDynamicRequestTest.phpTwoFAccountImportRequestTest.phpTwoFAccountReorderRequestTest.phpTwoFAccountStoreRequestTest.phpTwoFAccountUpdateRequestTest.phpTwoFAccountUriRequestTest.php
Classes
Data
Feature
Http
Auth
ForgotPasswordControllerTest.phpLoginTest.phpPasswordControllerTest.phpRegisterControllerTest.phpResetPasswordControllerTest.phpUserControllerTest.phpWebAuthnDeviceLostControllerTest.phpWebAuthnLoginControllerTest.phpWebAuthnManageControllerTest.phpWebAuthnRecoveryControllerTest.phpWebAuthnRegisterControllerTest.php
SystemControllerTest.phpModels
Services
Unit
@ -35,6 +35,8 @@ public function rules()
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
|
@ -40,6 +40,8 @@ public function rules()
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
|
@ -40,6 +40,8 @@ public function rules()
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
|
@ -33,6 +33,8 @@ public function rules()
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
|
@ -31,7 +31,7 @@ public function sendResetLink(array $credentials, Closure $callback = null) : st
|
||||
$token = $this->tokens->create($user);
|
||||
|
||||
if ($callback) {
|
||||
$callback($user, $token);
|
||||
$callback($user, $token); // @codeCoverageIgnore
|
||||
} else {
|
||||
$user->sendWebauthnRecoveryNotification($token);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ public static function getUniqueFilename(string $extension) : string
|
||||
*/
|
||||
public static function cleanVersionNumber(?string $release) : string|false
|
||||
{
|
||||
return preg_match('/([[0-9][0-9\.]*[0-9])/', $release, $version) ? $version[0] : false;
|
||||
// We use the regex for semver detection (see https://semver.org/)
|
||||
return preg_match('/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/', $release, $version) ? $version[0] : false;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
use Laragear\WebAuthn\Http\Requests\AssertedRequest;
|
||||
use Laragear\WebAuthn\Http\Requests\AssertionRequest;
|
||||
use Laragear\WebAuthn\WebAuthn;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class WebAuthnLoginController extends Controller
|
||||
{
|
||||
@ -33,7 +34,7 @@ class WebAuthnLoginController extends Controller
|
||||
*/
|
||||
public function options(AssertionRequest $request) : Responsable|JsonResponse
|
||||
{
|
||||
switch (env('WEBAUTHN_USER_VERIFICATION')) {
|
||||
switch (config('webauthn.user_verification')) {
|
||||
case WebAuthn::USER_VERIFICATION_DISCOURAGED:
|
||||
$request = $request->fastLogin(); // Makes the authenticator to only check for user presence on registration
|
||||
break;
|
||||
@ -69,7 +70,7 @@ public function login(AssertedRequest $request)
|
||||
|
||||
// Some authenticators do not send a userHandle so we hack the response to be compliant
|
||||
// with Larapass/webauthn-lib implementation that waits for a userHandle
|
||||
if (! $response['userHandle']) {
|
||||
if (!Arr::exists($response, 'userHandle') || blank($response['userHandle'])) {
|
||||
$response['userHandle'] = User::getFromCredentialId($request->id)?->userHandle();
|
||||
$request->merge(['response' => $response]);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class WebAuthnRegisterController extends Controller
|
||||
*/
|
||||
public function options(AttestationRequest $request) : Responsable
|
||||
{
|
||||
switch (env('WEBAUTHN_USER_VERIFICATION')) {
|
||||
switch (config('webauthn.user_verification')) {
|
||||
case WebAuthn::USER_VERIFICATION_DISCOURAGED:
|
||||
$request = $request->fastRegistration(); // Makes the authenticator to only check for user presence on registration
|
||||
break;
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
|
@ -60,8 +60,6 @@ class TwoFAccount extends Model implements Sortable
|
||||
|
||||
const FAKE_ID = -2;
|
||||
|
||||
private const IMAGELINK_STORAGE_PATH = 'imagesLink/';
|
||||
|
||||
/**
|
||||
* List of OTP types supported by 2FAuth
|
||||
*/
|
||||
@ -376,10 +374,6 @@ public function fillWithOtpParameters(array $parameters, bool $skipIconFetching
|
||||
$this->enforceAsSteam();
|
||||
}
|
||||
|
||||
if (! $this->icon && $skipIconFetching) {
|
||||
$this->icon = $this->getDefaultIcon();
|
||||
}
|
||||
|
||||
if (! $this->icon && Settings::get('getOfficialIcons') && ! $skipIconFetching) {
|
||||
$this->icon = $this->getDefaultIcon();
|
||||
}
|
||||
@ -441,6 +435,22 @@ public function fillWithURI(string $uri, bool $isSteamTotp = false, bool $skipIc
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare 2 TwoFAccounts
|
||||
*/
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
return $this->service === $other->service &&
|
||||
$this->account === $other->account &&
|
||||
$this->icon === $other->icon &&
|
||||
$this->otp_type === $other->otp_type &&
|
||||
$this->secret === $other->secret &&
|
||||
$this->digits === $other->digits &&
|
||||
$this->algorithm === $other->algorithm &&
|
||||
$this->period === $other->period &&
|
||||
$this->counter === $other->counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets model attributes to STEAM values
|
||||
*/
|
||||
@ -534,7 +544,6 @@ private function storeImageAsIcon(string $url)
|
||||
try {
|
||||
$path_parts = pathinfo($url);
|
||||
$newFilename = Helpers::getUniqueFilename($path_parts['extension']);
|
||||
$imageFile = self::IMAGELINK_STORAGE_PATH . $newFilename;
|
||||
|
||||
try {
|
||||
$response = Http::retry(3, 100)->get($url);
|
||||
@ -546,8 +555,10 @@ private function storeImageAsIcon(string $url)
|
||||
Log::error(sprintf('Cannot fetch imageLink at "%s"', $url));
|
||||
}
|
||||
|
||||
if (in_array(Storage::mimeType($imageFile), ['image/png', 'image/jpeg', 'image/webp', 'image/bmp'])
|
||||
&& getimagesize(storage_path() . '/app/' . $imageFile)) {
|
||||
if (
|
||||
in_array(Storage::disk('imagesLink')->mimeType($newFilename), ['image/png', 'image/jpeg', 'image/webp', 'image/bmp'])
|
||||
&& getimagesize(Storage::disk('imagesLink')->path($newFilename))
|
||||
) {
|
||||
// Should be a valid image, we move it to the icons disk
|
||||
if (Storage::disk('icons')->put($newFilename, Storage::disk('imagesLink')->get($newFilename))) {
|
||||
Storage::disk('imagesLink')->delete($newFilename);
|
||||
@ -555,10 +566,8 @@ private function storeImageAsIcon(string $url)
|
||||
|
||||
Log::info(sprintf('Icon file %s stored', $newFilename));
|
||||
} else {
|
||||
// @codeCoverageIgnoreStart
|
||||
Storage::disk('imagesLink')->delete($newFilename);
|
||||
throw new \Exception('Unsupported mimeType or missing image on storage');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return $newFilename;
|
||||
|
@ -49,6 +49,7 @@ public function boot()
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
|
@ -59,7 +59,7 @@ public function migrate(mixed $migrationPayload) : Collection
|
||||
|
||||
// The token failed to generate a valid account so we create a fake account to be returned.
|
||||
$fakeAccount = new TwoFAccount();
|
||||
$fakeAccount->id = -2;
|
||||
$fakeAccount->id = TwoFAccount::FAKE_ID;
|
||||
$fakeAccount->otp_type = $fakeAccount::TOTP;
|
||||
// Only basic fields are filled to limit the risk of another exception.
|
||||
$fakeAccount->account = $otp_parameters->getName() ?? __('twofaccounts.import.invalid_account');
|
||||
|
@ -39,7 +39,7 @@ public function migrate(mixed $migrationPayload) : Collection
|
||||
|
||||
// The token failed to generate a valid account so we create a fake account to be returned.
|
||||
$fakeAccount = new TwoFAccount();
|
||||
$fakeAccount->id = -2;
|
||||
$fakeAccount->id = TwoFAccount::FAKE_ID;
|
||||
$fakeAccount->otp_type = substr($uri, 10, 4);
|
||||
// Only basic fields are filled to limit the risk of another exception.
|
||||
$fakeAccount->account = __('twofaccounts.import.invalid_account');
|
||||
|
@ -89,8 +89,12 @@ public function migrate(mixed $migrationPayload) : Collection
|
||||
$parameters['secret'] = $this->padToValidBase32Secret($otp_parameters['secret']);
|
||||
$parameters['algorithm'] = $otp_parameters['otp']['algorithm'];
|
||||
$parameters['digits'] = $otp_parameters['otp']['digits'];
|
||||
$parameters['counter'] = $otp_parameters['otp']['counter'] ?? null;
|
||||
$parameters['period'] = $otp_parameters['otp']['period'] ?? null;
|
||||
$parameters['counter'] = strtolower($parameters['otp_type']) === 'hotp' && $otp_parameters['otp']['counter'] > 0
|
||||
? $otp_parameters['otp']['counter']
|
||||
: null;
|
||||
$parameters['period'] = strtolower($parameters['otp_type']) === 'totp' && $otp_parameters['otp']['period'] > 0
|
||||
? $otp_parameters['otp']['period']
|
||||
: null;
|
||||
|
||||
try {
|
||||
$twofaccounts[$key] = new TwoFAccount;
|
||||
|
@ -16,7 +16,7 @@ class ReleaseRadarService
|
||||
*/
|
||||
public function scheduledScan() : void
|
||||
{
|
||||
if ((Settings::get('lastRadarScan') + 604800) < time()) {
|
||||
if ((Settings::get('lastRadarScan') + (60 * 60 * 24 * 7)) < time()) {
|
||||
$this->newRelease();
|
||||
}
|
||||
}
|
||||
@ -39,18 +39,19 @@ public function manualScan() : false|string
|
||||
protected function newRelease() : false|string
|
||||
{
|
||||
if ($latestReleaseData = json_decode($this->getLatestReleaseData())) {
|
||||
|
||||
$githubVersion = Helpers::cleanVersionNumber($latestReleaseData->tag_name);
|
||||
$installedVersion = Helpers::cleanVersionNumber(config('2fauth.version'));
|
||||
|
||||
if ($githubVersion > $installedVersion && $latestReleaseData->prerelease == false && $latestReleaseData->draft == false) {
|
||||
Settings::set('latestRelease', $latestReleaseData->tag_name);
|
||||
if ($githubVersion && $installedVersion) {
|
||||
if ($githubVersion > $installedVersion && $latestReleaseData->prerelease == false && $latestReleaseData->draft == false) {
|
||||
Settings::set('latestRelease', $latestReleaseData->tag_name);
|
||||
|
||||
return $latestReleaseData->tag_name;
|
||||
} else {
|
||||
Settings::delete('latestRelease');
|
||||
return $latestReleaseData->tag_name;
|
||||
} else {
|
||||
Settings::delete('latestRelease');
|
||||
}
|
||||
}
|
||||
|
||||
Settings::set('lastRadarScan', time());
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -68,6 +69,8 @@ protected function getLatestReleaseData() : string|null
|
||||
->get(config('2fauth.latestReleaseUrl'));
|
||||
|
||||
if ($response->successful()) {
|
||||
Settings::set('lastRadarScan', time());
|
||||
|
||||
return $response->body();
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
return [
|
||||
|
||||
'user_verification' => env('WEBAUTHN_USER_VERIFICATION', 'discouraged'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Relaying Party
|
||||
|
@ -5,6 +5,10 @@
|
||||
use App\Models\User;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Controllers\UserController
|
||||
* @covers \App\Api\v1\Resources\UserResource
|
||||
*/
|
||||
class UserControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -15,7 +19,7 @@ class UserControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
@ -21,7 +21,7 @@ class GroupControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
@ -40,6 +40,31 @@ public function test_upload_with_invalid_data_returns_validation_error()
|
||||
->assertStatus(422);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_fetch_logo_returns_filename()
|
||||
{
|
||||
$response = $this->json('POST', '/api/v1/icons/default', [
|
||||
'service' => 'twitter',
|
||||
])
|
||||
->assertStatus(201)
|
||||
->assertJsonStructure([
|
||||
'filename',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_fetch_unknown_logo_returns_nothing()
|
||||
{
|
||||
$response = $this->json('POST', '/api/v1/icons/default', [
|
||||
'service' => 'unknown_company',
|
||||
])
|
||||
->assertNoContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
|
@ -9,13 +9,16 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\Classes\LocalFile;
|
||||
use Tests\Classes\OtpTestData;
|
||||
use Tests\Data\OtpTestData;
|
||||
use Tests\FeatureTestCase;
|
||||
use Tests\Data\MigrationTestData;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Controllers\TwoFAccountController
|
||||
* @covers \App\Api\v1\Resources\TwoFAccountReadResource
|
||||
* @covers \App\Api\v1\Resources\TwoFAccountStoreResource
|
||||
* @covers \App\Providers\MigrationServiceProvider
|
||||
* @covers \App\Providers\TwoFAuthServiceProvider
|
||||
*/
|
||||
class TwoFAccountControllerTest extends FeatureTestCase
|
||||
{
|
||||
@ -122,7 +125,7 @@ class TwoFAccountControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -447,7 +450,7 @@ public function test_import_valid_gauth_payload_returns_success_with_consistent_
|
||||
{
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('POST', '/api/v1/twofaccounts/migration', [
|
||||
'payload' => OtpTestData::GOOGLE_AUTH_MIGRATION_URI,
|
||||
'payload' => MigrationTestData::GOOGLE_AUTH_MIGRATION_URI,
|
||||
'withSecret' => 1,
|
||||
])
|
||||
->assertOk()
|
||||
@ -483,7 +486,7 @@ public function test_import_with_invalid_gauth_payload_returns_validation_error(
|
||||
{
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('POST', '/api/v1/twofaccounts/migration', [
|
||||
'uri' => OtpTestData::INVALID_GOOGLE_AUTH_MIGRATION_URI,
|
||||
'uri' => MigrationTestData::INVALID_GOOGLE_AUTH_MIGRATION_URI,
|
||||
])
|
||||
->assertStatus(422);
|
||||
}
|
||||
@ -507,7 +510,7 @@ public function test_import_gauth_payload_with_duplicates_returns_negative_ids()
|
||||
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('POST', '/api/v1/twofaccounts/migration', [
|
||||
'payload' => OtpTestData::GOOGLE_AUTH_MIGRATION_URI,
|
||||
'payload' => MigrationTestData::GOOGLE_AUTH_MIGRATION_URI,
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonFragment([
|
||||
@ -524,7 +527,7 @@ public function test_import_invalid_gauth_payload_returns_bad_request()
|
||||
{
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('POST', '/api/v1/twofaccounts/migration', [
|
||||
'payload' => OtpTestData::GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA,
|
||||
'payload' => MigrationTestData::GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA,
|
||||
])
|
||||
->assertStatus(400)
|
||||
->assertJsonStructure([
|
||||
@ -546,22 +549,11 @@ public function test_import_valid_aegis_json_file_returns_success()
|
||||
'withSecret' => 1,
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonCount(5, $key = null)
|
||||
->assertJsonCount(3, $key = null)
|
||||
->assertJsonFragment([
|
||||
'id' => 0,
|
||||
'service' => OtpTestData::SERVICE . '_totp',
|
||||
'account' => OtpTestData::ACCOUNT . '_totp',
|
||||
'otp_type' => 'totp',
|
||||
'secret' => OtpTestData::SECRET,
|
||||
'digits' => OtpTestData::DIGITS_DEFAULT,
|
||||
'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
|
||||
'period' => OtpTestData::PERIOD_DEFAULT,
|
||||
'counter' => null,
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'id' => 0,
|
||||
'service' => OtpTestData::SERVICE . '_totp_custom',
|
||||
'account' => OtpTestData::ACCOUNT . '_totp_custom',
|
||||
'service' => OtpTestData::SERVICE,
|
||||
'account' => OtpTestData::ACCOUNT,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => OtpTestData::SECRET,
|
||||
'digits' => OtpTestData::DIGITS_CUSTOM,
|
||||
@ -571,21 +563,10 @@ public function test_import_valid_aegis_json_file_returns_success()
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'id' => 0,
|
||||
'service' => OtpTestData::SERVICE . '_hotp',
|
||||
'account' => OtpTestData::ACCOUNT . '_hotp',
|
||||
'service' => OtpTestData::SERVICE,
|
||||
'account' => OtpTestData::ACCOUNT,
|
||||
'otp_type' => 'hotp',
|
||||
'secret' => OtpTestData::SECRET,
|
||||
'digits' => OtpTestData::DIGITS_DEFAULT,
|
||||
'algorithm' => OtpTestData::ALGORITHM_DEFAULT,
|
||||
'period' => null,
|
||||
'counter' => OtpTestData::COUNTER_DEFAULT,
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'id' => 0,
|
||||
'service' => OtpTestData::SERVICE . '_hotp_custom',
|
||||
'account' => OtpTestData::ACCOUNT . '_hotp_custom',
|
||||
'otp_type' => 'totp',
|
||||
'secret' => OtpTestData::SECRET,
|
||||
'digits' => OtpTestData::DIGITS_CUSTOM,
|
||||
'algorithm' => OtpTestData::ALGORITHM_CUSTOM,
|
||||
'period' => null,
|
||||
@ -594,7 +575,7 @@ public function test_import_valid_aegis_json_file_returns_success()
|
||||
->assertJsonFragment([
|
||||
'id' => 0,
|
||||
'service' => OtpTestData::STEAM,
|
||||
'account' => OtpTestData::ACCOUNT . '_steam',
|
||||
'account' => OtpTestData::ACCOUNT,
|
||||
'otp_type' => 'steamtotp',
|
||||
'secret' => OtpTestData::STEAM_SECRET,
|
||||
'digits' => OtpTestData::DIGITS_STEAM,
|
||||
@ -625,10 +606,10 @@ public function test_import_invalid_aegis_json_file_returns_bad_request($file)
|
||||
public function invalidAegisJsonFileProvider()
|
||||
{
|
||||
return [
|
||||
'validPlainTextFile' => [
|
||||
'encryptedAegisJsonFile' => [
|
||||
LocalFile::fake()->encryptedAegisJsonFile(),
|
||||
],
|
||||
'validPlainTextFileWithNewLines' => [
|
||||
'invalidAegisJsonFile' => [
|
||||
LocalFile::fake()->invalidAegisJsonFile(),
|
||||
],
|
||||
];
|
||||
@ -720,16 +701,16 @@ public function test_import_invalid_plain_text_file_returns_bad_request($file)
|
||||
public function invalidPlainTextFileProvider()
|
||||
{
|
||||
return [
|
||||
'validPlainTextFile' => [
|
||||
'invalidPlainTextFileEmpty' => [
|
||||
LocalFile::fake()->invalidPlainTextFileEmpty(),
|
||||
],
|
||||
'validPlainTextFileWithNewLines' => [
|
||||
'invalidPlainTextFileNoUri' => [
|
||||
LocalFile::fake()->invalidPlainTextFileNoUri(),
|
||||
],
|
||||
'validPlainTextFileWithNewLines' => [
|
||||
'invalidPlainTextFileWithInvalidUri' => [
|
||||
LocalFile::fake()->invalidPlainTextFileWithInvalidUri(),
|
||||
],
|
||||
'validPlainTextFileWithNewLines' => [
|
||||
'invalidPlainTextFileWithInvalidLine' => [
|
||||
LocalFile::fake()->invalidPlainTextFileWithInvalidLine(),
|
||||
],
|
||||
];
|
||||
@ -744,7 +725,8 @@ public function test_reorder_returns_success()
|
||||
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('POST', '/api/v1/twofaccounts/reorder', [
|
||||
'orderedIds' => [3, 2, 1], ])
|
||||
'orderedIds' => [3, 2, 1],
|
||||
])
|
||||
->assertStatus(200)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
@ -760,7 +742,8 @@ public function test_reorder_with_invalid_data_returns_validation_error()
|
||||
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('POST', '/api/v1/twofaccounts/reorder', [
|
||||
'orderedIds' => '3,2,1', ])
|
||||
'orderedIds' => '3,2,1',
|
||||
])
|
||||
->assertStatus(422);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\GroupAssignRequest
|
||||
*/
|
||||
class GroupAssignRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +21,8 @@ class GroupAssignRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new GroupAssignRequest();
|
||||
|
||||
@ -29,7 +32,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new GroupAssignRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +43,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -54,7 +57,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new GroupAssignRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -65,7 +68,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -9,6 +9,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\GroupStoreRequest
|
||||
*/
|
||||
class GroupStoreRequestTest extends FeatureTestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -21,8 +24,8 @@ class GroupStoreRequestTest extends FeatureTestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new GroupStoreRequest();
|
||||
|
||||
@ -32,7 +35,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new GroupStoreRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -43,7 +46,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -55,7 +58,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$group = new Group([
|
||||
'name' => $this->uniqueGroupName,
|
||||
@ -72,7 +75,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -9,6 +9,9 @@
|
||||
use Tests\Classes\LocalFile;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\QrCodeDecodeRequest
|
||||
*/
|
||||
class QrCodeDecodeRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -19,8 +22,8 @@ class QrCodeDecodeRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new QrCodeDecodeRequest();
|
||||
|
||||
@ -30,7 +33,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new QrCodeDecodeRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -41,7 +44,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
$file = LocalFile::fake()->validQrcode();
|
||||
|
||||
@ -55,7 +58,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new QrCodeDecodeRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -66,7 +69,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -9,6 +9,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\SettingStoreRequest
|
||||
*/
|
||||
class SettingStoreRequestTest extends FeatureTestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -21,8 +24,8 @@ class SettingStoreRequestTest extends FeatureTestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new SettingStoreRequest();
|
||||
|
||||
@ -32,7 +35,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new SettingStoreRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -43,7 +46,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -64,7 +67,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
Settings::set($this->uniqueKey, 'uniqueValue');
|
||||
|
||||
@ -77,7 +80,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -8,6 +8,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\SettingUpdateRequest
|
||||
*/
|
||||
class SettingUpdateRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +21,8 @@ class SettingUpdateRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new SettingUpdateRequest();
|
||||
|
||||
@ -29,7 +32,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new SettingUpdateRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +43,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -58,7 +61,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new SettingUpdateRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -69,7 +72,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -8,6 +8,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountBatchRequest
|
||||
*/
|
||||
class TwoFAccountBatchRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +21,8 @@ class TwoFAccountBatchRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountBatchRequest();
|
||||
|
||||
@ -29,7 +32,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountBatchRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +43,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -55,7 +58,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountBatchRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -66,7 +69,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -9,6 +9,9 @@
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountDynamicRequest
|
||||
*/
|
||||
class TwoFAccountDynamicRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -19,8 +22,8 @@ class TwoFAccountDynamicRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountDynamicRequest();
|
||||
|
||||
|
@ -8,6 +8,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountImportRequest
|
||||
*/
|
||||
class TwoFAccountImportRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +21,8 @@ class TwoFAccountImportRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountImportRequest();
|
||||
|
||||
@ -29,7 +32,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountImportRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +43,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -52,7 +55,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountImportRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -63,7 +66,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -8,6 +8,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountReorderRequest
|
||||
*/
|
||||
class TwoFAccountReorderRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +21,8 @@ class TwoFAccountReorderRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountReorderRequest();
|
||||
|
||||
@ -29,7 +32,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountReorderRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +43,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -55,7 +58,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountReorderRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -66,7 +69,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -8,6 +8,10 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountStoreRequest
|
||||
* @covers \App\Rules\IsBase32Encoded
|
||||
*/
|
||||
class TwoFAccountStoreRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +22,8 @@ class TwoFAccountStoreRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountStoreRequest();
|
||||
|
||||
@ -29,7 +33,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountStoreRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +44,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -98,7 +102,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountStoreRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -109,7 +113,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -8,6 +8,10 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountUpdateRequest
|
||||
* @covers \App\Rules\IsBase32Encoded
|
||||
*/
|
||||
class TwoFAccountUpdateRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +22,8 @@ class TwoFAccountUpdateRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountUpdateRequest();
|
||||
|
||||
@ -29,7 +33,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountUpdateRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +44,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -80,7 +84,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountUpdateRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -91,7 +95,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -8,6 +8,9 @@
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Api\v1\Requests\TwoFAccountUriRequest
|
||||
*/
|
||||
class TwoFAccountUriRequestTest extends TestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
@ -18,8 +21,8 @@ class TwoFAccountUriRequestTest extends TestCase
|
||||
public function test_user_is_authorized()
|
||||
{
|
||||
Auth::shouldReceive('check')
|
||||
->once()
|
||||
->andReturn(true);
|
||||
->once()
|
||||
->andReturn(true);
|
||||
|
||||
$request = new TwoFAccountUriRequest();
|
||||
|
||||
@ -29,7 +32,7 @@ public function test_user_is_authorized()
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
*/
|
||||
public function test_valid_data(array $data) : void
|
||||
public function test_valid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountUriRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -40,7 +43,7 @@ public function test_valid_data(array $data) : void
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideValidData() : array
|
||||
public function provideValidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -59,7 +62,7 @@ public function provideValidData() : array
|
||||
/**
|
||||
* @dataProvider provideInvalidData
|
||||
*/
|
||||
public function test_invalid_data(array $data) : void
|
||||
public function test_invalid_data(array $data): void
|
||||
{
|
||||
$request = new TwoFAccountUriRequest();
|
||||
$validator = Validator::make($data, $request->rules());
|
||||
@ -70,7 +73,7 @@ public function test_invalid_data(array $data) : void
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideInvalidData() : array
|
||||
public function provideInvalidData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
|
@ -3,6 +3,8 @@
|
||||
namespace Tests\Classes;
|
||||
|
||||
use Illuminate\Http\Testing\File;
|
||||
use Tests\Data\MigrationTestData;
|
||||
use Tests\Data\OtpTestData;
|
||||
|
||||
class LocalFileFactory
|
||||
{
|
||||
@ -58,7 +60,7 @@ public function validAegisJsonFile()
|
||||
return new File('validAegisMigration.json', tap(tmpfile(), function ($temp) {
|
||||
ob_start();
|
||||
|
||||
echo OtpTestData::AEGIS_JSON_MIGRATION_PAYLOAD;
|
||||
echo MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD;
|
||||
|
||||
fwrite($temp, ob_get_clean());
|
||||
}));
|
||||
@ -74,7 +76,7 @@ public function invalidAegisJsonFile()
|
||||
return new File('invalidAegisMigration.json', tap(tmpfile(), function ($temp) {
|
||||
ob_start();
|
||||
|
||||
echo OtpTestData::INVALID_AEGIS_JSON_MIGRATION_PAYLOAD;
|
||||
echo MigrationTestData::INVALID_AEGIS_JSON_MIGRATION_PAYLOAD;
|
||||
|
||||
fwrite($temp, ob_get_clean());
|
||||
}));
|
||||
@ -90,7 +92,7 @@ public function encryptedAegisJsonFile()
|
||||
return new File('encryptedAegisJsonFile.txt', tap(tmpfile(), function ($temp) {
|
||||
ob_start();
|
||||
|
||||
echo OtpTestData::ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD;
|
||||
echo MigrationTestData::ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD;
|
||||
|
||||
fwrite($temp, ob_get_clean());
|
||||
}));
|
||||
|
@ -1,257 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Classes;
|
||||
|
||||
class OtpTestData
|
||||
{
|
||||
const ACCOUNT = 'account';
|
||||
|
||||
const SERVICE = 'service';
|
||||
|
||||
const STEAM = 'Steam';
|
||||
|
||||
const SECRET = 'A4GRFHVVRBGY7UIW';
|
||||
|
||||
const STEAM_SECRET = 'XJGTDRUUKZH3X7TQN2QZUGCGXZCC5LXE';
|
||||
|
||||
const ALGORITHM_DEFAULT = 'sha1';
|
||||
|
||||
const ALGORITHM_CUSTOM = 'sha256';
|
||||
|
||||
const DIGITS_DEFAULT = 6;
|
||||
|
||||
const DIGITS_CUSTOM = 7;
|
||||
|
||||
const DIGITS_STEAM = 5;
|
||||
|
||||
const PERIOD_DEFAULT = 30;
|
||||
|
||||
const PERIOD_CUSTOM = 40;
|
||||
|
||||
const COUNTER_DEFAULT = 0;
|
||||
|
||||
const COUNTER_CUSTOM = 5;
|
||||
|
||||
const IMAGE = 'https%3A%2F%2Fen.opensuse.org%2Fimages%2F4%2F44%2FButton-filled-colour.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 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 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 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 ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP = [
|
||||
'service' => self::SERVICE,
|
||||
'account' => self::ACCOUNT,
|
||||
'icon' => self::ICON,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => self::SECRET,
|
||||
'digits' => self::DIGITS_CUSTOM,
|
||||
'algorithm' => self::ALGORITHM_CUSTOM,
|
||||
'period' => self::PERIOD_CUSTOM,
|
||||
'counter' => null,
|
||||
];
|
||||
|
||||
const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => self::SECRET,
|
||||
];
|
||||
|
||||
const ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'Xotp',
|
||||
'secret' => self::SECRET,
|
||||
];
|
||||
|
||||
const ARRAY_OF_INVALID_PARAMETERS_FOR_TOTP = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => 0,
|
||||
];
|
||||
|
||||
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP = [
|
||||
'service' => self::SERVICE,
|
||||
'account' => self::ACCOUNT,
|
||||
'icon' => self::ICON,
|
||||
'otp_type' => 'hotp',
|
||||
'secret' => self::SECRET,
|
||||
'digits' => self::DIGITS_CUSTOM,
|
||||
'algorithm' => self::ALGORITHM_CUSTOM,
|
||||
'period' => null,
|
||||
'counter' => self::COUNTER_CUSTOM,
|
||||
];
|
||||
|
||||
const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'hotp',
|
||||
'secret' => self::SECRET,
|
||||
];
|
||||
|
||||
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_STEAM_TOTP = [
|
||||
'service' => self::STEAM,
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'steamtotp',
|
||||
'secret' => self::STEAM_SECRET,
|
||||
'digits' => self::DIGITS_STEAM,
|
||||
'algorithm' => self::ALGORITHM_DEFAULT,
|
||||
'period' => self::PERIOD_DEFAULT,
|
||||
'counter' => null,
|
||||
];
|
||||
|
||||
const GOOGLE_AUTH_MIGRATION_URI = 'otpauth-migration://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 AEGIS_JSON_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "5be1b189-260d-4fe1-930a-a78cb669dd86",
|
||||
"name": "' . self::ACCOUNT . '_totp",
|
||||
"issuer": "' . self::SERVICE . '_totp",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . self::SECRET . '",
|
||||
"algo": "' . self::ALGORITHM_DEFAULT . '",
|
||||
"digits": ' . self::DIGITS_DEFAULT . ',
|
||||
"period": ' . self::PERIOD_DEFAULT . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . self::ACCOUNT . '_totp_custom",
|
||||
"issuer": "' . self::SERVICE . '_totp_custom",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . self::SECRET . '",
|
||||
"algo": "' . self::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . self::DIGITS_CUSTOM . ',
|
||||
"period": ' . self::PERIOD_CUSTOM . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "hotp",
|
||||
"uuid": "90a2af2e-2857-4515-bb18-52c4fa823f6f",
|
||||
"name": "' . self::ACCOUNT . '_hotp",
|
||||
"issuer": "' . self::SERVICE . '_hotp",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . self::SECRET . '",
|
||||
"algo": "' . self::ALGORITHM_DEFAULT . '",
|
||||
"digits": ' . self::DIGITS_DEFAULT . ',
|
||||
"counter": ' . self::COUNTER_DEFAULT . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "hotp",
|
||||
"uuid": "e1b3f683-d5fe-4126-b616-8c8abd8ad97c",
|
||||
"name": "' . self::ACCOUNT . '_hotp_custom",
|
||||
"issuer": "' . self::SERVICE . '_hotp_custom",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . self::SECRET . '",
|
||||
"algo": "' . self::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . self::DIGITS_CUSTOM . ',
|
||||
"counter": ' . self::COUNTER_CUSTOM . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "steamtotp",
|
||||
"uuid": "9fb06143-421d-46e1-a7e9-4aafe44b0e72",
|
||||
"name": "' . self::ACCOUNT . '_steam",
|
||||
"issuer": "' . self::STEAM . '",
|
||||
"note": "",
|
||||
"icon": "null",
|
||||
"info": {
|
||||
"secret": "' . self::STEAM_SECRET . '",
|
||||
"algo": "' . self::ALGORITHM_DEFAULT . '",
|
||||
"digits": ' . self::DIGITS_STEAM . ',
|
||||
"period": ' . self::PERIOD_DEFAULT . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const INVALID_AEGIS_JSON_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"thisIsNotTheCorrectKeyName": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "5be1b189-260d-4fe1-930a-a78cb669dd86",
|
||||
"name": "' . self::ACCOUNT . '",
|
||||
"issuer": "' . self::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . self::SECRET . '",
|
||||
"algo": "' . self::ALGORITHM_DEFAULT . '",
|
||||
"digits": ' . self::DIGITS_DEFAULT . ',
|
||||
"period": ' . self::PERIOD_DEFAULT . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": [
|
||||
{
|
||||
"type": 1,
|
||||
"uuid": "1f447956-c71c-4be4-8192-97197dc67df7",
|
||||
"key": "d742967686cae462c5732023a72d59245d8q7c5c93a66aeb2q2a350bb8b6a7ae",
|
||||
"key_params": {
|
||||
"nonce": "77a8ff6d84265efd2a3ed9b7",
|
||||
"tag": "cc13fb4a5baz3fd27bc97f5eacaa00d0"
|
||||
},
|
||||
"n": 32768,
|
||||
"r": 8,
|
||||
"p": 1,
|
||||
"salt": "1c245b2696b948dt040c30c538aeb6f9620b054d9ff182f33dd4b285b67bed51",
|
||||
"repaired": true
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"nonce": "f31675d9966d2z588bd07788",
|
||||
"tag": "ad5729fa135dc6d6sw87e0c955932661"
|
||||
}
|
||||
},
|
||||
"db": "1rX0ajzsxNbhN2hvnNCMBNooLlzqwz\/LMT3bNEIJjPH+zIvIbA6GVVPHLpna+yvjxLPKVkt1OQig=="
|
||||
}';
|
||||
}
|
161
tests/Data/HttpRequestTestData.php
Normal file
161
tests/Data/HttpRequestTestData.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Data;
|
||||
|
||||
class HttpRequestTestData
|
||||
{
|
||||
const TAG_NAME = 'v3.4.1';
|
||||
|
||||
const NEW_TAG_NAME = 'v3.4.2';
|
||||
|
||||
const SVG_LOGO_BODY = '<svg xmlns="http://www.w3.org/2000/svg" class="r-k200y r-13gxpu9 r-4qtqp9 r-yyyyoo r-np7d94 r-dnmrzs r-bnwqim r-1plcrui r-lrvibr" width="22.706" height="22.706"><path d="M22.706 4.311c-.835.37-1.732.62-2.675.733a4.67 4.67 0 002.048-2.578 9.3 9.3 0 01-2.958 1.13 4.66 4.66 0 00-7.938 4.25 13.229 13.229 0 01-9.602-4.868c-.4.69-.63 1.49-.63 2.342a4.66 4.66 0 002.072 3.878 4.647 4.647 0 01-2.11-.583v.06a4.66 4.66 0 003.737 4.568 4.692 4.692 0 01-2.104.08 4.661 4.661 0 004.352 3.234 9.348 9.348 0 01-5.786 1.995A9.5 9.5 0 010 18.487a13.175 13.175 0 007.14 2.093c8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602a9.47 9.47 0 002.323-2.41z" fill="#1da1f2"/></svg>';
|
||||
|
||||
const TFA_JSON_BODY = '
|
||||
[
|
||||
[
|
||||
"Twitch",
|
||||
{
|
||||
"domain": "twitch.tv",
|
||||
"url": "https://www.twitch.tv/",
|
||||
"tfa":
|
||||
[
|
||||
"sms",
|
||||
"custom-software",
|
||||
"totp"
|
||||
],
|
||||
"custom-software":
|
||||
[
|
||||
"Authy"
|
||||
],
|
||||
"documentation": "https://help.twitch.tv/s/article/two-factor-authentication",
|
||||
"notes": "To activate two factor authentication, you must provide a mobile phone number.",
|
||||
"keywords":
|
||||
[
|
||||
"entertainment"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"Twitter",
|
||||
{
|
||||
"domain": "twitter.com",
|
||||
"tfa":
|
||||
[
|
||||
"sms",
|
||||
"totp",
|
||||
"u2f"
|
||||
],
|
||||
"documentation": "https://help.twitter.com/en/managing-your-account/two-factor-authentication",
|
||||
"recovery": "https://help.twitter.com/en/managing-your-account/issues-with-login-authentication",
|
||||
"notes": "SMS only available on select providers.",
|
||||
"keywords":
|
||||
[
|
||||
"social"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"Txbit",
|
||||
{
|
||||
"domain": "txbit.io",
|
||||
"tfa":
|
||||
[
|
||||
"totp"
|
||||
],
|
||||
"documentation": "https://support.txbit.io/support/solutions/articles/44000447137",
|
||||
"keywords":
|
||||
[
|
||||
"cryptocurrencies"
|
||||
]
|
||||
}
|
||||
]
|
||||
]';
|
||||
|
||||
const LATEST_RELEASE_BODY_NO_NEW_RELEASE = '
|
||||
{
|
||||
"url": "https://api.github.com/repos/Bubka/2FAuth/releases/84186611",
|
||||
"assets_url": "https://api.github.com/repos/Bubka/2FAuth/releases/84186611/assets",
|
||||
"upload_url": "https://uploads.github.com/repos/Bubka/2FAuth/releases/84186611/assets{?name,label}",
|
||||
"html_url": "https://github.com/Bubka/2FAuth/releases/tag/' . self::TAG_NAME . '",
|
||||
"id": 84186611,
|
||||
"author": {
|
||||
"login": "Bubka",
|
||||
"id": 858858,
|
||||
"node_id": "MDQ6VXNlcjg1ODg1OA==",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/858858?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/Bubka",
|
||||
"html_url": "https://github.com/Bubka",
|
||||
"followers_url": "https://api.github.com/users/Bubka/followers",
|
||||
"following_url": "https://api.github.com/users/Bubka/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/Bubka/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/Bubka/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/Bubka/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/Bubka/orgs",
|
||||
"repos_url": "https://api.github.com/users/Bubka/repos",
|
||||
"events_url": "https://api.github.com/users/Bubka/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/Bubka/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"node_id": "RE_kwDOCyNVx84FBJXz",
|
||||
"tag_name": "' . self::TAG_NAME . '",
|
||||
"target_commitish": "master",
|
||||
"name": "' . self::TAG_NAME . '",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"created_at": "2022-11-25T13:31:45Z",
|
||||
"published_at": "2022-11-25T13:44:10Z",
|
||||
"assets": [
|
||||
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/Bubka/2FAuth/tarball/' . self::TAG_NAME . '",
|
||||
"zipball_url": "https://api.github.com/repos/Bubka/2FAuth/zipball/' . self::TAG_NAME . '",
|
||||
"body": "### Fixed\r\n\r\n- [issue #140](https://github.com/Bubka/2FAuth/issues/140) Bad regex for Period field (advanced form)\r\n- [issue #141](https://github.com/Bubka/2FAuth/issues/141) Digits field is missing in advanced form"
|
||||
}';
|
||||
|
||||
const LATEST_RELEASE_BODY_NEW_RELEASE = '
|
||||
{
|
||||
"url": "https://api.github.com/repos/Bubka/2FAuth/releases/84186611",
|
||||
"assets_url": "https://api.github.com/repos/Bubka/2FAuth/releases/84186611/assets",
|
||||
"upload_url": "https://uploads.github.com/repos/Bubka/2FAuth/releases/84186611/assets{?name,label}",
|
||||
"html_url": "https://github.com/Bubka/2FAuth/releases/tag/' . self::NEW_TAG_NAME . '",
|
||||
"id": 84186611,
|
||||
"author": {
|
||||
"login": "Bubka",
|
||||
"id": 858858,
|
||||
"node_id": "MDQ6VXNlcjg1ODg1OA==",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/858858?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/Bubka",
|
||||
"html_url": "https://github.com/Bubka",
|
||||
"followers_url": "https://api.github.com/users/Bubka/followers",
|
||||
"following_url": "https://api.github.com/users/Bubka/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/Bubka/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/Bubka/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/Bubka/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/Bubka/orgs",
|
||||
"repos_url": "https://api.github.com/users/Bubka/repos",
|
||||
"events_url": "https://api.github.com/users/Bubka/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/Bubka/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"node_id": "RE_kwDOCyNVx84FBJXz",
|
||||
"tag_name": "' . self::NEW_TAG_NAME . '",
|
||||
"target_commitish": "master",
|
||||
"name": "' . self::NEW_TAG_NAME . '",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"created_at": "2022-12-25T13:31:45Z",
|
||||
"published_at": "2022-12-25T13:44:10Z",
|
||||
"assets": [
|
||||
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/Bubka/2FAuth/tarball/' . self::NEW_TAG_NAME . '",
|
||||
"zipball_url": "https://api.github.com/repos/Bubka/2FAuth/zipball/' . self::NEW_TAG_NAME . '",
|
||||
"body": "### Fixed\r\n\r\n- [issue #140](https://github.com/Bubka/2FAuth/issues/140) Bad regex for Period field (advanced form)\r\n- [issue #141](https://github.com/Bubka/2FAuth/issues/141) Digits field is missing in advanced form"
|
||||
}';
|
||||
|
||||
const ICON_PNG = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAsUlEQVR4AWN44aVBEhoCGl4GGLzND/nYW/Fpdsf7urTX8Q74NLwtjf7z+vl/VPDzwvFX4eYIDUhm6//99AGi6PfDOz9OH4Tr+TSrHYuG1/GOn+f3AtGnOV0vvLXeZPr8+/IJouHbthU4nJQfAtQANBuuFJ+GDx2F///9g6gAMn5dOfP34zt8Gr7tWQ838n1DBlDk973r+DS8Sff+snQKBL2KsQOKfJzSAOFC9EPQcEhLAD5LqIU3S31+AAAAAElFTkSuQmCC';
|
||||
}
|
466
tests/Data/MigrationTestData.php
Normal file
466
tests/Data/MigrationTestData.php
Normal file
@ -0,0 +1,466 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Data;
|
||||
|
||||
class MigrationTestData
|
||||
{
|
||||
const GOOGLE_AUTH_MIGRATION_URI = 'otpauth-migration://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 VALID_PLAIN_TEXT_PAYLOAD = OtpTestData::TOTP_FULL_CUSTOM_URI_NO_IMG . PHP_EOL .
|
||||
OtpTestData::HOTP_FULL_CUSTOM_URI_NO_IMG . PHP_EOL .
|
||||
PHP_EOL .
|
||||
OtpTestData::STEAM_TOTP_URI . PHP_EOL .
|
||||
PHP_EOL;
|
||||
|
||||
const VALID_PLAIN_TEXT_PAYLOAD_WITH_INTRUDER = OtpTestData::TOTP_FULL_CUSTOM_URI_NO_IMG . PHP_EOL .
|
||||
OtpTestData::HOTP_FULL_CUSTOM_URI_NO_IMG . PHP_EOL .
|
||||
'This is an intruder line' . PHP_EOL .
|
||||
OtpTestData::STEAM_TOTP_URI;
|
||||
|
||||
const INVALID_PLAIN_TEXT_NO_URI = '
|
||||
|
||||
XJGTDRUUKZH3X7TQN2QZUGCGXZCC5LXE
|
||||
';
|
||||
|
||||
const INVALID_PLAIN_TEXT_ONLY_EMPTY_LINES = '
|
||||
|
||||
|
||||
';
|
||||
|
||||
const PLAIN_TEXT_PAYLOAD_WITH_INVALID_URI = OtpTestData::TOTP_FULL_CUSTOM_URI_NO_IMG . PHP_EOL .
|
||||
'otpauth://totp/';
|
||||
|
||||
const VALID_AEGIS_JSON_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "hotp",
|
||||
"uuid": "e1b3f683-d5fe-4126-b616-8c8abd8ad97c",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"counter": ' . OtpTestData::COUNTER_CUSTOM . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "steamtotp",
|
||||
"uuid": "9fb06143-421d-46e1-a7e9-4aafe44b0e72",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::STEAM . '",
|
||||
"note": "",
|
||||
"icon": "null",
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::STEAM_SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_DEFAULT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_STEAM . ',
|
||||
"period": ' . OtpTestData::PERIOD_DEFAULT . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_SVG_ICON = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiPg0KICAgPGNpcmNsZSBjeD0iNTEyIiBjeT0iNTEyIiByPSI1MTIiIHN0eWxlPSJmaWxsOiMwMDBlOWMiLz4NCiAgIDxwYXRoIGQ9Im03MDAuMiA0NjYuNSA2MS4yLTEwNi4zYzIzLjYgNDEuNiAzNy4yIDg5LjggMzcuMiAxNDEuMSAwIDY4LjgtMjQuMyAxMzEuOS02NC43IDE4MS40SDU3NS44bDQ4LjctODQuNmgtNjQuNGw3NS44LTEzMS43IDY0LjMuMXptLTU1LjQtMTI1LjJMNDQ4LjMgNjgyLjVsLjEuMkgyOTAuMWMtNDAuNS00OS41LTY0LjctMTEyLjYtNjQuNy0xODEuNCAwLTUxLjQgMTMuNi05OS42IDM3LjMtMTQxLjNsMTAyLjUgMTc4LjIgMTEzLjMtMTk3aDE2Ni4zeiIgc3R5bGU9ImZpbGw6I2ZmZiIvPg0KPC9zdmc+DQo=",
|
||||
"icon_mime": "image\/svg+xml",
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_PNG_ICON = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoAQMAAAB3bUanAAAABlBMVEUAAAD///+l2Z",
|
||||
"icon_mime": "image\/png",
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_JPG_ICON = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////////////////////////////////////2wBDAVVaWnhpeOuCguv/////////////////////////////////////////////////////////////////////////wAARCAAyADIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCIeUkEbNFvLZ/iI6GpYlTzYHRNu7dkZz0oWJ/KVHt923ODvx1p6I/mxfudiJn+LPWgBjWyJEwf5duMSdc/h+lP+0JJL8i7nX7nOM56/wCTUVsGkVU3bo+d64xj0569fSpIY1haENH+8bdznp/TpQA+4RXnhVhkHd/Kq8JgllCeRjPfealUyusEoXeRuzyB7UJGyMGW1wR/00oAoUVP9kn/ALn6j/GigBqOsM4ZTvA/DPFWI5E80yEbtv3penX2/T9aaZIgshaTzi+OMFelSCNZp2cx7426NnGMD069aAG5SL5JYPLR+p356U1IGkswfvH+AdMc8/5NAMQnjNsu485GSO3vU9yGKsm7JfGxcenXn/GgCAW6qse5cyHPyZ+9+PQYHNSFGZpLdRiMY5/u9/qcmorh3jlX99udc/w4xmrbmUsUVdo/v5B/T9KAKP2Sf+5+o/xoo+yT/wBz9R/jRQBbSeV1DLBkH/bFRLEnmqj2+3dnB356UwxPLbQ7Fzjdnn3qWNGRrZWGCN1AEcSRBXPnbo+N42kfT36+lSQuyQQtnEY3b/z49+vpStNmJJ/KzjP8XTt+OaJNjWwjh53/AHR64PPWgBlvAjxRkx7t2dzbsYp1sjSwKrDEYz/wLn8xg/nUUccCZMp3I33G5GfXp/WiO3drYsjZ39Vx1wfWgB/+m/520VN5s/8Az7/+PiigDMqez/4+U/H+RoooAm07/lp+H9aIP+XX/gdFFABB/wAuv/A6mi/4+Z/+A/yoooAzKKKKAP/Z",
|
||||
"icon_mime": "image\/jpeg",
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_ICON = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoAQMAAAB3bUanAAAABlBMVEUAAAD///+l2Z",
|
||||
"icon_mime": "image\/gif",
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const VALID_2FAS_MIGRATION_PAYLOAD = '{
|
||||
"groups":
|
||||
[{
|
||||
"id": "C2F69014-C4C7-4EEC-9225-D24E750F77FD",
|
||||
"name": "Test",
|
||||
"isExpanded": true
|
||||
}],
|
||||
"schemaVersion": 2,
|
||||
"appOrigin": "ios",
|
||||
"appVersionCode": 32001,
|
||||
"services":
|
||||
[{
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"badge": {"color": "Default"},
|
||||
"order": {"position": 1},
|
||||
"otp":
|
||||
{
|
||||
"account": "' . OtpTestData::ACCOUNT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"counter": 0,
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . ',
|
||||
"algorithm": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"tokenType": "TOTP"
|
||||
},
|
||||
"updatedAt": 1657529430000,
|
||||
"name": "' . OtpTestData::SERVICE . '",
|
||||
"type": "ManuallyAdded"
|
||||
},
|
||||
{
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"badge": {"color": "Default"},
|
||||
"order": {"position": 2},
|
||||
"otp":
|
||||
{
|
||||
"account": "' . OtpTestData::ACCOUNT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"counter": ' . OtpTestData::COUNTER_CUSTOM . ',
|
||||
"period": 30,
|
||||
"algorithm": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"tokenType": "HOTP"
|
||||
},
|
||||
"updatedAt": 1657529430000,
|
||||
"name": "' . OtpTestData::SERVICE . '",
|
||||
"type": "ManuallyAdded",
|
||||
"icon":
|
||||
{
|
||||
"selected": "Brand",
|
||||
"brand":
|
||||
{
|
||||
"id": "ManuallyAdded"
|
||||
},
|
||||
"label":
|
||||
{
|
||||
"text": "OW",
|
||||
"backgroundColor": "LightBlue"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"appVersionName": "3.20.1"
|
||||
}';
|
||||
|
||||
const VALID_2FAS_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_OTP_TYPE = '{
|
||||
"groups":
|
||||
[{
|
||||
"id": "C2F69014-C4C7-4EEC-9225-D24E750F77FD",
|
||||
"name": "Test",
|
||||
"isExpanded": true
|
||||
}],
|
||||
"schemaVersion": 2,
|
||||
"appOrigin": "ios",
|
||||
"appVersionCode": 32001,
|
||||
"services":
|
||||
[{
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"badge": {"color": "Default"},
|
||||
"order": {"position": 1},
|
||||
"otp":
|
||||
{
|
||||
"account": "' . OtpTestData::ACCOUNT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"counter": 0,
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . ',
|
||||
"algorithm": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"tokenType": "TOTP"
|
||||
},
|
||||
"updatedAt": 1657529430000,
|
||||
"name": "' . OtpTestData::SERVICE . '",
|
||||
"type": "ManuallyAdded"
|
||||
},
|
||||
{
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"badge": {"color": "Default"},
|
||||
"order": {"position": 2},
|
||||
"otp":
|
||||
{
|
||||
"account": "' . OtpTestData::ACCOUNT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"counter": ' . OtpTestData::COUNTER_CUSTOM . ',
|
||||
"period": 30,
|
||||
"algorithm": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"tokenType": "XOTP"
|
||||
},
|
||||
"updatedAt": 1657529430000,
|
||||
"name": "' . OtpTestData::SERVICE . '",
|
||||
"type": "ManuallyAdded"
|
||||
}],
|
||||
"appVersionName": "3.20.1"
|
||||
}';
|
||||
|
||||
const INVALID_2FAS_MIGRATION_PAYLOAD = '{
|
||||
"groups":
|
||||
[{
|
||||
"id": "C2F69014-C4C7-4EEC-9225-D24E750F77FD",
|
||||
"name": "Test",
|
||||
"isExpanded": true
|
||||
}],
|
||||
"schemaVersion": 2,
|
||||
"appOrigin": "ios",
|
||||
"appVersionCode": 32001,
|
||||
"service__BAD__NAME___s":
|
||||
[{
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"badge": {"color": "Default"},
|
||||
"order": {"position": 1},
|
||||
"otp":
|
||||
{
|
||||
"account": "' . OtpTestData::ACCOUNT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"counter": 0,
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . ',
|
||||
"algorithm": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"tokenType": "TOTP"
|
||||
},
|
||||
"updatedAt": 1657529430000,
|
||||
"name": "' . OtpTestData::SERVICE . '",
|
||||
"type": "ManuallyAdded"
|
||||
}],
|
||||
"appVersionName": "3.20.1"
|
||||
}';
|
||||
|
||||
const ENCRYPTED_2FAS_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"reference": "RPKUw+hful0yPrvxaDjt2IZWMVgesKPMgJQ/U8CzT6I+eyPo9+yO1xX7jdYYS0aD3DGghxXBN6diIjTe5wKhbWXGvBdCF1a528j1/bgY0cn+MCXf0eG6uQ7oE+idBXeFZsXk5oxqZEA5waYsuwBenILCHqzU7mIlzqUXcNyb3gFF+AhkQGlfUBKeVcIXrbuTuwbtA8IwgJfV8klsUFH96EGsiCMQ2v2MCXVaqFR3TlTMfj4iDg9ULktnKisxfm6O3B/XQtsiDVpnhqQzFSqktbh9vKOrO2NgK40kLmgRkS08SpxiC4Yt0CoGyp9bgLYySdQDvKMnbYxVatx6AJpckVuA7ICiQjbHa5mItGITU78=:yrDmOf1HirJ5N7wlYXyUmL0EtNy2t5oNpvQw1rV22nA=:8MwK8sWyL3iVv+6w",
|
||||
"schemaVersion": 2,
|
||||
"appVersionCode": 32001,
|
||||
"services":
|
||||
[],
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"id": "C2F69014-C4C7-4EEC-9225-D24E750F77FD",
|
||||
"name": "Test",
|
||||
"isExpanded": true
|
||||
}
|
||||
],
|
||||
"appOrigin": "ios",
|
||||
"servicesEncrypted": "xlH6lrz/oW/TJO2WExdt33bI4CvCjE/W2wlcl+8VVbRj/BHrYujY7AexaenaBhoJUB0xeOMDL1q2swvtVYQZLAMFoBcg4XMEVfk5R6R0SjwJd7/cvcv89nKNDX/eHtIHKoaDkgFxk4Po9YN1OtG0M/jXH9DdERu+LAJHboLK8VpnehOJTbs/s0vahrw0q4jZiHGCfV7vXrylsuEjXBVv90/dT0mZE5iiPIteWCJyxl+04cuTLMl6kMUXem0OGTsqOLmmSMVz8sCrf6HRzqH8KFj1sR6h+MGVdsVij8f99O4ZB+tCCy0A3BY8Yn68J8aBAOUU4nzETAl0Zhc489E9X9eibMLkaBt452gfwXU/9muIeA2WM8so3sM6SPQVls8xYu653z7bh7WCCIjrJmZ10DVydK9Krjoe6nd+DQjuNy/vcNFeCe6/CljF3KWfWyNRdcwaUH+yKZE/TFw3P+ZuvudnfHRBE6OsE3c/XmxnI66mSy+SePhmIoe9x0rxC5T5LM5Fw+bWBLR6mLXo1ouxxrljeboqJ4THR6a2x8hhhP8wvK30v/PDPyLTtBdLv+HilQibXF9YqVH562BhlmhLAe5dv67srP6JwK10tAo5TfhKjewMqTouOiCqT+/mvvqpTV/1f676mzF7s1Rh83ikfqzT/u5OJrFqZdu3tC86KqCLIDx0fQYF/Ha88tXPkVGvPXygH3Adgf7c8spyC9s3yeAAtSSc/qtIpm1Ux5XvaZx3M9cD9ku0KLJqE+rJr0HS2AatS54I/ybdHSbMLaPRdV67jeM1OwCkY5F+FXQpFfcQkpmgCMTvFddnkhUqTR8GgqXcOg2aPcwNQ/EqhIJr+sXqfFS85+eSlroErvUuR9gO/awB2XA4NsZI6bHK9GNebcSTmA16FhiuAFpOqfWcdN3iPeKS/kcUJ8KRlsAO0x9UINnkwJtpSGSA8bdXkOFfnd4rPPDcBGrx7dBtPC+Sc5Y8nvbkLcxn8/CCFgenQByPwGLUp4NuMz8H1/1EwNQ4jF5swoqdnhz6ldBAF+T1trOzQEmpFfG54lur3yEoiYipaZ5WTKJnp6X0GHkgT3Wi0N0im4oGEsqyPEEknm17BamooqRo1JMPA6+cp90udr91DKeLxzRBi8rYaolQjaATbc6rvufcoCuEdOYpGqWfX6mGKD/jmHko4RaoM+7cx6LaH5zRdpBLDzr1aW0TJSAHGgTuTiMJwW73MBaayUf+X+alzlXpcyDlWW+aGpSsYI/TkKTPaIgQFcNWa/+1ayUyg1Tmvuf1J6YgyQJNBVf2LV06I+TziUHPGkwwN7nj/0chqw4sv29I4mpI41C6a/NnoSCa3Vz448DPlk1e80KyMba722gBMTCyU+S1K/UtVE7cPchOETL7X5tl6yoyeh95jbpYX4kJhXHNFZ5XA7d0tFxmYF0mSN5+D63jP2tTqPW7lsz3No9y+VFhQLWTEShrBck1blgNXRFdUCDX5zN4vSYmt44vYGWl3+iDNhYysVh2x2bAGOxCR5EPNPNuUjrxCHRtt8X7Xof7/R0fZF4Qi/F1o6D/ToUpp4XZ/3sjJ1BTouw2Jpx/f2gGDwzFq8uql46eeBgqiQwCSYZTbSSYliVaXh01jJltfqFaMMQF9UGehNdcsAHaLr53I8snMJZLl4IIGwtNgb9cHZYXYM68RtP0UPBntCFA74fWMBVcLLfbpUcGe5fuj4CoBo5gCakYygnsvlqcsmXNR/zaf5xIOC009qVUU0BO+qtgt+TGxAn4K9jgxVOCzR7TPGPvwaOajURwcutj9QXUKuqHz9LwIYZLLzDd2OQxkDoPKfLqKSLP4ZXpKiRP8LkgvZE91ZgxJgCy37STqO1py/VyrHjT3OOmbZ6srwaIOJhZ8/YGh6mecA7n4qJsJUepvD3sbAF0FZJE0xu1kkw=:yrDmOf1HirJ5N7wlYXyUmL0EtNy2t5oNpvQw1rV22nA=:ZhgkRGr/vRf6K1ri",
|
||||
"appVersionName": "3.20.1"
|
||||
}';
|
||||
|
||||
const VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_OTP_TYPE = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"entries": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "fb2ebd05-9d71-4b2e-9d4e-b7f8d2942bfb",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_CUSTOM . '",
|
||||
"digits": ' . OtpTestData::DIGITS_CUSTOM . ',
|
||||
"period": ' . OtpTestData::PERIOD_CUSTOM . '
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "xotp",
|
||||
"uuid": "e1b3f683-d5fe-4126-b616-8c8abd8ad97c",
|
||||
"name": "",
|
||||
"issuer": "",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "",
|
||||
"algo": "",
|
||||
"digits": 0,
|
||||
"counter": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const INVALID_AEGIS_JSON_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": null,
|
||||
"params": null
|
||||
},
|
||||
"db": {
|
||||
"version": 2,
|
||||
"thisIsNotTheCorrectKeyName": [
|
||||
{
|
||||
"type": "totp",
|
||||
"uuid": "5be1b189-260d-4fe1-930a-a78cb669dd86",
|
||||
"name": "' . OtpTestData::ACCOUNT . '",
|
||||
"issuer": "' . OtpTestData::SERVICE . '",
|
||||
"note": "",
|
||||
"icon": null,
|
||||
"info": {
|
||||
"secret": "' . OtpTestData::SECRET . '",
|
||||
"algo": "' . OtpTestData::ALGORITHM_DEFAULT . '",
|
||||
"digits": ' . OtpTestData::DIGITS_DEFAULT . ',
|
||||
"period": ' . OtpTestData::PERIOD_DEFAULT . '
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
|
||||
const ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD = '
|
||||
{
|
||||
"version": 1,
|
||||
"header": {
|
||||
"slots": [
|
||||
{
|
||||
"type": 1,
|
||||
"uuid": "1f447956-c71c-4be4-8192-97197dc67df7",
|
||||
"key": "d742967686cae462c5732023a72d59245d8q7c5c93a66aeb2q2a350bb8b6a7ae",
|
||||
"key_params": {
|
||||
"nonce": "77a8ff6d84265efd2a3ed9b7",
|
||||
"tag": "cc13fb4a5baz3fd27bc97f5eacaa00d0"
|
||||
},
|
||||
"n": 32768,
|
||||
"r": 8,
|
||||
"p": 1,
|
||||
"salt": "1c245b2696b948dt040c30c538aeb6f9620b054d9ff182f33dd4b285b67bed51",
|
||||
"repaired": true
|
||||
}
|
||||
],
|
||||
"params": {
|
||||
"nonce": "f31675d9966d2z588bd07788",
|
||||
"tag": "ad5729fa135dc6d6sw87e0c955932661"
|
||||
}
|
||||
},
|
||||
"db": "1rX0ajzsxNbhN2hvnNCMBNooLlzqwz\/LMT3bNEIJjPH+zIvIbA6GVVPHLpna+yvjxLPKVkt1OQig=="
|
||||
}';
|
||||
}
|
116
tests/Data/OtpTestData.php
Normal file
116
tests/Data/OtpTestData.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Data;
|
||||
|
||||
class OtpTestData
|
||||
{
|
||||
const ACCOUNT = 'account';
|
||||
|
||||
const SERVICE = 'service';
|
||||
|
||||
const STEAM = 'Steam';
|
||||
|
||||
const SECRET = 'A4GRFHVVRBGY7UIW';
|
||||
|
||||
const STEAM_SECRET = 'XJGTDRUUKZH3X7TQN2QZUGCGXZCC5LXE';
|
||||
|
||||
const ALGORITHM_DEFAULT = 'sha1';
|
||||
|
||||
const ALGORITHM_CUSTOM = 'sha256';
|
||||
|
||||
const DIGITS_DEFAULT = 6;
|
||||
|
||||
const DIGITS_CUSTOM = 7;
|
||||
|
||||
const DIGITS_STEAM = 5;
|
||||
|
||||
const PERIOD_DEFAULT = 30;
|
||||
|
||||
const PERIOD_CUSTOM = 40;
|
||||
|
||||
const COUNTER_DEFAULT = 0;
|
||||
|
||||
const COUNTER_CUSTOM = 5;
|
||||
|
||||
const IMAGE = 'https%3A%2F%2Fen.opensuse.org%2Fimages%2F4%2F44%2FButton-filled-colour.png';
|
||||
|
||||
const ICON = 'test.png';
|
||||
|
||||
const TOTP_FULL_CUSTOM_URI_NO_IMG = 'otpauth://totp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&period=' . self::PERIOD_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM;
|
||||
|
||||
const TOTP_FULL_CUSTOM_URI = self::TOTP_FULL_CUSTOM_URI_NO_IMG . '&image=' . self::IMAGE;
|
||||
|
||||
const HOTP_FULL_CUSTOM_URI_NO_IMG = 'otpauth://hotp/' . self::SERVICE . ':' . self::ACCOUNT . '?secret=' . self::SECRET . '&issuer=' . self::SERVICE . '&digits=' . self::DIGITS_CUSTOM . '&counter=' . self::COUNTER_CUSTOM . '&algorithm=' . self::ALGORITHM_CUSTOM;
|
||||
|
||||
const HOTP_FULL_CUSTOM_URI = self::HOTP_FULL_CUSTOM_URI_NO_IMG . '&image=' . self::IMAGE;
|
||||
|
||||
const TOTP_SHORT_URI = 'otpauth://totp/' . 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 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 ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_TOTP = [
|
||||
'service' => self::SERVICE,
|
||||
'account' => self::ACCOUNT,
|
||||
'icon' => self::ICON,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => self::SECRET,
|
||||
'digits' => self::DIGITS_CUSTOM,
|
||||
'algorithm' => self::ALGORITHM_CUSTOM,
|
||||
'period' => self::PERIOD_CUSTOM,
|
||||
'counter' => null,
|
||||
];
|
||||
|
||||
const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_TOTP = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => self::SECRET,
|
||||
];
|
||||
|
||||
const ARRAY_OF_PARAMETERS_FOR_UNSUPPORTED_OTP_TYPE = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'Xotp',
|
||||
'secret' => self::SECRET,
|
||||
];
|
||||
|
||||
const ARRAY_OF_INVALID_PARAMETERS_FOR_TOTP = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'totp',
|
||||
'secret' => 0,
|
||||
];
|
||||
|
||||
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_CUSTOM_HOTP = [
|
||||
'service' => self::SERVICE,
|
||||
'account' => self::ACCOUNT,
|
||||
'icon' => self::ICON,
|
||||
'otp_type' => 'hotp',
|
||||
'secret' => self::SECRET,
|
||||
'digits' => self::DIGITS_CUSTOM,
|
||||
'algorithm' => self::ALGORITHM_CUSTOM,
|
||||
'period' => null,
|
||||
'counter' => self::COUNTER_CUSTOM,
|
||||
];
|
||||
|
||||
const ARRAY_OF_MINIMUM_VALID_PARAMETERS_FOR_HOTP = [
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'hotp',
|
||||
'secret' => self::SECRET,
|
||||
];
|
||||
|
||||
const ARRAY_OF_FULL_VALID_PARAMETERS_FOR_STEAM_TOTP = [
|
||||
'service' => self::STEAM,
|
||||
'account' => self::ACCOUNT,
|
||||
'otp_type' => 'steamtotp',
|
||||
'secret' => self::STEAM_SECRET,
|
||||
'digits' => self::DIGITS_STEAM,
|
||||
'algorithm' => self::ALGORITHM_DEFAULT,
|
||||
'period' => self::PERIOD_DEFAULT,
|
||||
'counter' => null,
|
||||
];
|
||||
|
||||
}
|
@ -9,6 +9,12 @@
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\ForgotPasswordController
|
||||
* @covers \App\Models\User
|
||||
* @covers \App\Http\Middleware\RejectIfDemoMode
|
||||
* @covers \App\Http\Middleware\RejectIfAuthenticated
|
||||
*/
|
||||
class ForgotPasswordControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -26,7 +32,7 @@ public function test_submit_email_password_request_without_email_returns_validat
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['email']);
|
||||
->assertJsonValidationErrors(['email']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,7 +45,7 @@ public function test_submit_email_password_request_with_invalid_email_returns_va
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['email']);
|
||||
->assertJsonValidationErrors(['email']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +58,7 @@ public function test_submit_email_password_request_with_unknown_email_returns_va
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['email']);
|
||||
->assertJsonValidationErrors(['email']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,4 +97,21 @@ public function test_submit_email_password_request_in_demo_mode_returns_unauthor
|
||||
|
||||
$response->assertStatus(401);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_submit_email_password_request_when_authenticated_returns_bad_request()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($user, 'web-guard')
|
||||
->json('POST', '/user/password/lost', [
|
||||
'email' => $user->email,
|
||||
])
|
||||
->assertStatus(400)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,15 @@
|
||||
use App\Facades\Settings;
|
||||
use App\Models\User;
|
||||
use Tests\FeatureTestCase;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\LoginController
|
||||
* @covers \App\Http\Middleware\RejectIfAuthenticated
|
||||
* @covers \App\Http\Middleware\RejectIfReverseProxy
|
||||
* @covers \App\Http\Middleware\RejectIfDemoMode
|
||||
* @covers \App\Http\Middleware\SkipIfAuthenticated
|
||||
*/
|
||||
class LoginTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -20,7 +28,7 @@ class LoginTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -36,15 +44,35 @@ public function test_user_login_returns_success()
|
||||
'email' => $this->user->email,
|
||||
'password' => self::PASSWORD,
|
||||
])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'message' => 'authenticated',
|
||||
'name' => $this->user->name,
|
||||
]);
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'message' => 'authenticated',
|
||||
'name' => $this->user->name,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers \App\Rules\CaseInsensitiveEmailExists
|
||||
*/
|
||||
public function test_user_login_with_uppercased_email_returns_success()
|
||||
{
|
||||
$response = $this->json('POST', '/user/login', [
|
||||
'email' => strtoupper($this->user->email),
|
||||
'password' => self::PASSWORD,
|
||||
])
|
||||
->assertOk()
|
||||
->assertExactJson([
|
||||
'message' => 'authenticated',
|
||||
'name' => $this->user->name,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers \App\Http\Middleware\SkipIfAuthenticated
|
||||
*/
|
||||
public function test_user_login_already_authenticated_returns_bad_request()
|
||||
{
|
||||
@ -74,26 +102,28 @@ public function test_user_login_with_missing_data_returns_validation_error()
|
||||
'email' => '',
|
||||
'password' => '',
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'email',
|
||||
'password',
|
||||
]);
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'email',
|
||||
'password',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers \App\Exceptions\Handler
|
||||
*/
|
||||
public function test_user_login_with_invalid_credentials_returns_validation_error()
|
||||
public function test_user_login_with_invalid_credentials_returns_authentication_error()
|
||||
{
|
||||
$response = $this->json('POST', '/user/login', [
|
||||
'email' => $this->user->email,
|
||||
'password' => self::WRONG_PASSWORD,
|
||||
])
|
||||
->assertStatus(401)
|
||||
->assertJson([
|
||||
'message' => 'unauthorised',
|
||||
]);
|
||||
->assertStatus(401)
|
||||
->assertJson([
|
||||
'message' => 'unauthorised',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,6 +184,9 @@ public function test_user_logout_returns_validation_success()
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers \App\Http\Middleware\KickOutInactiveUser
|
||||
* @covers \App\Http\Middleware\LogUserLastSeen
|
||||
*/
|
||||
public function test_user_logout_after_inactivity_returns_teapot()
|
||||
{
|
||||
@ -169,7 +202,7 @@ public function test_user_logout_after_inactivity_returns_teapot()
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('GET', '/api/v1/twofaccounts');
|
||||
|
||||
sleep(61);
|
||||
$this->travelTo(Carbon::now()->addMinutes(2));
|
||||
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('GET', '/api/v1/twofaccounts')
|
||||
|
@ -5,6 +5,9 @@
|
||||
use App\Models\User;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\PasswordController
|
||||
*/
|
||||
class PasswordControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -19,7 +22,7 @@ class PasswordControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
@ -6,6 +6,9 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\RegisterController
|
||||
*/
|
||||
class RegisterControllerTest extends FeatureTestCase
|
||||
{
|
||||
private const USERNAME = 'john doe';
|
||||
@ -17,7 +20,7 @@ class RegisterControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
@ -35,18 +38,20 @@ public function test_register_returns_success()
|
||||
'password' => self::PASSWORD,
|
||||
'password_confirmation' => self::PASSWORD,
|
||||
])
|
||||
->assertCreated()
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
'name',
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'name' => self::USERNAME,
|
||||
]);
|
||||
->assertCreated()
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
'name',
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'name' => self::USERNAME,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @covers \App\Rules\FirstUser
|
||||
*/
|
||||
public function test_register_returns_already_an_existing_user()
|
||||
{
|
||||
@ -59,7 +64,7 @@ public function test_register_returns_already_an_existing_user()
|
||||
'password' => self::PASSWORD,
|
||||
'password_confirmation' => self::PASSWORD,
|
||||
])
|
||||
->assertJsonValidationErrorFor('name');
|
||||
->assertJsonValidationErrorFor('name');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,10 @@
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\ResetPasswordController
|
||||
* @covers \App\Models\User
|
||||
*/
|
||||
class ResetPasswordControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -28,7 +32,7 @@ public function test_submit_reset_password_without_input_returns_validation_erro
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['email', 'password', 'token']);
|
||||
->assertJsonValidationErrors(['email', 'password', 'token']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,7 +48,7 @@ public function test_submit_reset_password_with_invalid_data_returns_validation_
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['email', 'password']);
|
||||
->assertJsonValidationErrors(['email', 'password']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,7 +64,7 @@ public function test_submit_reset_password_with_too_short_pwd_returns_validation
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors(['password']);
|
||||
->assertJsonValidationErrors(['password']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,10 @@
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\UserController
|
||||
* @covers \App\Http\Middleware\RejectIfDemoMode
|
||||
*/
|
||||
class UserControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -23,7 +27,7 @@ class UserControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
@ -5,7 +5,16 @@
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Tests\FeatureTestCase;
|
||||
use App\Notifications\WebauthnRecoveryNotification;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\WebAuthnDeviceLostController
|
||||
* @covers \App\Notifications\WebauthnRecoveryNotification
|
||||
* @covers \App\Extensions\WebauthnCredentialBroker
|
||||
* @covers \App\Http\Requests\WebauthnDeviceLostRequest
|
||||
* @covers \App\Providers\AuthServiceProvider
|
||||
*/
|
||||
class WebAuthnDeviceLostControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -16,7 +25,7 @@ class WebAuthnDeviceLostControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -25,6 +34,7 @@ public function setUp() : void
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers \App\Models\Traits\WebAuthnManageCredentials
|
||||
*/
|
||||
public function test_sendRecoveryEmail_sends_notification_on_success()
|
||||
{
|
||||
@ -34,18 +44,60 @@ public function test_sendRecoveryEmail_sends_notification_on_success()
|
||||
'email' => $this->user->email,
|
||||
]);
|
||||
|
||||
Notification::assertSentTo($this->user, \App\Notifications\WebauthnRecoveryNotification::class);
|
||||
Notification::assertSentTo($this->user, WebauthnRecoveryNotification::class);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('webauthn_recoveries', [
|
||||
'email' => $this->user->email
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_sendRecoveryEmail_does_not_send_anything_on_error()
|
||||
public function test_WebauthnRecoveryNotification_renders_to_email()
|
||||
{
|
||||
$mail = (new WebauthnRecoveryNotification('test_token'))->toMail($this->user)->render();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'http://localhost/webauthn/recover?token=test_token&email=' . urlencode($this->user->email),
|
||||
$mail
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
Lang::get('Recover Account'),
|
||||
$mail
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
Lang::get(
|
||||
'You are receiving this email because we received an account recovery request for your account.'
|
||||
),
|
||||
$mail
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
Lang::get(
|
||||
'This recovery link will expire in :count minutes.',
|
||||
['count' => config('auth.passwords.webauthn.expire')]
|
||||
),
|
||||
$mail
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
Lang::get('If you did not request an account recovery, no further action is required.'),
|
||||
$mail
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_sendRecoveryEmail_does_not_send_anything_to_unknown_email()
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
@ -56,8 +108,103 @@ public function test_sendRecoveryEmail_does_not_send_anything_on_error()
|
||||
Notification::assertNothingSent();
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'email',
|
||||
->assertJsonValidationErrors([
|
||||
'email',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('webauthn_recoveries', [
|
||||
'email' => 'bad@email.com'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_sendRecoveryEmail_does_not_send_anything_to_invalid_email()
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$response = $this->json('POST', '/webauthn/lost', [
|
||||
'email' => 'bad@email.com',
|
||||
]);
|
||||
|
||||
Notification::assertNothingSent();
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'email',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('webauthn_recoveries', [
|
||||
'email' => 'bad@email.com'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_sendRecoveryEmail_does_not_send_anything_to_not_WebAuthnAuthenticatable()
|
||||
{
|
||||
$mock = $this->mock(\App\Extensions\WebauthnCredentialBroker::class)->makePartial();
|
||||
$mock->shouldReceive('getUser')
|
||||
->andReturn(new \Illuminate\Foundation\Auth\User());
|
||||
|
||||
Notification::fake();
|
||||
|
||||
$response = $this->json('POST', '/webauthn/lost', [
|
||||
'email' => $this->user->email,
|
||||
]);
|
||||
|
||||
Notification::assertNothingSent();
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'email',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_sendRecoveryEmail_is_throttled()
|
||||
{
|
||||
Notification::fake();
|
||||
|
||||
$response = $this->json('POST', '/webauthn/lost', [
|
||||
'email' => $this->user->email,
|
||||
]);
|
||||
|
||||
Notification::assertSentTo($this->user, WebauthnRecoveryNotification::class);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('webauthn_recoveries', [
|
||||
'email' => $this->user->email
|
||||
]);
|
||||
|
||||
$this->json('POST', '/webauthn/lost', [
|
||||
'email' => $this->user->email,
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrorfor('email')
|
||||
->assertJsonFragment([
|
||||
'message' => __('passwords.throttled')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_error_if_no_broker_is_set()
|
||||
{
|
||||
$this->app['config']->set('auth.passwords.webauthn', null);
|
||||
|
||||
$this->json('POST', '/webauthn/lost', [
|
||||
'email' => $this->user->email
|
||||
])
|
||||
->assertStatus(500);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,14 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laragear\WebAuthn\Http\Requests\AssertedRequest;
|
||||
use Tests\FeatureTestCase;
|
||||
use Laragear\WebAuthn\WebAuthn;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Laragear\WebAuthn\Assertion\Validator\AssertionValidator;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\WebAuthnLoginController
|
||||
* @covers \App\Models\User
|
||||
*/
|
||||
class WebAuthnLoginControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -15,15 +22,44 @@ class WebAuthnLoginControllerTest extends FeatureTestCase
|
||||
protected $user;
|
||||
|
||||
const CREDENTIAL_ID = 's06aG41wsIYh5X1YUhB-SlH8y3F2RzdJZVse8iXRXOCd3oqQdEyCOsBawzxrYBtJRQA2azAMEN_q19TUp6iMgg';
|
||||
const CREDENTIAL_ID_ALT = '-VOLFKPY-_FuMI_sJ7gMllK76L3VoRUINj6lL_Z3qDg';
|
||||
const CREDENTIAL_ID_ALT_RAW = '+VOLFKPY+/FuMI/sJ7gMllK76L3VoRUINj6lL/Z3qDg=';
|
||||
|
||||
const PUBLIC_KEY = 'eyJpdiI6ImYyUHlJOEJML0pwTXJ2UDkveTQwZFE9PSIsInZhbHVlIjoiQWFSYi9LVEszazlBRUZsWHp0cGNRNktGeEQ3aTBsbU9zZ1g5MEgrWFJJNmgraElsNU9hV0VsRVlWc3NoUVVHUjRRdlcxTS9pVklnOWtVYWY5TFJQTTFhR1Rxb1ZzTFkxTWE4VUVvK1lyU3pYQ1M3VlBMWWxZcDVaYWFnK25iaXVyWGR6ZFRmMFVoSmdPZ3UvSnptbVZER0FYdEEyYmNYcW43RkV5aTVqSjNwZEFsUjhUYSs0YjU2Z2V2bUJXa0E0aVB1VC8xSjdJZ2llRGlHY2RwOGk3MmNPTyt6eDFDWUs1dVBOSWp1ZUFSeUlkclgwRW16RE9sUUpDSWV6Sk50TSIsIm1hYyI6IjI3ODQ5NzcxZGY1MzMwYTNiZjAwZmEwMDJkZjYzMGU4N2UzZjZlOGM0ZWE3NDkyYWMxMThhNmE5NWZiMTVjNGEiLCJ0YWciOiIifQ==';
|
||||
|
||||
const USER_ID = '3b758ac868b74307a7e96e69ae187339';
|
||||
const USER_ID_ALT = 'e8af6f703f8042aa91c30cf72289aa07';
|
||||
|
||||
const ASSERTION_RESPONSE = [
|
||||
'id' => self::CREDENTIAL_ID_ALT,
|
||||
'rawId' => self::CREDENTIAL_ID_ALT_RAW,
|
||||
'type' => 'public-key',
|
||||
'response' => [
|
||||
'clientDataJSON' => 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaVhvem15bktpLVlEMmlSdktOYlNQQSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3QiLCJjcm9zc09yaWdpbiI6ZmFsc2V9',
|
||||
'authenticatorData' => 'SZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2MFAAAAAQ==',
|
||||
'signature' => 'ca4IJ9h8bZnjMbEFuHX1zfX5LcbiPyDVz6sD1/ppR4t8++1DxKa5EdBIrfNlo8FSOv/JSzMrGGUCQvc/Ngj1KnZpO3s9OdTb54/gMDewH/K8EG4wSvxzHdL6sMbP7UUc5Wq1pcdu9MgXY8V+1gftXpzcoaae0X+mLEETgU7eB8jG0mZhVWvE4yQKuDnZA1i9r8oQhqsvG4nUw1BxvR8wAGiRR+R287LaL41k+xum5mS8zEojUmuLSH50miyVxZ4Y+/oyfxG7i+wSYGNSXlW5iNPB+2WupGS7ce4TuOgaFeMmP2a9rzP4m2IBSQoJ2FyrdzR7HwBEewqqrUVbGQw3Aw==',
|
||||
'userHandle' => self::USER_ID_ALT,
|
||||
]
|
||||
];
|
||||
|
||||
const ASSERTION_RESPONSE_NO_HANDLE = [
|
||||
'id' => self::CREDENTIAL_ID_ALT,
|
||||
'rawId' => self::CREDENTIAL_ID_ALT_RAW,
|
||||
'type' => 'public-key',
|
||||
'response' => [
|
||||
'clientDataJSON' => 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaVhvem15bktpLVlEMmlSdktOYlNQQSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3QiLCJjcm9zc09yaWdpbiI6ZmFsc2V9',
|
||||
'authenticatorData' => 'SZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2MFAAAAAQ==',
|
||||
'signature' => 'ca4IJ9h8bZnjMbEFuHX1zfX5LcbiPyDVz6sD1/ppR4t8++1DxKa5EdBIrfNlo8FSOv/JSzMrGGUCQvc/Ngj1KnZpO3s9OdTb54/gMDewH/K8EG4wSvxzHdL6sMbP7UUc5Wq1pcdu9MgXY8V+1gftXpzcoaae0X+mLEETgU7eB8jG0mZhVWvE4yQKuDnZA1i9r8oQhqsvG4nUw1BxvR8wAGiRR+R287LaL41k+xum5mS8zEojUmuLSH50miyVxZ4Y+/oyfxG7i+wSYGNSXlW5iNPB+2WupGS7ce4TuOgaFeMmP2a9rzP4m2IBSQoJ2FyrdzR7HwBEewqqrUVbGQw3Aw==',
|
||||
'userHandle' => null,
|
||||
]
|
||||
];
|
||||
|
||||
const ASSERTION_CHALLENGE = 'iXozmynKi+YD2iRvKNbSPA==';
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -47,6 +83,42 @@ public function test_webauthn_login_uses_login_and_returns_no_content()
|
||||
->assertNoContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_webauthn_login_merge_handle_if_missing()
|
||||
{
|
||||
$this->user = User::factory()->create();
|
||||
|
||||
DB::table('webauthn_credentials')->insert([
|
||||
'id' => self::CREDENTIAL_ID_ALT,
|
||||
'authenticatable_type' => \App\Models\User::class,
|
||||
'authenticatable_id' => $this->user->id,
|
||||
'user_id' => self::USER_ID_ALT,
|
||||
'counter' => 0,
|
||||
'rp_id' => 'http://localhost',
|
||||
'origin' => 'http://localhost',
|
||||
'aaguid' => '00000000-0000-0000-0000-000000000000',
|
||||
'attestation_format' => 'none',
|
||||
'public_key' => self::PUBLIC_KEY,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
$this->session(['_webauthn' => new \Laragear\WebAuthn\Challenge(
|
||||
new \Laragear\WebAuthn\ByteBuffer(base64_decode(self::ASSERTION_CHALLENGE)),
|
||||
60,
|
||||
false,
|
||||
)]);
|
||||
|
||||
$this->mock(AssertionValidator::class)
|
||||
->expects('send->thenReturn')
|
||||
->andReturn();
|
||||
|
||||
$this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE_NO_HANDLE)
|
||||
->assertNoContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
@ -84,22 +156,24 @@ public function test_webauthn_login_with_missing_data_returns_validation_error()
|
||||
];
|
||||
|
||||
$response = $this->json('POST', '/webauthn/login', $data)
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'id',
|
||||
'rawId',
|
||||
'type',
|
||||
'response.authenticatorData',
|
||||
'response.clientDataJSON',
|
||||
'response.signature',
|
||||
]);
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'id',
|
||||
'rawId',
|
||||
'type',
|
||||
'response.authenticatorData',
|
||||
'response.clientDataJSON',
|
||||
'response.signature',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_get_options_returns_success()
|
||||
public function test_get_options_for_securelogin_returns_success()
|
||||
{
|
||||
Config::set('webauthn.user_verification', WebAuthn::USER_VERIFICATION_REQUIRED);
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
|
||||
DB::table('webauthn_credentials')->insert([
|
||||
@ -118,18 +192,59 @@ public function test_get_options_returns_success()
|
||||
]);
|
||||
|
||||
$response = $this->json('POST', '/webauthn/login/options')
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'challenge',
|
||||
'userVerification',
|
||||
'timeout',
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'allowCredentials' => [[
|
||||
'id' => self::CREDENTIAL_ID,
|
||||
'type' => 'public-key',
|
||||
]],
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'challenge',
|
||||
'userVerification',
|
||||
'timeout',
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'userVerification' => 'required',
|
||||
'allowCredentials' => [[
|
||||
'id' => self::CREDENTIAL_ID,
|
||||
'type' => 'public-key',
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_get_options_for_fastlogin_returns_success()
|
||||
{
|
||||
Config::set('webauthn.user_verification', WebAuthn::USER_VERIFICATION_DISCOURAGED);
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
|
||||
DB::table('webauthn_credentials')->insert([
|
||||
'id' => self::CREDENTIAL_ID,
|
||||
'authenticatable_type' => \App\Models\User::class,
|
||||
'authenticatable_id' => $this->user->id,
|
||||
'user_id' => self::USER_ID,
|
||||
'counter' => 0,
|
||||
'rp_id' => 'http://localhost',
|
||||
'origin' => 'http://localhost',
|
||||
'aaguid' => '00000000-0000-0000-0000-000000000000',
|
||||
'attestation_format' => 'none',
|
||||
'public_key' => self::PUBLIC_KEY,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->json('POST', '/webauthn/login/options')
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'challenge',
|
||||
'userVerification',
|
||||
'timeout',
|
||||
])
|
||||
->assertJsonFragment([
|
||||
'userVerification' => 'discouraged',
|
||||
'allowCredentials' => [[
|
||||
'id' => self::CREDENTIAL_ID,
|
||||
'type' => 'public-key',
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,9 +253,9 @@ public function test_get_options_returns_success()
|
||||
public function test_get_options_with_no_registred_user_returns_error()
|
||||
{
|
||||
$this->json('POST', '/webauthn/login/options')
|
||||
->assertStatus(400)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
]);
|
||||
->assertStatus(400)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\WebAuthnManageController
|
||||
* @covers \App\Http\Middleware\RejectIfReverseProxy
|
||||
* @covers \App\Models\Traits\WebAuthnManageCredentials
|
||||
*/
|
||||
class WebAuthnManageControllerTest extends FeatureTestCase
|
||||
{
|
||||
// use WithoutMiddleware;
|
||||
@ -23,7 +28,7 @@ class WebAuthnManageControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
@ -8,6 +8,12 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\WebAuthnRecoveryController
|
||||
* @covers \App\Extensions\WebauthnCredentialBroker
|
||||
* @covers \App\Http\Requests\WebauthnRecoveryRequest
|
||||
* @covers \App\Providers\AuthServiceProvider
|
||||
*/
|
||||
class WebAuthnRecoveryControllerTest extends FeatureTestCase
|
||||
{
|
||||
/**
|
||||
@ -29,7 +35,7 @@ class WebAuthnRecoveryControllerTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -47,16 +53,55 @@ public function setUp() : void
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_recover_with_invalid_token_returns_validation_error()
|
||||
public function test_recover_fails_if_no_recovery_is_set()
|
||||
{
|
||||
$response = $this->json('POST', '/webauthn/recover', [
|
||||
'token' => 'bad_token',
|
||||
DB::table('webauthn_recoveries')->delete();
|
||||
|
||||
$this->json('POST', '/webauthn/recover', [
|
||||
'token' => self::ACTUAL_TOKEN_VALUE,
|
||||
'email' => $this->user->email,
|
||||
'password' => UserFactory::USER_PASSWORD,
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonMissingValidationErrors('email')
|
||||
->assertJsonValidationErrors('token');
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors('token');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_recover_with_wrong_token_returns_validation_error()
|
||||
{
|
||||
$response = $this->json('POST', '/webauthn/recover', [
|
||||
'token' => 'wrong_token',
|
||||
'email' => $this->user->email,
|
||||
'password' => UserFactory::USER_PASSWORD,
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonMissingValidationErrors('email')
|
||||
->assertJsonValidationErrors('token');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_recover_with_expired_token_returns_validation_error()
|
||||
{
|
||||
Date::setTestNow($now = Date::create(2020, 01, 01, 16, 30));
|
||||
|
||||
DB::table('webauthn_recoveries')->delete();
|
||||
DB::table('webauthn_recoveries')->insert([
|
||||
'token' => self::STORED_TOKEN_VALUE,
|
||||
'email' => $this->user->email,
|
||||
'created_at' => $now->clone()->subHour()->subSecond()->toDateTimeString(),
|
||||
]);
|
||||
|
||||
$this->json('POST', '/webauthn/recover', [
|
||||
'token' => self::ACTUAL_TOKEN_VALUE,
|
||||
'email' => $this->user->email,
|
||||
'password' => UserFactory::USER_PASSWORD,
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors('token');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,12 +109,28 @@ public function test_recover_with_invalid_token_returns_validation_error()
|
||||
*/
|
||||
public function test_recover_with_invalid_password_returns_authentication_error()
|
||||
{
|
||||
$response = $this->json('POST', '/webauthn/recover', [
|
||||
$this->json('POST', '/webauthn/recover', [
|
||||
'token' => self::ACTUAL_TOKEN_VALUE,
|
||||
'email' => $this->user->email,
|
||||
'password' => 'bad_password',
|
||||
])
|
||||
->assertStatus(401);
|
||||
->assertStatus(401);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_recover_returns_validation_error_when_no_user_exists()
|
||||
{
|
||||
$this->json('POST', '/webauthn/recover', [
|
||||
'token' => self::ACTUAL_TOKEN_VALUE,
|
||||
'email' => 'no@user.com',
|
||||
'password' => UserFactory::USER_PASSWORD,
|
||||
])
|
||||
->assertStatus(422)
|
||||
->assertJsonMissingValidationErrors('password')
|
||||
->assertJsonMissingValidationErrors('token')
|
||||
->assertJsonValidationErrors('email');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,7 +143,7 @@ public function test_recover_returns_success()
|
||||
'email' => $this->user->email,
|
||||
'password' => UserFactory::USER_PASSWORD,
|
||||
])
|
||||
->assertStatus(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$this->assertDatabaseMissing('webauthn_recoveries', [
|
||||
'token' => self::STORED_TOKEN_VALUE,
|
||||
@ -119,7 +180,7 @@ public function test_revoke_all_credentials_clear_registered_credentials()
|
||||
'password' => UserFactory::USER_PASSWORD,
|
||||
'revokeAll' => true,
|
||||
])
|
||||
->assertStatus(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$this->assertDatabaseMissing('webauthn_credentials', [
|
||||
'authenticatable_id' => $this->user->id,
|
||||
|
79
tests/Feature/Http/Auth/WebAuthnRegisterControllerTest.php
Normal file
79
tests/Feature/Http/Auth/WebAuthnRegisterControllerTest.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Http\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use Tests\FeatureTestCase;
|
||||
use Laragear\WebAuthn\Http\Requests\AttestedRequest;
|
||||
use Laragear\WebAuthn\Http\Requests\AttestationRequest;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Laragear\WebAuthn\WebAuthn;
|
||||
use Laragear\WebAuthn\JsonTransport;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\Auth\WebAuthnRegisterController
|
||||
*/
|
||||
class WebAuthnRegisterControllerTest extends FeatureTestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_uses_attestation_with_fastRegistration_request(): void
|
||||
{
|
||||
Config::set('webauthn.user_verification', WebAuthn::USER_VERIFICATION_DISCOURAGED);
|
||||
|
||||
$request = $this->mock(AttestationRequest::class);
|
||||
|
||||
$request->expects('fastRegistration')->andReturnSelf();
|
||||
$request->expects('toCreate')->andReturn(new JsonTransport());
|
||||
|
||||
$this->actingAs($this->user, 'web-guard')
|
||||
->json('POST', '/webauthn/register/options')
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_uses_attestation_with_secureRegistration_request(): void
|
||||
{
|
||||
Config::set('webauthn.user_verification', WebAuthn::USER_VERIFICATION_REQUIRED);
|
||||
|
||||
$request = $this->mock(AttestationRequest::class);
|
||||
|
||||
$request->expects('secureRegistration')->andReturnSelf();
|
||||
$request->expects('toCreate')->andReturn(new JsonTransport());
|
||||
|
||||
$this->actingAs($this->user, 'web-guard')
|
||||
->json('POST', '/webauthn/register/options')
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_register_uses_attested_request(): void
|
||||
{
|
||||
$this->mock(AttestedRequest::class)->expects('save')->andReturn();
|
||||
|
||||
$this->actingAs($this->user, 'web-guard')
|
||||
->json('POST', '/webauthn/register')
|
||||
->assertNoContent();
|
||||
}
|
||||
}
|
122
tests/Feature/Http/SystemControllerTest.php
Normal file
122
tests/Feature/Http/SystemControllerTest.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Api\v1\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
||||
use App\Services\ReleaseRadarService;
|
||||
use Tests\FeatureTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Http\Controllers\SystemController
|
||||
*/
|
||||
class SystemControllerTest extends FeatureTestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
|
||||
/**
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_infos_returns_only_base_collection()
|
||||
{
|
||||
$response = $this->json('GET', '/infos')
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'Date',
|
||||
'userAgent',
|
||||
'Version',
|
||||
'Environment',
|
||||
'Debug',
|
||||
'Cache driver',
|
||||
'Log channel',
|
||||
'Log level',
|
||||
'DB driver',
|
||||
'PHP version',
|
||||
'Operating system',
|
||||
'interface',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_infos_returns_full_collection_when_signed_in()
|
||||
{
|
||||
$response = $this->actingAs($this->user, 'api-guard')
|
||||
->json('GET', '/infos')
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'Auth guard',
|
||||
'webauthn user verification',
|
||||
'Trusted proxies',
|
||||
'options' => [
|
||||
'showTokenAsDot',
|
||||
'closeOtpOnCopy',
|
||||
'copyOtpOnDisplay',
|
||||
'useBasicQrcodeReader',
|
||||
'displayMode',
|
||||
'showAccountsIcons',
|
||||
'kickUserAfter',
|
||||
'activeGroup',
|
||||
'rememberActiveGroup',
|
||||
'defaultGroup',
|
||||
'useEncryption',
|
||||
'defaultCaptureMode',
|
||||
'useDirectCapture',
|
||||
'useWebauthnAsDefault',
|
||||
'useWebauthnOnly',
|
||||
'getOfficialIcons',
|
||||
'checkForUpdate',
|
||||
'lastRadarScan',
|
||||
'latestRelease',
|
||||
'lang',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_infos_returns_full_collection_when_signed_in_behind_proxy()
|
||||
{
|
||||
$response = $this->actingAs($this->user, 'reverse-proxy-guard')
|
||||
->json('GET', '/infos')
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'Auth proxy header for user',
|
||||
'Auth proxy header for email',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_latestrelease_runs_manual_scan()
|
||||
{
|
||||
$releaseRadarService = $this->mock(ReleaseRadarService::class)->makePartial();
|
||||
$releaseRadarService->shouldReceive('manualScan')
|
||||
->once()
|
||||
->andReturn('new_release');
|
||||
|
||||
$response = $this->json('GET', '/latestRelease')
|
||||
->assertOk()
|
||||
->assertJson([
|
||||
'newRelease' => 'new_release',
|
||||
]);
|
||||
}
|
||||
}
|
@ -3,8 +3,14 @@
|
||||
namespace Tests\Feature\Models;
|
||||
|
||||
use App\Models\TwoFAccount;
|
||||
use Tests\Classes\OtpTestData;
|
||||
use Tests\Data\OtpTestData;
|
||||
use Tests\FeatureTestCase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Http\Testing\FileFactory;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Helpers\Helpers;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\Data\HttpRequestTestData;
|
||||
|
||||
/**
|
||||
* @covers \App\Models\TwoFAccount
|
||||
@ -21,10 +27,15 @@ class TwoFAccountModelTest extends FeatureTestCase
|
||||
*/
|
||||
protected $customHotpTwofaccount;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const ICON_NAME = 'oDBngpjQaQAgLtHqGuYiPRqftCXv6Sj4hSAXARpA.png';
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -69,12 +80,36 @@ public function setUp() : void
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_fill_with_custom_totp_uri_returns_correct_value()
|
||||
{
|
||||
$this->mock('alias:' . Helpers::class, function (MockInterface $helper) {
|
||||
$helper->shouldReceive('getUniqueFilename')
|
||||
->andReturn(self::ICON_NAME);
|
||||
|
||||
$helper->shouldReceive('isValidImage')
|
||||
->andReturn(true);
|
||||
});
|
||||
|
||||
$file = (new FileFactory)->image(self::ICON_NAME, 10, 10);
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response($file->tempFile, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('imagesLink');
|
||||
Storage::fake('icons');
|
||||
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->fillWithURI(OtpTestData::TOTP_FULL_CUSTOM_URI);
|
||||
|
||||
Storage::disk('icons')->assertExists(self::ICON_NAME);
|
||||
Storage::disk('imagesLink')->assertMissing(self::ICON_NAME);
|
||||
|
||||
$this->assertEquals('totp', $twofaccount->otp_type);
|
||||
$this->assertEquals(OtpTestData::TOTP_FULL_CUSTOM_URI, $twofaccount->legacy_uri);
|
||||
$this->assertEquals(OtpTestData::SERVICE, $twofaccount->service);
|
||||
@ -84,7 +119,7 @@ public function test_fill_with_custom_totp_uri_returns_correct_value()
|
||||
$this->assertEquals(OtpTestData::PERIOD_CUSTOM, $twofaccount->period);
|
||||
$this->assertEquals(null, $twofaccount->counter);
|
||||
$this->assertEquals(OtpTestData::ALGORITHM_CUSTOM, $twofaccount->algorithm);
|
||||
$this->assertStringEndsWith('.png', $twofaccount->icon);
|
||||
$this->assertEquals(self::ICON_NAME, $twofaccount->icon);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,12 +144,36 @@ public function test_fill_with_basic_totp_uri_returns_default_value()
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_fill_with_custom_hotp_uri_returns_correct_value()
|
||||
{
|
||||
$this->mock('alias:' . Helpers::class, function (MockInterface $helper) {
|
||||
$helper->shouldReceive('getUniqueFilename')
|
||||
->andReturn(self::ICON_NAME);
|
||||
|
||||
$helper->shouldReceive('isValidImage')
|
||||
->andReturn(true);
|
||||
});
|
||||
|
||||
$file = (new FileFactory)->image(self::ICON_NAME, 10, 10);
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response($file->tempFile, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('imagesLink');
|
||||
Storage::fake('icons');
|
||||
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->fillWithURI(OtpTestData::HOTP_FULL_CUSTOM_URI);
|
||||
|
||||
Storage::disk('icons')->assertExists(self::ICON_NAME);
|
||||
Storage::disk('imagesLink')->assertMissing(self::ICON_NAME);
|
||||
|
||||
$this->assertEquals('hotp', $twofaccount->otp_type);
|
||||
$this->assertEquals(OtpTestData::HOTP_FULL_CUSTOM_URI, $twofaccount->legacy_uri);
|
||||
$this->assertEquals(OtpTestData::SERVICE, $twofaccount->service);
|
||||
@ -124,7 +183,7 @@ public function test_fill_with_custom_hotp_uri_returns_correct_value()
|
||||
$this->assertEquals(null, $twofaccount->period);
|
||||
$this->assertEquals(OtpTestData::COUNTER_CUSTOM, $twofaccount->counter);
|
||||
$this->assertEquals(OtpTestData::ALGORITHM_CUSTOM, $twofaccount->algorithm);
|
||||
$this->assertStringEndsWith('.png', $twofaccount->icon);
|
||||
$this->assertEquals(self::ICON_NAME, $twofaccount->icon);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -391,9 +450,28 @@ public function test_update_totp_persists_updated_model()
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_getOTP_for_totp_returns_the_same_password()
|
||||
{
|
||||
$this->mock('alias:' . Helpers::class, function (MockInterface $helper) {
|
||||
$helper->shouldReceive('getUniqueFilename')
|
||||
->andReturn(self::ICON_NAME);
|
||||
|
||||
$helper->shouldReceive('isValidImage')
|
||||
->andReturn(true);
|
||||
});
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response(HttpRequestTestData::ICON_PNG, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('imagesLink');
|
||||
Storage::fake('icons');
|
||||
|
||||
$twofaccount = new TwoFAccount;
|
||||
|
||||
$otp_from_model = $this->customTotpTwofaccount->getOTP();
|
||||
@ -413,9 +491,28 @@ public function test_getOTP_for_totp_returns_the_same_password()
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_getOTP_for_hotp_returns_the_same_password()
|
||||
{
|
||||
$this->mock('alias:' . Helpers::class, function (MockInterface $helper) {
|
||||
$helper->shouldReceive('getUniqueFilename')
|
||||
->andReturn(self::ICON_NAME);
|
||||
|
||||
$helper->shouldReceive('isValidImage')
|
||||
->andReturn(true);
|
||||
});
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://en.opensuse.org/images/4/44/Button-filled-colour.png' => Http::response(HttpRequestTestData::ICON_PNG, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('imagesLink');
|
||||
Storage::fake('icons');
|
||||
|
||||
$twofaccount = new TwoFAccount;
|
||||
|
||||
$otp_from_model = $this->customHotpTwofaccount->getOTP();
|
||||
@ -507,4 +604,107 @@ public function test_getURI_for_custom_hotp_model_returns_uri()
|
||||
$this->assertStringContainsString('counter=' . OtpTestData::COUNTER_CUSTOM, $uri);
|
||||
$this->assertStringContainsString('algorithm=' . OtpTestData::ALGORITHM_CUSTOM, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_fill_succeed_when_image_fetching_fails()
|
||||
{
|
||||
$this->mock('alias:' . Helpers::class, function (MockInterface $helper) {
|
||||
$helper->shouldReceive('getUniqueFilename')
|
||||
->andReturn(self::ICON_NAME);
|
||||
});
|
||||
|
||||
Http::preventStrayRequests();
|
||||
|
||||
Storage::fake('imagesLink');
|
||||
Storage::fake('icons');
|
||||
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->fillWithURI(OtpTestData::TOTP_FULL_CUSTOM_URI);
|
||||
|
||||
Storage::disk('icons')->assertMissing(self::ICON_NAME);
|
||||
Storage::disk('imagesLink')->assertMissing(self::ICON_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_saving_totp_without_period_set_default_one()
|
||||
{
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->service = OtpTestData::SERVICE;
|
||||
$twofaccount->account = OtpTestData::ACCOUNT;
|
||||
$twofaccount->otp_type = TwoFAccount::TOTP;
|
||||
$twofaccount->secret = OtpTestData::SECRET;
|
||||
|
||||
$twofaccount->save();
|
||||
|
||||
$account = TwoFAccount::find($twofaccount->id);
|
||||
|
||||
$this->assertEquals(TwoFAccount::DEFAULT_PERIOD, $account->period);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_saving_hotp_without_counter_set_default_one()
|
||||
{
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->service = OtpTestData::SERVICE;
|
||||
$twofaccount->account = OtpTestData::ACCOUNT;
|
||||
$twofaccount->otp_type = TwoFAccount::HOTP;
|
||||
$twofaccount->secret = OtpTestData::SECRET;
|
||||
|
||||
$twofaccount->save();
|
||||
|
||||
$account = TwoFAccount::find($twofaccount->id);
|
||||
|
||||
$this->assertEquals(TwoFAccount::DEFAULT_COUNTER, $account->counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_equals_returns_true()
|
||||
{
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->legacy_uri = OtpTestData::TOTP_FULL_CUSTOM_URI;
|
||||
$twofaccount->service = OtpTestData::SERVICE;
|
||||
$twofaccount->account = OtpTestData::ACCOUNT;
|
||||
$twofaccount->icon = OtpTestData::ICON;
|
||||
$twofaccount->otp_type = 'totp';
|
||||
$twofaccount->secret = OtpTestData::SECRET;
|
||||
$twofaccount->digits = OtpTestData::DIGITS_CUSTOM;
|
||||
$twofaccount->algorithm = OtpTestData::ALGORITHM_CUSTOM;
|
||||
$twofaccount->period = OtpTestData::PERIOD_CUSTOM;
|
||||
$twofaccount->counter = null;
|
||||
$twofaccount->save();
|
||||
|
||||
$this->assertTrue($twofaccount->equals($this->customTotpTwofaccount));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_equals_returns_false()
|
||||
{
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->legacy_uri = OtpTestData::TOTP_FULL_CUSTOM_URI;
|
||||
$twofaccount->service = OtpTestData::SERVICE;
|
||||
$twofaccount->account = OtpTestData::ACCOUNT;
|
||||
$twofaccount->icon = OtpTestData::ICON;
|
||||
$twofaccount->otp_type = 'totp';
|
||||
$twofaccount->secret = OtpTestData::SECRET;
|
||||
$twofaccount->digits = OtpTestData::DIGITS_CUSTOM;
|
||||
$twofaccount->algorithm = OtpTestData::ALGORITHM_CUSTOM;
|
||||
$twofaccount->period = OtpTestData::PERIOD_CUSTOM;
|
||||
$twofaccount->counter = null;
|
||||
$twofaccount->save();
|
||||
|
||||
$this->assertFalse($twofaccount->equals($this->customHotpTwofaccount));
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
/**
|
||||
* @covers \App\Services\GroupService
|
||||
* @covers \App\Facades\Groups
|
||||
*/
|
||||
class GroupServiceTest extends FeatureTestCase
|
||||
{
|
||||
@ -52,7 +53,7 @@ class GroupServiceTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
@ -6,6 +6,9 @@
|
||||
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\Data\HttpRequestTestData;
|
||||
|
||||
/**
|
||||
* @covers \App\Services\LogoService
|
||||
@ -17,7 +20,7 @@ class LogoServiceTest extends TestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
@ -25,18 +28,65 @@ public function setUp() : void
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_getIcon_returns_iconFilename_when_logo_exists()
|
||||
public function test_getIcon_returns_stored_icon_file_when_logo_exists()
|
||||
{
|
||||
$logoServiceMock = $this->partialMock(LogoService::class, function (MockInterface $mock) {
|
||||
$mock->shouldAllowMockingProtectedMethods();
|
||||
$mock->shouldReceive('getLogo', 'copyToIcons')
|
||||
->once()
|
||||
->andReturn('service.svg', true);
|
||||
});
|
||||
$svgLogo = HttpRequestTestData::SVG_LOGO_BODY;
|
||||
$tfaJsonBody = HttpRequestTestData::TFA_JSON_BODY;
|
||||
|
||||
$icon = $logoServiceMock->getIcon('service');
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/*' => Http::response($svgLogo, 200),
|
||||
'https://2fa.directory/api/v3/tfa.json' => Http::response($tfaJsonBody, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('icons');
|
||||
Storage::fake('logos');
|
||||
|
||||
$logoService = new LogoService();
|
||||
$icon = $logoService->getIcon('twitter');
|
||||
|
||||
$this->assertNotNull($icon);
|
||||
Storage::disk('icons')->assertExists($icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_getIcon_returns_null_when_github_request_fails()
|
||||
{
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/*' => Http::response('not found', 404),
|
||||
]);
|
||||
|
||||
Storage::fake('icons');
|
||||
Storage::fake('logos');
|
||||
$logoService = new LogoService();
|
||||
|
||||
$icon = $logoService->getIcon('twitter');
|
||||
|
||||
$this->assertEquals(null, $icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_getIcon_returns_null_when_logo_fetching_fails()
|
||||
{
|
||||
$tfaJsonBody = HttpRequestTestData::TFA_JSON_BODY;
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://2fa.directory/api/v3/tfa.json' => Http::response($tfaJsonBody, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('icons');
|
||||
Storage::fake('logos');
|
||||
$logoService = new LogoService();
|
||||
|
||||
$icon = $logoService->getIcon('twitter');
|
||||
|
||||
$this->assertEquals(null, $icon);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,15 +94,32 @@ public function test_getIcon_returns_iconFilename_when_logo_exists()
|
||||
*/
|
||||
public function test_getIcon_returns_null_when_no_logo_exists()
|
||||
{
|
||||
$logoServiceMock = $this->partialMock(LogoService::class, function (MockInterface $mock) {
|
||||
$mock->shouldAllowMockingProtectedMethods()
|
||||
->shouldReceive('getLogo')
|
||||
->once()
|
||||
->andReturn(null);
|
||||
});
|
||||
$logoService = new LogoService();
|
||||
|
||||
$icon = $logoServiceMock->getIcon('no_logo_should_exists_with_this_name');
|
||||
$icon = $logoService->getIcon('no_logo_should_exists_with_this_name');
|
||||
|
||||
$this->assertEquals(null, $icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_logoService_loads_empty_collection_when_tfajson_fetching_fails()
|
||||
{
|
||||
$svgLogo = HttpRequestTestData::SVG_LOGO_BODY;
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
'https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/*' => Http::response($svgLogo, 200),
|
||||
]);
|
||||
|
||||
Storage::fake('icons');
|
||||
Storage::fake('logos');
|
||||
|
||||
$logoService = new LogoService();
|
||||
$icon = $logoService->getIcon('twitter');
|
||||
|
||||
$this->assertNull($icon);
|
||||
Storage::disk('logos')->assertMissing(LogoService::TFA_JSON);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
/**
|
||||
* @covers \App\Services\QrCodeService
|
||||
* @covers \App\Facades\QrCode
|
||||
*/
|
||||
class QrCodeServiceTest extends FeatureTestCase
|
||||
{
|
||||
@ -20,7 +21,7 @@ class QrCodeServiceTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
138
tests/Feature/Services/ReleaseRadarServiceTest.php
Normal file
138
tests/Feature/Services/ReleaseRadarServiceTest.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Services;
|
||||
|
||||
use App\Facades\Settings;
|
||||
use App\Services\ReleaseRadarService;
|
||||
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
||||
use Tests\FeatureTestCase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\Data\HttpRequestTestData;
|
||||
|
||||
/**
|
||||
* @covers \App\Services\ReleaseRadarService
|
||||
*/
|
||||
class ReleaseRadarServiceTest extends FeatureTestCase
|
||||
{
|
||||
use WithoutMiddleware;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_manualScan_returns_no_new_release()
|
||||
{
|
||||
$url = config('2fauth.latestReleaseUrl');
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
$url => Http::response(HttpRequestTestData::LATEST_RELEASE_BODY_NO_NEW_RELEASE, 200),
|
||||
]);
|
||||
|
||||
$releaseRadarService = new ReleaseRadarService();
|
||||
$release = $releaseRadarService->manualScan();
|
||||
|
||||
$this->assertFalse($release);
|
||||
$this->assertDatabaseHas('options', [
|
||||
'key' => 'lastRadarScan',
|
||||
]);
|
||||
$this->assertDatabaseMissing('options', [
|
||||
'key' => 'latestRelease',
|
||||
'value' => HttpRequestTestData::TAG_NAME
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_manualScan_returns_new_release()
|
||||
{
|
||||
$url = config('2fauth.latestReleaseUrl');
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
$url => Http::response(HttpRequestTestData::LATEST_RELEASE_BODY_NEW_RELEASE, 200),
|
||||
]);
|
||||
|
||||
$releaseRadarService = new ReleaseRadarService();
|
||||
$release = $releaseRadarService->manualScan();
|
||||
|
||||
$this->assertEquals(HttpRequestTestData::NEW_TAG_NAME, $release);
|
||||
$this->assertDatabaseHas('options', [
|
||||
'key' => 'latestRelease',
|
||||
'value' => HttpRequestTestData::NEW_TAG_NAME
|
||||
]);
|
||||
$this->assertDatabaseHas('options', [
|
||||
'key' => 'lastRadarScan',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_manualScan_succeed_when_something_fails()
|
||||
{
|
||||
$url = config('2fauth.latestReleaseUrl');
|
||||
|
||||
// We do not fake the http request so an exception will be thrown
|
||||
Http::preventStrayRequests();
|
||||
|
||||
$releaseRadarService = new ReleaseRadarService();
|
||||
$release = $releaseRadarService->manualScan();
|
||||
|
||||
$this->assertFalse($release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_manualScan_succeed_when_github_is_unreachable()
|
||||
{
|
||||
$url = config('2fauth.latestReleaseUrl');
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
$url => Http::response(null, 400),
|
||||
]);
|
||||
|
||||
$releaseRadarService = new ReleaseRadarService();
|
||||
$release = $releaseRadarService->manualScan();
|
||||
|
||||
$this->assertFalse($release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_scheduleScan_runs_after_one_week()
|
||||
{
|
||||
$url = config('2fauth.latestReleaseUrl');
|
||||
|
||||
Http::preventStrayRequests();
|
||||
Http::fake([
|
||||
$url => Http::response(HttpRequestTestData::LATEST_RELEASE_BODY_NEW_RELEASE, 200),
|
||||
]);
|
||||
|
||||
Settings::set('lastRadarScan', time() - (60 * 60 * 24 * 7) - 1);
|
||||
|
||||
$releaseRadarService = $this->mock(ReleaseRadarService::class)->makePartial();
|
||||
$releaseRadarService->shouldAllowMockingProtectedMethods()
|
||||
->shouldReceive('newRelease')
|
||||
->once();
|
||||
|
||||
$releaseRadarService->scheduledScan();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_scheduleScan_does_not_run_before_one_week()
|
||||
{
|
||||
Settings::set('lastRadarScan', time() - (60 * 60 * 24 * 7) + 2);
|
||||
|
||||
$releaseRadarService = $this->mock(ReleaseRadarService::class)->makePartial();
|
||||
$releaseRadarService->shouldAllowMockingProtectedMethods()
|
||||
->shouldNotReceive('newRelease');
|
||||
|
||||
$releaseRadarService->scheduledScan();
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
|
||||
/**
|
||||
* @covers \App\Services\SettingService
|
||||
* @covers \App\Facades\Settings
|
||||
*/
|
||||
class SettingServiceTest extends FeatureTestCase
|
||||
{
|
||||
@ -57,7 +58,7 @@ class SettingServiceTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -238,7 +239,7 @@ public function test_set_useEncryption_off_returns_exception_when_data_are_undec
|
||||
/**
|
||||
* Provide invalid data for validation test
|
||||
*/
|
||||
public function provideUndecipherableData() : array
|
||||
public function provideUndecipherableData(): array
|
||||
{
|
||||
return [
|
||||
[[
|
||||
@ -316,4 +317,26 @@ public function test_del_remove_setting_from_db()
|
||||
self::VALUE => self::SETTING_VALUE_STRING,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_isUserDefined_returns_true()
|
||||
{
|
||||
DB::table('options')->insert(
|
||||
[self::KEY => 'showTokenAsDot', self::VALUE => strval(self::SETTING_VALUE_TRUE_TRANSFORMED)]
|
||||
);
|
||||
|
||||
$this->assertTrue(Settings::isUserDefined('showTokenAsDot'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_isUserDefined_returns_false()
|
||||
{
|
||||
DB::table('options')->where(self::KEY, 'showTokenAsDot')->delete();
|
||||
|
||||
$this->assertFalse(Settings::isUserDefined('showTokenAsDot'));
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,13 @@
|
||||
use App\Facades\TwoFAccounts;
|
||||
use App\Models\Group;
|
||||
use App\Models\TwoFAccount;
|
||||
use Tests\Classes\OtpTestData;
|
||||
use Tests\Data\OtpTestData;
|
||||
use Tests\FeatureTestCase;
|
||||
use Tests\Data\MigrationTestData;
|
||||
|
||||
/**
|
||||
* @covers \App\Services\TwoFAccountService
|
||||
* @covers \App\Facades\TwoFAccounts
|
||||
*/
|
||||
class TwoFAccountServiceTest extends FeatureTestCase
|
||||
{
|
||||
@ -31,7 +33,7 @@ class TwoFAccountServiceTest extends FeatureTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
@ -179,7 +181,7 @@ public function test_delete_single_id()
|
||||
*/
|
||||
public function test_convert_migration_from_gauth_returns_correct_accounts()
|
||||
{
|
||||
$twofaccounts = TwoFAccounts::migrate(OtpTestData::GOOGLE_AUTH_MIGRATION_URI);
|
||||
$twofaccounts = TwoFAccounts::migrate(MigrationTestData::GOOGLE_AUTH_MIGRATION_URI);
|
||||
|
||||
$this->assertCount(2, $twofaccounts);
|
||||
|
||||
@ -226,7 +228,7 @@ public function test_convert_migration_from_gauth_returns_flagged_duplicates()
|
||||
$twofaccount = new TwoFAccount;
|
||||
$twofaccount->fillWithOtpParameters($parameters)->save();
|
||||
|
||||
$twofaccounts = TwoFAccounts::migrate(OtpTestData::GOOGLE_AUTH_MIGRATION_URI);
|
||||
$twofaccounts = TwoFAccounts::migrate(MigrationTestData::GOOGLE_AUTH_MIGRATION_URI);
|
||||
|
||||
$this->assertEquals(-1, $twofaccounts->first()->id);
|
||||
$this->assertEquals(-1, $twofaccounts->last()->id);
|
||||
@ -238,6 +240,6 @@ public function test_convert_migration_from_gauth_returns_flagged_duplicates()
|
||||
public function test_convert_invalid_migration_from_gauth_returns_InvalidMigrationData_exception()
|
||||
{
|
||||
$this->expectException(\App\Exceptions\InvalidMigrationDataException::class);
|
||||
$twofaccounts = TwoFAccounts::migrate(OtpTestData::GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA);
|
||||
$twofaccounts = TwoFAccounts::migrate(MigrationTestData::GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,15 @@
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Tests\TestCase;
|
||||
use App\Exceptions\InvalidOtpParameterException;
|
||||
use \App\Exceptions\InvalidQrCodeException;
|
||||
use App\Exceptions\InvalidSecretException;
|
||||
use App\Exceptions\DbEncryptionException;
|
||||
use App\Exceptions\InvalidMigrationDataException;
|
||||
use App\Exceptions\UndecipherableException;
|
||||
use App\Exceptions\UnsupportedMigrationException;
|
||||
use App\Exceptions\UnsupportedOtpTypeException;
|
||||
use App\Exceptions\EncryptedMigrationException;
|
||||
|
||||
/**
|
||||
* @covers \App\Exceptions\Handler
|
||||
@ -41,32 +50,35 @@ public function test_exceptions_returns_badRequest_json_response($exception)
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideExceptionsforBadRequest() : array
|
||||
public function provideExceptionsforBadRequest(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'\App\Exceptions\InvalidOtpParameterException',
|
||||
InvalidOtpParameterException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\InvalidQrCodeException',
|
||||
InvalidQrCodeException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\InvalidSecretException',
|
||||
InvalidSecretException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\DbEncryptionException',
|
||||
DbEncryptionException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\InvalidMigrationDataException',
|
||||
InvalidMigrationDataException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\UndecipherableException',
|
||||
UndecipherableException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\UnsupportedMigrationException',
|
||||
UnsupportedMigrationException::class,
|
||||
],
|
||||
[
|
||||
'\App\Exceptions\UnsupportedOtpTypeException',
|
||||
UnsupportedOtpTypeException::class,
|
||||
],
|
||||
[
|
||||
EncryptedMigrationException::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
@ -99,7 +111,7 @@ public function test_exceptions_returns_notFound_json_response($exception)
|
||||
/**
|
||||
* Provide Valid data for validation test
|
||||
*/
|
||||
public function provideExceptionsforNotFound() : array
|
||||
public function provideExceptionsforNotFound(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
@ -111,6 +123,32 @@ public function provideExceptionsforNotFound() : array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_authenticationException_returns_unauthorized_json_response()
|
||||
{
|
||||
$request = $this->createMock(Request::class);
|
||||
$instance = new Handler($this->createMock(Container::class));
|
||||
$class = new \ReflectionClass(Handler::class);
|
||||
|
||||
$method = $class->getMethod('render');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$mockException = $this->createMock(\Illuminate\Auth\AuthenticationException::class);
|
||||
$mockException->method('guards')->willReturn(['web-guard']);
|
||||
|
||||
$response = $method->invokeArgs($instance, [$request, $mockException]);
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
|
||||
$response = \Illuminate\Testing\TestResponse::fromBaseResponse($response);
|
||||
$response->assertStatus(401)
|
||||
->assertJsonStructure([
|
||||
'message',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
|
98
tests/Unit/HelpersTest.php
Normal file
98
tests/Unit/HelpersTest.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Helpers\Helpers;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Helpers\Helpers
|
||||
*/
|
||||
class HelpersTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_getUniqueFilename_returns_filename()
|
||||
{
|
||||
$ext = 'jpg';
|
||||
$filename = Helpers::getUniqueFilename($ext);
|
||||
|
||||
$this->assertIsString($filename);
|
||||
$this->assertStringEndsWith('.' . $ext, $filename);
|
||||
$this->assertEquals(41 + strlen($ext), strlen($filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider versionNumberProvider
|
||||
*/
|
||||
public function test_cleanVersionNumber_returns_cleaned_version($dirtyVersion, $expected)
|
||||
{
|
||||
$cleanedVersion = Helpers::cleanVersionNumber($dirtyVersion);
|
||||
|
||||
$this->assertEquals($expected, $cleanedVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for cleanVersionNumber() tests
|
||||
*/
|
||||
public function versionNumberProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'v3.2.1',
|
||||
'3.2.1',
|
||||
],
|
||||
[
|
||||
'v3.2.1-beta',
|
||||
'3.2.1-beta',
|
||||
],
|
||||
[
|
||||
'v3.0.1-alpha+001',
|
||||
'3.0.1-alpha+001',
|
||||
],
|
||||
[
|
||||
'version03.0.1 alpha+001',
|
||||
'3.0.1',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider invalidVersionNumberProvider
|
||||
*/
|
||||
public function test_cleanVersionNumber_returns_false_with_invalid_semver($dirtyVersion)
|
||||
{
|
||||
$cleanedVersion = Helpers::cleanVersionNumber($dirtyVersion);
|
||||
|
||||
$this->assertEquals(false, $cleanedVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for cleanVersionNumber() tests
|
||||
*/
|
||||
public function invalidVersionNumberProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'v3.2.',
|
||||
],
|
||||
[
|
||||
'v3..1-beta',
|
||||
],
|
||||
[
|
||||
'v.0.1-alpha+001',
|
||||
],
|
||||
[
|
||||
'3.00.1 alpha+001',
|
||||
],
|
||||
[
|
||||
'3.00.1 alpha+001',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -16,6 +16,9 @@
|
||||
*/
|
||||
class CleanIconStorageTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_it_deletes_icon_file_on_twofaccount_deletion()
|
||||
{
|
||||
$settingService = $this->mock(SettingService::class, function (MockInterface $settingService) {
|
||||
@ -34,6 +37,9 @@ public function test_it_deletes_icon_file_on_twofaccount_deletion()
|
||||
$this->assertNull($listener->handle($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_CleanIconStorage_listen_to_TwoFAccountDeleted_event()
|
||||
{
|
||||
Event::fake();
|
||||
|
@ -5,23 +5,41 @@
|
||||
use App\Events\GroupDeleting;
|
||||
use App\Listeners\DissociateTwofaccountFromGroup;
|
||||
use App\Models\Group;
|
||||
use App\Models\TwoFAccount;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
use Mockery\MockInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\Listeners\DissociateTwofaccountFromGroup
|
||||
*/
|
||||
class DissociateTwofaccountFromGroupTest extends TestCase
|
||||
{
|
||||
// public function test_twofaccount_is_released_on_group_deletion()
|
||||
// {
|
||||
// $group = Group::factory()->make();
|
||||
// $event = new GroupDeleting($group);
|
||||
// $listener = new DissociateTwofaccountFromGroup();
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_twofaccount_is_released_on_group_deletion()
|
||||
{
|
||||
|
||||
// $this->assertNull($listener->handle($event));
|
||||
// }
|
||||
$this->mock('alias:' . TwoFAccount::class, function (MockInterface $twoFAccount) {
|
||||
$twoFAccount->shouldReceive('where->update')
|
||||
->once()
|
||||
->andReturn(1);
|
||||
});
|
||||
|
||||
$group = Group::factory()->make();
|
||||
$event = new GroupDeleting($group);
|
||||
$listener = new DissociateTwofaccountFromGroup();
|
||||
|
||||
$this->assertNull($listener->handle($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_DissociateTwofaccountFromGroup_listen_to_groupDeleting_event()
|
||||
{
|
||||
Event::fake();
|
||||
|
484
tests/Unit/MigratorTest.php
Normal file
484
tests/Unit/MigratorTest.php
Normal file
@ -0,0 +1,484 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Exceptions\EncryptedMigrationException;
|
||||
use App\Factories\MigratorFactory;
|
||||
use App\Exceptions\InvalidMigrationDataException;
|
||||
use App\Models\TwoFAccount;
|
||||
use App\Services\Migrators\AegisMigrator;
|
||||
use App\Services\Migrators\TwoFASMigrator;
|
||||
use App\Services\Migrators\Migrator;
|
||||
use App\Services\Migrators\PlainTextMigrator;
|
||||
use App\Services\Migrators\GoogleAuthMigrator;
|
||||
use App\Services\SettingService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Mockery;
|
||||
use Mockery\Mock;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\Data\MigrationTestData;
|
||||
use Tests\Data\OtpTestData;
|
||||
use Tests\TestCase;
|
||||
use ParagonIE\ConstantTime\Base32;
|
||||
use App\Protobuf\GoogleAuth\Payload\Algorithm;
|
||||
use App\Exceptions\UnsupportedMigrationException;
|
||||
|
||||
|
||||
/**
|
||||
* @covers \App\Providers\MigrationServiceProvider
|
||||
* @covers \App\Factories\MigratorFactory
|
||||
* @covers \App\Services\Migrators\Migrator
|
||||
* @covers \App\Services\Migrators\AegisMigrator
|
||||
* @covers \App\Services\Migrators\TwoFASMigrator
|
||||
* @covers \App\Services\Migrators\PlainTextMigrator
|
||||
* @covers \App\Services\Migrators\GoogleAuthMigrator
|
||||
* @uses \App\Models\TwoFAccount
|
||||
*/
|
||||
class MigratorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* App\Models\TwoFAccount $totpTwofaccount
|
||||
*/
|
||||
protected $totpTwofaccount;
|
||||
|
||||
/**
|
||||
* App\Models\TwoFAccount $totpTwofaccount
|
||||
*/
|
||||
protected $hotpTwofaccount;
|
||||
|
||||
/**
|
||||
* App\Models\TwoFAccount $steamTwofaccount
|
||||
*/
|
||||
protected $steamTwofaccount;
|
||||
|
||||
/**
|
||||
* App\Models\TwoFAccount $GAuthTotpTwofaccount
|
||||
*/
|
||||
protected $GAuthTotpTwofaccount;
|
||||
|
||||
/**
|
||||
* App\Models\TwoFAccount $GAuthTotpBisTwofaccount
|
||||
*/
|
||||
protected $GAuthTotpBisTwofaccount;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->mock(SettingService::class, function (MockInterface $settingService) {
|
||||
$settingService->allows()
|
||||
->get('useEncryption')
|
||||
->andReturn(false);
|
||||
|
||||
$settingService->allows()
|
||||
->get('getOfficialIcons')
|
||||
->andReturn(false);
|
||||
});
|
||||
|
||||
$this->totpTwofaccount = new TwoFAccount;
|
||||
$this->totpTwofaccount->legacy_uri = OtpTestData::TOTP_FULL_CUSTOM_URI_NO_IMG;
|
||||
$this->totpTwofaccount->service = OtpTestData::SERVICE;
|
||||
$this->totpTwofaccount->account = OtpTestData::ACCOUNT;
|
||||
$this->totpTwofaccount->icon = null;
|
||||
$this->totpTwofaccount->otp_type = 'totp';
|
||||
$this->totpTwofaccount->secret = OtpTestData::SECRET;
|
||||
$this->totpTwofaccount->digits = OtpTestData::DIGITS_CUSTOM;
|
||||
$this->totpTwofaccount->algorithm = OtpTestData::ALGORITHM_CUSTOM;
|
||||
$this->totpTwofaccount->period = OtpTestData::PERIOD_CUSTOM;
|
||||
$this->totpTwofaccount->counter = null;
|
||||
|
||||
$this->hotpTwofaccount = new TwoFAccount;
|
||||
$this->hotpTwofaccount->legacy_uri = OtpTestData::HOTP_FULL_CUSTOM_URI_NO_IMG;
|
||||
$this->hotpTwofaccount->service = OtpTestData::SERVICE;
|
||||
$this->hotpTwofaccount->account = OtpTestData::ACCOUNT;
|
||||
$this->hotpTwofaccount->icon = null;
|
||||
$this->hotpTwofaccount->otp_type = 'hotp';
|
||||
$this->hotpTwofaccount->secret = OtpTestData::SECRET;
|
||||
$this->hotpTwofaccount->digits = OtpTestData::DIGITS_CUSTOM;
|
||||
$this->hotpTwofaccount->algorithm = OtpTestData::ALGORITHM_CUSTOM;
|
||||
$this->hotpTwofaccount->period = null;
|
||||
$this->hotpTwofaccount->counter = OtpTestData::COUNTER_CUSTOM;
|
||||
|
||||
$this->steamTwofaccount = new TwoFAccount;
|
||||
$this->steamTwofaccount->legacy_uri = OtpTestData::STEAM_TOTP_URI;
|
||||
$this->steamTwofaccount->service = OtpTestData::STEAM;
|
||||
$this->steamTwofaccount->account = OtpTestData::ACCOUNT;
|
||||
$this->steamTwofaccount->icon = null;
|
||||
$this->steamTwofaccount->otp_type = 'steamtotp';
|
||||
$this->steamTwofaccount->secret = OtpTestData::STEAM_SECRET;
|
||||
$this->steamTwofaccount->digits = OtpTestData::DIGITS_STEAM;
|
||||
$this->steamTwofaccount->algorithm = OtpTestData::ALGORITHM_DEFAULT;
|
||||
$this->steamTwofaccount->period = OtpTestData::PERIOD_DEFAULT;
|
||||
$this->steamTwofaccount->counter = null;
|
||||
|
||||
$this->GAuthTotpTwofaccount = new TwoFAccount;
|
||||
$this->GAuthTotpTwofaccount->service = OtpTestData::SERVICE;
|
||||
$this->GAuthTotpTwofaccount->account = OtpTestData::ACCOUNT;
|
||||
$this->GAuthTotpTwofaccount->icon = null;
|
||||
$this->GAuthTotpTwofaccount->otp_type = 'totp';
|
||||
$this->GAuthTotpTwofaccount->secret = OtpTestData::SECRET;
|
||||
$this->GAuthTotpTwofaccount->digits = OtpTestData::DIGITS_DEFAULT;
|
||||
$this->GAuthTotpTwofaccount->algorithm = OtpTestData::ALGORITHM_DEFAULT;
|
||||
$this->GAuthTotpTwofaccount->period = OtpTestData::PERIOD_DEFAULT;
|
||||
$this->GAuthTotpTwofaccount->counter = null;
|
||||
|
||||
$this->GAuthTotpBisTwofaccount = new TwoFAccount;
|
||||
$this->GAuthTotpBisTwofaccount->service = OtpTestData::SERVICE . '_bis';
|
||||
$this->GAuthTotpBisTwofaccount->account = OtpTestData::ACCOUNT . '_bis';
|
||||
$this->GAuthTotpBisTwofaccount->icon = null;
|
||||
$this->GAuthTotpBisTwofaccount->otp_type = 'totp';
|
||||
$this->GAuthTotpBisTwofaccount->secret = OtpTestData::SECRET;
|
||||
$this->GAuthTotpBisTwofaccount->digits = OtpTestData::DIGITS_DEFAULT;
|
||||
$this->GAuthTotpBisTwofaccount->algorithm = OtpTestData::ALGORITHM_DEFAULT;
|
||||
$this->GAuthTotpBisTwofaccount->period = OtpTestData::PERIOD_DEFAULT;
|
||||
$this->GAuthTotpBisTwofaccount->counter = null;
|
||||
|
||||
$this->fakeTwofaccount = new TwoFAccount;
|
||||
$this->fakeTwofaccount->id = TwoFAccount::FAKE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider validMigrationsProvider
|
||||
*/
|
||||
public function test_migrate_returns_consistent_accounts(Migrator $migrator, mixed $payload, string $expected, bool $hasSteam)
|
||||
{
|
||||
$accounts = $migrator->migrate($payload);
|
||||
|
||||
if ($expected === 'gauth') {
|
||||
$totp = $this->GAuthTotpTwofaccount;
|
||||
$hotp = $this->GAuthTotpBisTwofaccount;
|
||||
} else {
|
||||
$totp = $this->totpTwofaccount;
|
||||
$hotp = $this->hotpTwofaccount;
|
||||
if ($hasSteam) {
|
||||
$steam = $this->steamTwofaccount;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertContainsOnlyInstancesOf(TwoFAccount::class, $accounts);
|
||||
$this->assertCount($hasSteam ? 3 : 2, $accounts);
|
||||
|
||||
// The returned collection could have non-linear index (because of possible blank lines
|
||||
// in the migration payload) so we do not use get() to retrieve items
|
||||
$this->assertObjectEquals($totp, $accounts->first());
|
||||
$this->assertObjectEquals($hotp, $accounts->slice(1, 1)->first());
|
||||
if ($hasSteam) {
|
||||
$this->assertObjectEquals($steam, $accounts->last());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for TwoFAccount store tests
|
||||
*/
|
||||
public function validMigrationsProvider()
|
||||
{
|
||||
return [
|
||||
'PLAIN_TEXT_PAYLOAD' => [
|
||||
new PlainTextMigrator(),
|
||||
MigrationTestData::VALID_PLAIN_TEXT_PAYLOAD,
|
||||
'custom',
|
||||
$hasSteam = true
|
||||
],
|
||||
'PLAIN_TEXT_PAYLOAD_WITH_INTRUDER' => [
|
||||
new PlainTextMigrator(),
|
||||
MigrationTestData::VALID_PLAIN_TEXT_PAYLOAD_WITH_INTRUDER,
|
||||
'custom',
|
||||
$hasSteam = true
|
||||
],
|
||||
'AEGIS_JSON_MIGRATION_PAYLOAD' => [
|
||||
new AegisMigrator(),
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD,
|
||||
'custom',
|
||||
$hasSteam = true
|
||||
],
|
||||
'2FAS_MIGRATION_PAYLOAD' => [
|
||||
new TwoFASMigrator(),
|
||||
MigrationTestData::VALID_2FAS_MIGRATION_PAYLOAD,
|
||||
'custom',
|
||||
$hasSteam = false
|
||||
],
|
||||
'GOOGLE_AUTH_MIGRATION_PAYLOAD' => [
|
||||
new GoogleAuthMigrator(),
|
||||
MigrationTestData::GOOGLE_AUTH_MIGRATION_URI,
|
||||
'gauth',
|
||||
$hasSteam = false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider invalidMigrationsProvider
|
||||
*/
|
||||
public function test_migrate_with_invalid_payload_returns_InvalidMigrationDataException(Migrator $migrator, mixed $payload)
|
||||
{
|
||||
$this->expectException(InvalidMigrationDataException::class);
|
||||
|
||||
$accounts = $migrator->migrate($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for TwoFAccount store tests
|
||||
*/
|
||||
public function invalidMigrationsProvider()
|
||||
{
|
||||
return [
|
||||
'INVALID_PLAIN_TEXT_NO_URI' => [
|
||||
new PlainTextMigrator(),
|
||||
MigrationTestData::INVALID_PLAIN_TEXT_NO_URI,
|
||||
],
|
||||
'INVALID_PLAIN_TEXT_ONLY_EMPTY_LINES' => [
|
||||
new PlainTextMigrator(),
|
||||
MigrationTestData::INVALID_PLAIN_TEXT_ONLY_EMPTY_LINES,
|
||||
],
|
||||
'INVALID_PLAIN_TEXT_NULL' => [
|
||||
new PlainTextMigrator(),
|
||||
null,
|
||||
],
|
||||
'INVALID_PLAIN_TEXT_EMPTY_STRING' => [
|
||||
new PlainTextMigrator(),
|
||||
'',
|
||||
],
|
||||
'INVALID_PLAIN_TEXT_INT' => [
|
||||
new PlainTextMigrator(),
|
||||
10,
|
||||
],
|
||||
'INVALID_PLAIN_TEXT_BOOL' => [
|
||||
new PlainTextMigrator(),
|
||||
true,
|
||||
],
|
||||
'INVALID_AEGIS_JSON_MIGRATION_PAYLOAD' => [
|
||||
new AegisMigrator(),
|
||||
MigrationTestData::INVALID_AEGIS_JSON_MIGRATION_PAYLOAD,
|
||||
],
|
||||
'ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD' => [
|
||||
new AegisMigrator(),
|
||||
MigrationTestData::ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD,
|
||||
],
|
||||
'INVALID_2FAS_MIGRATION_PAYLOAD' => [
|
||||
new TwoFASMigrator(),
|
||||
MigrationTestData::INVALID_2FAS_MIGRATION_PAYLOAD,
|
||||
],
|
||||
'INVALID_GOOGLE_AUTH_MIGRATION_URI' => [
|
||||
new GoogleAuthMigrator(),
|
||||
MigrationTestData::INVALID_GOOGLE_AUTH_MIGRATION_URI,
|
||||
],
|
||||
'GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA' => [
|
||||
new GoogleAuthMigrator(),
|
||||
MigrationTestData::GOOGLE_AUTH_MIGRATION_URI_WITH_INVALID_DATA,
|
||||
],
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider migrationWithInvalidAccountsProvider
|
||||
*/
|
||||
public function test_migrate_returns_fake_accounts(Migrator $migrator, mixed $payload)
|
||||
{
|
||||
$accounts = $migrator->migrate($payload);
|
||||
|
||||
$this->assertContainsOnlyInstancesOf(TwoFAccount::class, $accounts);
|
||||
$this->assertCount(2, $accounts);
|
||||
|
||||
// The returned collection could have non-linear index (because of possible blank lines
|
||||
// in the migration payload) so we do not use get() to retrieve items
|
||||
$this->assertObjectEquals($this->totpTwofaccount, $accounts->first());
|
||||
$this->assertEquals($this->fakeTwofaccount->id, $accounts->last()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for TwoFAccount store tests
|
||||
*/
|
||||
public function migrationWithInvalidAccountsProvider()
|
||||
{
|
||||
return [
|
||||
'PLAIN_TEXT_PAYLOAD_WITH_INVALID_URI' => [
|
||||
new PlainTextMigrator(),
|
||||
MigrationTestData::PLAIN_TEXT_PAYLOAD_WITH_INVALID_URI,
|
||||
],
|
||||
'VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_OTP_TYPE' => [
|
||||
new AegisMigrator(),
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_OTP_TYPE,
|
||||
],
|
||||
'VALID_2FAS_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_OTP_TYPE' => [
|
||||
new TwoFASMigrator(),
|
||||
MigrationTestData::VALID_2FAS_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_OTP_TYPE,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_migrate_gauth_returns_fake_accounts()
|
||||
{
|
||||
$this->mock('alias:' . Base32::class, function (MockInterface $baseEncoder) {
|
||||
$baseEncoder->shouldReceive('encodeUpper')
|
||||
->andThrow(new \Exception());
|
||||
});
|
||||
|
||||
$migrator = new GoogleAuthMigrator();
|
||||
$accounts = $migrator->migrate(MigrationTestData::GOOGLE_AUTH_MIGRATION_URI);
|
||||
|
||||
$this->assertContainsOnlyInstancesOf(TwoFAccount::class, $accounts);
|
||||
$this->assertCount(2, $accounts);
|
||||
|
||||
// The returned collection could have non-linear index (because of possible blank lines
|
||||
// in the migration payload) so we do not use get() to retrieve items
|
||||
$this->assertEquals($this->fakeTwofaccount->id, $accounts->first()->id);
|
||||
$this->assertEquals($this->fakeTwofaccount->id, $accounts->last()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider AegisWithIconMigrationProvider
|
||||
*/
|
||||
public function test_migrate_aegis_payload_with_icon_sets_and_stores_the_icon($migration)
|
||||
{
|
||||
Storage::fake('icons');
|
||||
|
||||
$migrator = new AegisMigrator();
|
||||
$accounts = $migrator->migrate($migration);
|
||||
|
||||
$this->assertContainsOnlyInstancesOf(TwoFAccount::class, $accounts);
|
||||
$this->assertCount(1, $accounts);
|
||||
|
||||
Storage::disk('icons')->assertExists($accounts->first()->icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for TwoFAccount store tests
|
||||
*/
|
||||
public function AegisWithIconMigrationProvider()
|
||||
{
|
||||
return [
|
||||
'SVG' => [
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_SVG_ICON,
|
||||
],
|
||||
'PNG' => [
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_PNG_ICON,
|
||||
],
|
||||
'JPG' => [
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_JPG_ICON,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_migrate_aegis_payload_with_unsupported_icon_does_not_fail()
|
||||
{
|
||||
Storage::fake('icons');
|
||||
|
||||
$migrator = new AegisMigrator();
|
||||
$accounts = $migrator->migrate(MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_ICON);
|
||||
|
||||
$this->assertContainsOnlyInstancesOf(TwoFAccount::class, $accounts);
|
||||
$this->assertCount(1, $accounts);
|
||||
|
||||
$this->assertNull($this->fakeTwofaccount->icon);
|
||||
Storage::disk('icons')->assertDirectoryEmpty('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider factoryProvider
|
||||
*/
|
||||
public function test_factory_returns_plain_text_migrator($payload, $migratorClass)
|
||||
{
|
||||
$factory = new MigratorFactory();
|
||||
|
||||
$migrator = $factory->create($payload);
|
||||
|
||||
$this->assertInstanceOf($migratorClass, $migrator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for TwoFAccount store tests
|
||||
*/
|
||||
public function factoryProvider()
|
||||
{
|
||||
return [
|
||||
'VALID_PLAIN_TEXT_PAYLOAD' => [
|
||||
MigrationTestData::VALID_PLAIN_TEXT_PAYLOAD,
|
||||
PlainTextMigrator::class,
|
||||
],
|
||||
'VALID_AEGIS_JSON_MIGRATION_PAYLOAD' => [
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD,
|
||||
AegisMigrator::class,
|
||||
],
|
||||
'VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_ICON' => [
|
||||
MigrationTestData::VALID_AEGIS_JSON_MIGRATION_PAYLOAD_WITH_UNSUPPORTED_ICON,
|
||||
AegisMigrator::class,
|
||||
],
|
||||
'VALID_2FAS_MIGRATION_PAYLOAD' => [
|
||||
MigrationTestData::VALID_2FAS_MIGRATION_PAYLOAD,
|
||||
TwoFASMigrator::class,
|
||||
],
|
||||
'GOOGLE_AUTH_MIGRATION_URI' => [
|
||||
MigrationTestData::GOOGLE_AUTH_MIGRATION_URI,
|
||||
GoogleAuthMigrator::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function test_factory_throw_UnsupportedMigrationException()
|
||||
{
|
||||
$this->expectException(UnsupportedMigrationException::class);
|
||||
$factory = new MigratorFactory();
|
||||
|
||||
$migrator = $factory->create('not_a_valid_payload');
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*
|
||||
* @dataProvider encryptedMigrationDataProvider
|
||||
*/
|
||||
public function test_factory_throw_EncryptedMigrationException($payload)
|
||||
{
|
||||
$this->expectException(EncryptedMigrationException::class);
|
||||
|
||||
$factory = new MigratorFactory();
|
||||
|
||||
$migrator = $factory->create($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide data for TwoFAccount store tests
|
||||
*/
|
||||
public function encryptedMigrationDataProvider()
|
||||
{
|
||||
return [
|
||||
'ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD' => [
|
||||
MigrationTestData::ENCRYPTED_AEGIS_JSON_MIGRATION_PAYLOAD
|
||||
],
|
||||
'ENCRYPTED_2FAS_MIGRATION_PAYLOAD' => [
|
||||
MigrationTestData::ENCRYPTED_2FAS_MIGRATION_PAYLOAD
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user