Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
04c772d2f1
|
|||
0b7958ddb9
|
|||
e4e27c8952
|
|||
20bb53c215
|
|||
d61dcda7fb
|
|||
08e58b1b88
|
|||
dec4b3cebd
|
|||
c63724c064
|
|||
018e91bc0c
|
|||
10d8fddd88
|
|||
cc0e0fb32b
|
|||
291c99e4e5
|
|||
462bb34447
|
|||
028fc05d54
|
|||
e11b93e300
|
|||
e72f365a29
|
|||
161787d26f
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -4,13 +4,14 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased](https://git.d3data.de/D3Public/webauthn/compare/2.0.0.1...rel_2.x)
|
||||
## [Unreleased](https://git.d3data.de/D3Public/webauthn/compare/1.0.0.0...rel_1.x)
|
||||
|
||||
## [1.0.0.0](https://git.d3data.de/D3Public/webauthn/releases/tag/1.0.0.0) - 2019-08-19
|
||||
## [1.0.0.0](https://git.d3data.de/D3Public/webauthn/compare/0.1.0.0...1.0.0.0) - 2023-05-25
|
||||
### Added
|
||||
- 2-factor authentication for logins in front- and backend in addition to username and password
|
||||
- Activation and setup possible in the front and back end
|
||||
- Authentication is shown for user accounts that have this enabled - otherwise the usual default login.
|
||||
- Access can be set up in the Auth app by scannable QR code or copyable character string
|
||||
- Validation of one-time passwords and generation of QR codes are only carried out within the shop - no communication to the outside necessary
|
||||
- static backup codes also allow (limited) login without access to the generation tool
|
||||
- make installable in OXID 6.5.2
|
||||
|
||||
## [0.1.0.0](https://git.d3data.de/D3Public/webauthn/releases/tag/0.1.0.0) - 2023-02-18
|
||||
### Added
|
||||
- Key management in front and back end
|
||||
- FIDO2 / passkey as password alternative for login in front- and backend - password as fallback
|
||||
- compatible with our 2FA one-time password module (https://packagist.org/packages/d3/oxid-twofactor-onetimepassword from version 2.1.0.0)
|
||||
|
16
README.en.md
16
README.en.md
@ -1,13 +1,15 @@
|
||||
[](README.md)
|
||||
[](README.en.md)
|
||||
|
||||
# D³ Passwordless login for OXID eShop
|
||||
# Passwordless login for OXID eShop
|
||||
|
||||
With this module, the login in the OXID shop can be carried out with a hardware token instead of a password (WebAuthn / FIDO2 based).
|
||||

|
||||
|
||||
With this module, the login in the OXID shop can be carried out with a hardware based login key (WebAuthn / FIDO2 based passkey) instead of a password.
|
||||
|
||||
This secures the login in the frontend and (if allowed for the user) also in the backend.
|
||||
|
||||
Security keys are devices that contain cryptographic keys. These can be used for two-factor authentication. The security key must support the standard "[WebAuthn](https://w3c.github.io/webauthn/#webauthn-authenticator)".
|
||||
Login keys are from devices that contain cryptographic keys. These can be used for two-factor authentication. The login key device must support the standard "[WebAuthn](https://w3c.github.io/webauthn/#webauthn-authenticator)".
|
||||
|
||||
The key management is done in the admin area and in the user's "My Account".
|
||||
|
||||
@ -63,12 +65,14 @@ and its requirements.
|
||||
|
||||
The Flow and Wave themes are supported by default. Other themes may require customisation.
|
||||
|
||||
## Module installation
|
||||
## Module installation / update
|
||||
|
||||
Open a command line interface and navigate to the shop root directory (parent of source and vendor). Execute the following command. Adapt the paths to your environment.
|
||||
Open a command line interface and navigate to the shop root directory (parent of source and vendor). Execute the following commands. Adapt the paths to your environment.
|
||||
|
||||
```bash
|
||||
php composer require d3/oxid-twofactor-passwordless:^1.0
|
||||
composer require d3/oxid-twofactor-passwordless:^1.0
|
||||
|
||||
./vendor/bin/oe-eshop-db_migrate migrations:migrate d3webauthn
|
||||
```
|
||||
|
||||
If a reference to an unsuitable package `symfony/process` is shown, this must be changed. To do this, please add the switch `-W` to the above command (`... require -W ...`).
|
||||
|
16
README.md
16
README.md
@ -1,13 +1,15 @@
|
||||
[](README.md)
|
||||
[](README.en.md)
|
||||
|
||||
# D³ Passwortloses Anmelden für OXID eShop
|
||||
# Passwortloses Anmelden für OXID eShop
|
||||
|
||||
Mit diesem Modul kann die Anmeldung im OXID-Shop mit einem Hardwaretoken anstelle eines Passworts durchgeführt werden (WebAuthn / FIDO2 basiert).
|
||||

|
||||
|
||||
Mit diesem Modul kann die Anmeldung im OXID-Shop mit einem hardwarebasierten Anmeldeschlüssel (WebAuthn / FIDO2 basierter passkey) anstelle eines Passworts durchgeführt werden.
|
||||
|
||||
Hierbei wird die Anmeldung im Frontend und (sofern für den Benutzer erlaubt) auch im Backend gesichert.
|
||||
|
||||
Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard "[WebAuthn](https://w3c.github.io/webauthn/#webauthn-authenticator)" unterstützen.
|
||||
Anmeldeschlüssel stammen von Geräten, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Das Anmeldeschlüsselgerät muss den Standard "[WebAuthn](https://w3c.github.io/webauthn/#webauthn-authenticator)" unterstützen.
|
||||
|
||||
Die Schlüsselverwaltung erfolgt im Adminbereich sowie im "Mein Konto" des Benutzers.
|
||||
|
||||
@ -63,12 +65,14 @@ und dessen Anforderungen.
|
||||
|
||||
Im Standard wird das Flow- und Wave-Theme unterstützt. Andere Themes können Anpassungen erfordern.
|
||||
|
||||
## Modulinstallation
|
||||
## Modulinstallation / -update
|
||||
|
||||
Öffnen Sie eine Kommandozeile und navigieren Sie zum Stammverzeichnis des Shops (Elternverzeichnis von source und vendor). Führen Sie den folgenden Befehl aus. Passen Sie die Pfadangaben an Ihre Installationsumgebung an.
|
||||
Öffnen Sie eine Kommandozeile und navigieren Sie zum Stammverzeichnis des Shops (Elternverzeichnis von source und vendor). Führen Sie die folgenden Befehle aus. Passen Sie die Pfadangaben an Ihre Installationsumgebung an.
|
||||
|
||||
```bash
|
||||
php composer require d3/oxid-twofactor-passwordless:^1.0
|
||||
composer require d3/oxid-twofactor-passwordless:^1.0
|
||||
|
||||
./vendor/bin/oe-eshop-db_migrate migrations:migrate d3webauthn
|
||||
```
|
||||
|
||||
Wird ein Hinweis auf ein unpassendes Paket "symfony/process" gezeigt, muss dieses geändert werden. Fügen Sie dazu in den oben genannten Befehl bitte den Schalter `-W` ein (`... require -W ...`).
|
||||
|
@ -18,7 +18,9 @@
|
||||
"token",
|
||||
"yubikey",
|
||||
"solokey",
|
||||
"credential"
|
||||
"credential",
|
||||
"login",
|
||||
"passkey"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
@ -40,7 +42,7 @@
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"oxid-esales/oxideshop-ce": "6.8 - 6.13",
|
||||
"oxid-esales/oxideshop-ce": "6.8 - 6.14",
|
||||
"web-auth/webauthn-lib": "^3.3",
|
||||
"nyholm/psr7": "^1.5.1",
|
||||
"nyholm/psr7-server": "^1.0.2",
|
||||
@ -55,7 +57,8 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"D3\\Webauthn\\": "../../../source/modules/d3/oxwebauthn"
|
||||
"D3\\Webauthn\\": "../../../source/modules/d3/oxwebauthn",
|
||||
"D3\\Webauthn\\Migrations\\": "../../../source/modules/d3/oxwebauthn/migration/data"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
|
@ -15,7 +15,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\Application\Controller\Admin;
|
||||
|
||||
use Assert\Assert;
|
||||
use Assert\AssertionFailedException;
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
|
||||
use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList;
|
||||
@ -83,7 +85,7 @@ class d3user_webauthn extends AdminDetailsController
|
||||
try {
|
||||
$this->setPageType('requestnew');
|
||||
$this->setAuthnRegister();
|
||||
} catch (Exception|ContainerExceptionInterface|NotFoundExceptionInterface|DoctrineDriverException $e) {
|
||||
} catch (AssertionFailedException|ContainerExceptionInterface|NotFoundExceptionInterface|DoctrineDriverException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class)->addErrorToDisplay($e->getMessage());
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getMessage(), ['UserId' => $this->getEditObjectId()]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
@ -107,12 +109,15 @@ class d3user_webauthn extends AdminDetailsController
|
||||
}
|
||||
|
||||
$credential = Registry::getRequest()->getRequestEscapedParameter('credential');
|
||||
if (strlen((string) $credential)) {
|
||||
Assert::that($credential)->minLength(1, 'Credential should not be empty.');
|
||||
|
||||
$keyname = Registry::getRequest()->getRequestEscapedParameter('keyname');
|
||||
Assert::that($keyname)->minLength(1, 'Key name should not be empty.');
|
||||
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($credential);
|
||||
/** @var Webauthn $webauthn */
|
||||
$webauthn = d3GetOxidDIC()->get(Webauthn::class);
|
||||
$webauthn->saveAuthn($credential, Registry::getRequest()->getRequestEscapedParameter('keyname'));
|
||||
}
|
||||
$webauthn->saveAuthn($credential, $keyname);
|
||||
} catch (WebauthnException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getDetailedErrorMessage(), ['UserId' => $this->getEditObjectId()]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
@ -136,11 +141,14 @@ class d3user_webauthn extends AdminDetailsController
|
||||
/**
|
||||
* @throws DoctrineDriverException
|
||||
* @throws DoctrineException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setAuthnRegister(): void
|
||||
{
|
||||
/** @var Webauthn $authn */
|
||||
$authn = d3GetOxidDIC()->get(Webauthn::class);
|
||||
|
||||
/** @var User $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$user->load($this->getEditObjectId());
|
||||
$publicKeyCredentialCreationOptions = $authn->getCreationOptions($user);
|
||||
@ -165,6 +173,7 @@ class d3user_webauthn extends AdminDetailsController
|
||||
*/
|
||||
public function getCredentialList($userId): array
|
||||
{
|
||||
/** @var User $oUser */
|
||||
$oUser = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$oUser->load($userId);
|
||||
|
||||
|
@ -15,6 +15,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\Application\Controller\Admin;
|
||||
|
||||
use Assert\Assert;
|
||||
use Assert\AssertionFailedException;
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
|
||||
use D3\Webauthn\Application\Model\Webauthn;
|
||||
@ -26,7 +29,6 @@ use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||
use Doctrine\DBAL\Exception as DoctrineException;
|
||||
use OxidEsales\Eshop\Application\Controller\Admin\AdminController;
|
||||
use OxidEsales\Eshop\Application\Controller\FrontendController;
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
use OxidEsales\Eshop\Core\Request;
|
||||
use OxidEsales\Eshop\Core\Routing\ControllerClassNameResolver;
|
||||
use OxidEsales\Eshop\Core\Session;
|
||||
@ -112,6 +114,13 @@ class d3webauthnadminlogin extends AdminController
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getDetailedErrorMessage(), ['UserId' => $userId]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=login');
|
||||
} catch (AssertionFailedException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->setVariable(WebauthnConf::GLOBAL_SWITCH, true);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class)->addErrorToDisplay($e);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getMessage(), ['UserId' => $userId]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=login');
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,10 +131,10 @@ class d3webauthnadminlogin extends AdminController
|
||||
{
|
||||
try {
|
||||
$login = $this->getWebAuthnLogin();
|
||||
return $login->adminLogin(
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Request::class)->getRequestEscapedParameter('profile')
|
||||
);
|
||||
} catch (WebauthnGetException $e) {
|
||||
$profile = d3GetOxidDIC()->get('d3ox.webauthn.'.Request::class)->getRequestEscapedParameter('profile');
|
||||
Assert::that($profile)->string();
|
||||
return $login->adminLogin($profile);
|
||||
} catch (WebauthnGetException|AssertionFailedException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class)->addErrorToDisplay($e);
|
||||
return 'login';
|
||||
}
|
||||
@ -165,16 +174,19 @@ class d3webauthnadminlogin extends AdminController
|
||||
|
||||
/**
|
||||
* @return WebauthnLogin
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function getWebAuthnLogin(): WebauthnLogin
|
||||
{
|
||||
/** @var Request $request */
|
||||
$request = d3GetOxidDIC()->get('d3ox.webauthn.'.Request::class);
|
||||
|
||||
return oxNew(
|
||||
WebauthnLogin::class,
|
||||
$request->getRequestEscapedParameter('credential'),
|
||||
$request->getRequestEscapedParameter('error')
|
||||
);
|
||||
$credential = $request->getRequestEscapedParameter('credential');
|
||||
$error = $request->getRequestEscapedParameter('error');
|
||||
|
||||
Assert::that($credential)->string('credential value expected to be string');
|
||||
Assert::that($error)->string('error value expected to be string');
|
||||
|
||||
return oxNew(WebauthnLogin::class, $credential, $error);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\Application\Controller;
|
||||
|
||||
use Assert\Assert;
|
||||
use Assert\AssertionFailedException;
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Controller\Traits\accountTrait;
|
||||
use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
|
||||
@ -86,6 +88,10 @@ class d3_account_webauthn extends AccountController
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getDetailedErrorMessage(), ['UserId: ' => $this->getUser()->getId()]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class)->addErrorToDisplay($e);
|
||||
} catch (AssertionFailedException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getMessage(), ['UserId: ' => $this->getUser()->getId()]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class)->addErrorToDisplay($e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +109,7 @@ class d3_account_webauthn extends AccountController
|
||||
* @throws DoctrineException
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws InvalidArgumentException
|
||||
* @return void
|
||||
*/
|
||||
public function setAuthnRegister(): void
|
||||
@ -137,11 +144,10 @@ class d3_account_webauthn extends AccountController
|
||||
}
|
||||
|
||||
$credential = d3GetOxidDIC()->get('d3ox.webauthn.'.Request::class)->getRequestEscapedParameter('credential');
|
||||
if (strlen((string) $credential)) {
|
||||
Assert::that($credential)->minLength(1, 'Credential should not be empty.');
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($credential);
|
||||
$webauthn = d3GetOxidDIC()->get(Webauthn::class);
|
||||
$webauthn->saveAuthn($credential, d3GetOxidDIC()->get('d3ox.webauthn.'.Request::class)->getRequestEscapedParameter('keyname'));
|
||||
}
|
||||
} catch (WebauthnException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error(
|
||||
$e->getDetailedErrorMessage(),
|
||||
|
@ -15,6 +15,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\Application\Controller;
|
||||
|
||||
use Assert\AssertionFailedException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Model\Webauthn;
|
||||
use D3\Webauthn\Application\Model\WebauthnConf;
|
||||
@ -103,6 +104,13 @@ class d3webauthnlogin extends FrontendController
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
Registry::getUtilsView()->addErrorToDisplay($e);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=start');
|
||||
} catch (AssertionFailedException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->setVariable(WebauthnConf::GLOBAL_SWITCH, true);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->error($e->getMessage(), ['UserId' => $userId]);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($e->getTraceAsString());
|
||||
Registry::getUtilsView()->addErrorToDisplay($e->getMessage());
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=start');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,9 @@ namespace D3\Webauthn\Application\Model\Credential;
|
||||
|
||||
use Assert\Assert;
|
||||
use Assert\AssertionFailedException;
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Setup\Actions;
|
||||
use D3\Webauthn\Migrations\Version20230209212939;
|
||||
use DateTime;
|
||||
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||
use Doctrine\DBAL\Exception as DoctrineException;
|
||||
@ -75,7 +76,7 @@ class PublicKeyCredential extends BaseModel
|
||||
|
||||
Assert::that($encodedCID)
|
||||
->maxLength(
|
||||
Actions::FIELDLENGTH_CREDID,
|
||||
Version20230209212939::FIELDLENGTH_CREDID,
|
||||
'the credentialId (%3$d) does not fit into the database field (%2$d)'
|
||||
);
|
||||
|
||||
@ -85,11 +86,16 @@ class PublicKeyCredential extends BaseModel
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getCredentialId(): ?string
|
||||
{
|
||||
return base64_decode($this->__get($this->_getFieldLongName('credentialid'))->rawValue) ?: null;
|
||||
$encodedCID = $this->__get($this->_getFieldLongName('credentialid'))->rawValue;
|
||||
|
||||
Assert::that($encodedCID)->base64('Credential ID "%s" is not a valid base64 string.');
|
||||
|
||||
return base64_decode($encodedCID);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,7 +126,7 @@ class PublicKeyCredential extends BaseModel
|
||||
|
||||
Assert::that($encodedCredential)
|
||||
->maxLength(
|
||||
Actions::FIELDLENGTH_CREDENTIAL,
|
||||
Version20230209212939::FIELDLENGTH_CREDENTIAL,
|
||||
'the credential source (%3$d) does not fit into the database field (%2$d)',
|
||||
);
|
||||
|
||||
|
@ -15,7 +15,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\Application\Model;
|
||||
|
||||
use Assert\Assert;
|
||||
use Assert\AssertionFailedException;
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
|
||||
use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList;
|
||||
@ -24,7 +26,6 @@ use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
|
||||
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
|
||||
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||
use Doctrine\DBAL\Exception as DoctrineException;
|
||||
use Exception;
|
||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||
use Nyholm\Psr7Server\ServerRequestCreator;
|
||||
use OxidEsales\Eshop\Application\Model\User;
|
||||
@ -76,6 +77,7 @@ class Webauthn
|
||||
* @throws DoctrineDriverException
|
||||
* @throws DoctrineException
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getCreationOptions(User $user): string
|
||||
{
|
||||
@ -94,9 +96,7 @@ class Webauthn
|
||||
|
||||
$json = $this->jsonEncode($publicKeyCredentialCreationOptions);
|
||||
|
||||
if ($json === false) {
|
||||
throw oxNew(Exception::class, "can't encode creation options");
|
||||
}
|
||||
Assert::that($json)->isJsonString("can't encode request options");
|
||||
|
||||
return $json;
|
||||
}
|
||||
@ -134,6 +134,7 @@ class Webauthn
|
||||
* @return string
|
||||
* @throws DoctrineDriverException
|
||||
* @throws DoctrineException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getRequestOptions(string $userId): string
|
||||
{
|
||||
@ -143,11 +144,16 @@ class Webauthn
|
||||
d3GetOxidDIC()->set(UserEntity::class.'.args.user', $user);
|
||||
/** @var UserEntity $userEntity */
|
||||
$userEntity = d3GetOxidDIC()->get(UserEntity::class);
|
||||
$existingCredentials = $this->getExistingCredentials($userEntity);
|
||||
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug(
|
||||
'found user credentials: '.count($existingCredentials).' for ID '.$userId
|
||||
);
|
||||
|
||||
// We generate the set of options.
|
||||
$publicKeyCredentialRequestOptions = $this->getServer()->generatePublicKeyCredentialRequestOptions(
|
||||
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, // Default value
|
||||
$this->getExistingCredentials($userEntity)
|
||||
$existingCredentials
|
||||
);
|
||||
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
@ -155,9 +161,11 @@ class Webauthn
|
||||
|
||||
$json = $this->jsonEncode($publicKeyCredentialRequestOptions);
|
||||
|
||||
if ($json === false) {
|
||||
throw oxNew(Exception::class, "can't encode request options");
|
||||
}
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug(
|
||||
'request options: '.$json
|
||||
);
|
||||
|
||||
Assert::that($json)->isJsonString("can't encode request options");
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ namespace D3\Webauthn\Application\Model;
|
||||
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use OxidEsales\Eshop\Core\Language;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class WebauthnErrors
|
||||
{
|
||||
@ -38,6 +39,10 @@ class WebauthnErrors
|
||||
*/
|
||||
public function translateError(string $msg, string $type = null): string
|
||||
{
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug(
|
||||
'error occured: '.$msg
|
||||
);
|
||||
|
||||
$lang = d3GetOxidDIC()->get('d3ox.webauthn.'.Language::class);
|
||||
$type = $type ? '_'.$type : null;
|
||||
|
||||
|
@ -110,10 +110,11 @@ class WebauthnLogin
|
||||
{
|
||||
/** @var UtilsView $myUtilsView */
|
||||
$myUtilsView = d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class);
|
||||
|
||||
try {
|
||||
/** @var d3_User_Webauthn $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$userId = null;
|
||||
|
||||
try {
|
||||
$userId = $this->getUserId();
|
||||
|
||||
$this->handleErrorMessage();
|
||||
@ -158,10 +159,11 @@ class WebauthnLogin
|
||||
{
|
||||
/** @var UtilsView $myUtilsView */
|
||||
$myUtilsView = d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class);
|
||||
|
||||
try {
|
||||
/** @var d3_User_Webauthn $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$userId = null;
|
||||
|
||||
try {
|
||||
$userId = $this->getUserId();
|
||||
|
||||
$this->handleErrorMessage();
|
||||
@ -195,8 +197,6 @@ class WebauthnLogin
|
||||
|
||||
$user->logout();
|
||||
$oStr = Str::getStr();
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Config::class)->getActiveView()
|
||||
->addTplParam('user', $oStr->htmlspecialchars($userId));
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Config::class)->getActiveView()
|
||||
->addTplParam('profile', $oStr->htmlspecialchars($selectedProfile));
|
||||
|
||||
|
@ -20,15 +20,15 @@ $aLang = [
|
||||
'charset' => 'UTF-8',
|
||||
|
||||
'PAGE_TITLE_D3WEBAUTHNLOGIN' => 'Passwortloses Anmelden',
|
||||
'D3_WEBAUTHN_ACCOUNT' => 'Meine Schlüssel',
|
||||
'PAGE_TITLE_D3_ACCOUNT_WEBAUTHN' => 'Meine Schlüssel',
|
||||
'D3_WEBAUTHN_ACCOUNT' => 'Meine Anmeldeschlüssel',
|
||||
'PAGE_TITLE_D3_ACCOUNT_WEBAUTHN' => 'Meine Anmeldeschlüssel',
|
||||
'D3_WEBAUTHN_ACCOUNT_DESC' => 'Verwalten Sie hier Ihre Anmeldeschlüssel.',
|
||||
'D3_WEBAUTHN_ACC_REGISTERNEW' => 'neue Registrierung erstellen',
|
||||
'D3_WEBAUTHN_ACC_ADDKEY' => 'Sicherheitsschlüssel hinzufügen',
|
||||
'D3_WEBAUTHN_ACC_ADDKEY' => 'Anmeldeschlüssel hinzufügen',
|
||||
|
||||
'D3_WEBAUTHN_ACC_REGISTEREDKEYS' => 'registrierte Schlüssel',
|
||||
'D3_WEBAUTHN_ACC_REGISTEREDKEYS' => 'registrierte Anmeldeschlüssel',
|
||||
|
||||
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Hardwareschlüssel authentisieren.',
|
||||
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Anmeldeschlüssel authentisieren.',
|
||||
'WEBAUTHN_CANCEL_LOGIN' => 'Anmeldung abbrechen',
|
||||
'D3_WEBAUTHN_BREADCRUMB' => 'Passwortloses Anmelden',
|
||||
'D3_WEBAUTHN_CONFIRMATION' => 'Bestätigung erforderlich',
|
||||
@ -37,15 +37,15 @@ $aLang = [
|
||||
'D3_WEBAUTHN_DELETE' => 'Löschen',
|
||||
'D3_WEBAUTHN_DELETE_CONFIRM' => 'Soll der Schlüssel wirklich gelöscht werden?',
|
||||
'D3_WEBAUTHN_KEYNAME' => 'Name des Schlüssels',
|
||||
'D3_WEBAUTHN_NOKEYREGISTERED' => 'kein Schlüssel registriert',
|
||||
'D3_WEBAUTHN_NOKEYREGISTERED' => 'kein Anmeldeschlüssel registriert',
|
||||
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE0' => 'nur Passwort',
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE1' => 'nur Auth-Schlüssel',
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE2' => 'nur Auth-Sschlüssel, Passwort als Alternative',
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE3' => 'Auth-Schlüssel und Passwort in Kombination',
|
||||
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'Die Verwendung von Sicherheitsschlüsseln ist nur bei lokalen oder gesicherten Verbindungen (https) möglich.',
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Die Anmeldung mit Sicherheitsschlüssel ist aus technischen Gründen derzeit leider nicht möglich. Bitte verwenden Sie statt dessen Ihr Passwort.',
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'Die Verwendung von Anmeldeschlüsseln ist nur bei lokalen oder gesicherten Verbindungen (https) möglich.',
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Die Anmeldung mit Anmeldeschlüssel ist aus technischen Gründen derzeit leider nicht möglich. Bitte verwenden Sie statt dessen Ihr Passwort.',
|
||||
'D3_WEBAUTHN_ERR_NOTLOADEDUSER' => "Kann keine Anmeldedaten von nicht geladenem Kundenkonto beziehen.",
|
||||
'D3_WEBAUTHN_ERR_NOTCREDENTIALNOTSAVEABLE' => "Der Schlüssel kann aus technischen Gründen nicht registriert werden. Bitte wenden Sie sich an den Shopbetreiber.",
|
||||
'D3_WEBAUTHN_ERR_NOTCREDENTIALNOTSAVEABLE' => "Der Anmeldeschlüssel kann aus technischen Gründen nicht registriert werden. Bitte wenden Sie sich an den Shopbetreiber.",
|
||||
];
|
||||
|
@ -20,15 +20,15 @@ $aLang = [
|
||||
'charset' => 'UTF-8',
|
||||
|
||||
'PAGE_TITLE_D3WEBAUTHNLOGIN' => 'Passwordless login',
|
||||
'D3_WEBAUTHN_ACCOUNT' => 'My keys',
|
||||
'PAGE_TITLE_D3_ACCOUNT_WEBAUTHN' => 'My keys',
|
||||
'D3_WEBAUTHN_ACCOUNT' => 'My login keys',
|
||||
'PAGE_TITLE_D3_ACCOUNT_WEBAUTHN' => 'My login keys',
|
||||
'D3_WEBAUTHN_ACCOUNT_DESC' => 'Manage your login keys here.',
|
||||
'D3_WEBAUTHN_ACC_REGISTERNEW' => 'create new registration',
|
||||
'D3_WEBAUTHN_ACC_ADDKEY' => 'add security key',
|
||||
'D3_WEBAUTHN_ACC_ADDKEY' => 'add login key',
|
||||
|
||||
'D3_WEBAUTHN_ACC_REGISTEREDKEYS' => 'registered keys',
|
||||
'D3_WEBAUTHN_ACC_REGISTEREDKEYS' => 'registered login keys',
|
||||
|
||||
'WEBAUTHN_INPUT_HELP' => 'Please authenticate with hardware key.',
|
||||
'WEBAUTHN_INPUT_HELP' => 'Please authenticate with login key.',
|
||||
'WEBAUTHN_CANCEL_LOGIN' => 'Cancel login',
|
||||
'D3_WEBAUTHN_BREADCRUMB' => 'Passwordless login',
|
||||
'D3_WEBAUTHN_CONFIRMATION' => 'Confirmation required',
|
||||
@ -37,15 +37,15 @@ $aLang = [
|
||||
'D3_WEBAUTHN_DELETE' => 'delete',
|
||||
'D3_WEBAUTHN_DELETE_CONFIRM' => 'Do you really want to delete the key?',
|
||||
'D3_WEBAUTHN_KEYNAME' => 'name of the key',
|
||||
'D3_WEBAUTHN_NOKEYREGISTERED' => 'no key registered',
|
||||
'D3_WEBAUTHN_NOKEYREGISTERED' => 'no login key registered',
|
||||
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE0' => 'password only',
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE1' => 'auth keys only',
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE2' => 'auth keys only, password as an alternative',
|
||||
'D3_WEBAUTHN_ACCOUNT_TYPE3' => 'auth key and password combined',
|
||||
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'The use of security keys is only possible with local or secured connections (https).',
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Unfortunately, logging in with a security key is currently not possible for technical reasons. Please use your password instead.',
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'The use of login keys is only possible with local or secured connections (https).',
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Unfortunately, logging in with a login key is currently not possible for technical reasons. Please use your password instead.',
|
||||
'D3_WEBAUTHN_ERR_NOTLOADEDUSER' => "Cannot obtain login data from unloaded customer account.",
|
||||
'D3_WEBAUTHN_ERR_NOTCREDENTIALNOTSAVEABLE' => "The key cannot be registered for technical reasons. Please contact the shop operator.",
|
||||
'D3_WEBAUTHN_ERR_NOTCREDENTIALNOTSAVEABLE' => "The login key cannot be registered for technical reasons. Please contact the shop operator.",
|
||||
];
|
||||
|
@ -18,35 +18,35 @@ $sLangName = "Deutsch";
|
||||
$aLang = [
|
||||
'charset' => 'UTF-8',
|
||||
|
||||
'D3_WEBAUTHN_ERROR_UNVALID' => 'Der verwendete Schlüssel ist ungültig oder kann nicht geprüft werden.',
|
||||
'D3_WEBAUTHN_ERROR_UNVALID' => 'Der verwendete Anmeldeschlüssel ist ungültig oder kann nicht geprüft werden.',
|
||||
'D3_WEBAUTHN_ERROR_MISSINGPKC' => 'Keine prüfbaren Anfrageoptionen gespeichert. Bitte führen Sie die Anmeldung noch einmal durch bzw. wenden sich an den Betreiber.',
|
||||
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Hardwareschlüssel authentisieren.',
|
||||
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Anmeldeschlüssel authentisieren.',
|
||||
'WEBAUTHN_CANCEL_LOGIN' => 'Anmeldung abbrechen',
|
||||
'D3WEBAUTHN_CONF_BROWSER_REQUEST' => 'Bitte die Anfrage des Browsers bestätigen:',
|
||||
'D3WEBAUTHN_CANCEL' => 'Abbrechen',
|
||||
'D3WEBAUTHN_DELETE' => 'Löschen',
|
||||
'D3WEBAUTHN_DELETE_CONFIRM' => 'Soll der Schlüssel wirklich gelöscht werden?',
|
||||
'D3WEBAUTHN_CANCELNOKEYREGISTERED' => 'kein Schlüssel registriert',
|
||||
'D3WEBAUTHN_DELETE_CONFIRM' => 'Soll der Anmeldeschlüssel wirklich gelöscht werden?',
|
||||
'D3WEBAUTHN_CANCELNOKEYREGISTERED' => 'kein Anmeldeschlüssel registriert',
|
||||
|
||||
'd3mxuser_webauthn' => 'Hardwareschlüssel',
|
||||
'd3mxuser_webauthn' => 'Anmeldeschlüssel',
|
||||
|
||||
'D3_WEBAUTHN_REGISTERNEW' => 'neue Registrierung erstellen',
|
||||
'D3_WEBAUTHN_ADDKEY' => 'Sicherheitsschlüssel hinzufügen',
|
||||
'D3_WEBAUTHN_ADDKEY' => 'Anmeldeschlüssel hinzufügen',
|
||||
'D3_WEBAUTHN_KEYNAME' => 'Name des Schlüssels',
|
||||
|
||||
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registrierte Schlüssel',
|
||||
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registrierte Anmeldeschlüssel',
|
||||
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'Die Verwendung von Sicherheitsschlüsseln ist nur bei lokalen oder gesicherten Verbindungen (https) möglich.',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_CREATE => 'Der Schlüssel vom Token kann nicht oder nicht mehr verwendet werden. Möglicherweise wurde dieser in Ihrem Konto schon einmal gespeichert.',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_GET => 'Der Schlüssel kann nicht validiert werden.',
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'Die Verwendung von Anmeldeschlüsseln ist nur bei lokalen oder gesicherten Verbindungen (https) möglich.',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_CREATE => 'Der AnmeldeSchlüssel kann nicht oder nicht mehr verwendet werden. Möglicherweise wurde dieser in Ihrem Konto schon einmal gespeichert.',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_GET => 'Der Anmeldeschlüssel kann nicht validiert werden.',
|
||||
'D3_WEBAUTHN_ERR_NOTALLOWED' => 'Die Anfrage wurde vom Browser oder der Plattform nicht zugelassen. Möglicherweise fehlen Berechtigungen oder die Zeit ist abgelaufen.',
|
||||
'D3_WEBAUTHN_ERR_ABORT' => 'Die Aktion wurde vom Browser oder der Plattform abgebrochen.',
|
||||
'D3_WEBAUTHN_ERR_CONSTRAINT' => 'Die Aktion konnte vom authentisierenden Gerät nicht durchgeführt werden.',
|
||||
'D3_WEBAUTHN_ERR_NOTSUPPORTED' => 'Die Aktion wird nicht unterstützt.',
|
||||
'D3_WEBAUTHN_ERR_UNKNOWN' => 'Die Aktion wurde wegen eines unbekannten Fehlers abgebrochen.',
|
||||
'D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT' => 'Ihr Browser unterstützt die Verwendung von Hardwareschlüsseln leider nicht.',
|
||||
'D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT' => 'Ihr Browser unterstützt die Verwendung von Anmeldeschlüsseln leider nicht.',
|
||||
'D3_WEBAUTHN_ERR_TECHNICALERROR' => 'Beim Prüfen der Zugangsdaten ist ein technischer Fehler aufgetreten.',
|
||||
'D3_WEBAUTHN_ERR_NOTLOADEDUSER' => "Kann keine Anmeldedaten von nicht geladenem Kundenkonto beziehen.",
|
||||
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Die Anmeldung mit Sicherheitsschlüssel ist aus technischen Gründen derzeit leider nicht möglich. Bitte verwenden Sie statt dessen Ihr Passwort.',
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Die Anmeldung mit Anmeldeschlüssel ist aus technischen Gründen derzeit leider nicht möglich. Bitte verwenden Sie statt dessen Ihr Passwort.',
|
||||
];
|
||||
|
@ -18,35 +18,35 @@ $sLangName = "English";
|
||||
$aLang = [
|
||||
'charset' => 'UTF-8',
|
||||
|
||||
'D3_WEBAUTHN_ERROR_UNVALID' => 'The key used is invalid or cannot be checked.',
|
||||
'D3_WEBAUTHN_ERROR_UNVALID' => 'The used login key is invalid or cannot be checked.',
|
||||
'D3_WEBAUTHN_ERROR_MISSINGPKC' => 'No verifiable request options saved. Please perform the registration again or contact the operator.',
|
||||
'WEBAUTHN_INPUT_HELP' => 'Please authenticate with hardware key.',
|
||||
'WEBAUTHN_INPUT_HELP' => 'Please authenticate with login key.',
|
||||
'WEBAUTHN_CANCEL_LOGIN' => 'Cancel login',
|
||||
'D3WEBAUTHN_CONF_BROWSER_REQUEST' => 'Please confirm the browser request:',
|
||||
'D3WEBAUTHN_CANCEL' => 'Cancel',
|
||||
'D3WEBAUTHN_DELETE' => 'Delete',
|
||||
'D3WEBAUTHN_DELETE_CONFIRM' => 'Do you really want to delete the key?',
|
||||
'D3WEBAUTHN_CANCELNOKEYREGISTERED' => 'No key registered',
|
||||
'D3WEBAUTHN_DELETE_CONFIRM' => 'Do you really want to delete the login key?',
|
||||
'D3WEBAUTHN_CANCELNOKEYREGISTERED' => 'No login key registered',
|
||||
|
||||
'd3mxuser_webauthn' => 'hardware key',
|
||||
'd3mxuser_webauthn' => 'login keys',
|
||||
|
||||
'D3_WEBAUTHN_REGISTERNEW' => 'create new registration',
|
||||
'D3_WEBAUTHN_ADDKEY' => 'add security key',
|
||||
'D3_WEBAUTHN_ADDKEY' => 'add login key',
|
||||
'D3_WEBAUTHN_KEYNAME' => 'Key name',
|
||||
|
||||
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registered keys',
|
||||
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registered login keys',
|
||||
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'The use of security keys is only possible with local or secure connections (https).',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_CREATE => 'The key from the token cannot be used or can no longer be used. It may have been stored in your account before.',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_GET => 'The key cannot be validated.',
|
||||
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'The use of login keys is only possible with local or secure connections (https).',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_CREATE => 'The login key from the token cannot be used or can no longer be used. It may have been stored in your account before.',
|
||||
'D3_WEBAUTHN_ERR_INVALIDSTATE_'.WebauthnConf::TYPE_GET => 'The login key cannot be validated.',
|
||||
'D3_WEBAUTHN_ERR_NOTALLOWED' => 'The request was not allowed by the browser or the platform. Possibly permissions are missing or the time has expired.',
|
||||
'D3_WEBAUTHN_ERR_ABORT' => 'The action was aborted by the browser or the platform.',
|
||||
'D3_WEBAUTHN_ERR_CONSTRAINT' => 'The action could not be performed by the authenticating device.',
|
||||
'D3_WEBAUTHN_ERR_NOTSUPPORTED' => 'The action is not supported.',
|
||||
'D3_WEBAUTHN_ERR_UNKNOWN' => 'The action was cancelled due to an unknown error.',
|
||||
'D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT' => 'Unfortunately, your browser does not support the use of hardware keys.',
|
||||
'D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT' => 'Unfortunately, your browser does not support the use of login keys.',
|
||||
'D3_WEBAUTHN_ERR_TECHNICALERROR' => 'A technical error occurred while checking the access data.',
|
||||
'D3_WEBAUTHN_ERR_NOTLOADEDUSER' => "Can't create webauthn user entity from not loaded user",
|
||||
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Unfortunately, logging in with a security key is currently not possible for technical reasons. Please use your password instead.',
|
||||
'D3_WEBAUTHN_ERR_LOGINPROHIBITED' => 'Unfortunately, logging in with a login key is currently not possible for technical reasons. Please use your password instead.',
|
||||
];
|
||||
|
@ -136,3 +136,10 @@ services:
|
||||
arguments:
|
||||
- 2
|
||||
shared: true
|
||||
|
||||
d3ox.webauthn.OxidEsales\DoctrineMigrationWrapper\MigrationsBuilder:
|
||||
class: 'OxidEsales\DoctrineMigrationWrapper\MigrationsBuilder'
|
||||
factory: 'oxNew'
|
||||
arguments:
|
||||
- 'OxidEsales\DoctrineMigrationWrapper\MigrationsBuilder'
|
||||
shared: false
|
@ -17,6 +17,7 @@ namespace D3\Webauthn\Modules\Application\Component;
|
||||
|
||||
use Assert\Assert;
|
||||
use Assert\AssertionFailedException;
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
|
||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnLoginErrorException;
|
||||
@ -65,8 +66,7 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$userId = $user->d3GetLoginUserId($lgn_user);
|
||||
|
||||
if ($this->d3CanUseWebauthn($lgn_user, $userId)) {
|
||||
if ($this->d3HasWebauthnButNotLoggedin($userId)) {
|
||||
if ($this->d3CanUseWebauthn($lgn_user, $userId) && $this->d3HasWebauthnButNotLoggedin($userId)) {
|
||||
$session = d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
|
||||
@ -89,7 +89,6 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect($sUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $lgn_user
|
||||
@ -163,6 +162,7 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
|
||||
|
||||
/**
|
||||
* @return WebauthnLogin
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function d3GetWebauthnLogin(): WebauthnLogin
|
||||
{
|
||||
@ -172,8 +172,7 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
|
||||
$credential = $request->getRequestEscapedParameter('credential');
|
||||
$error = $request->getRequestEscapedParameter('error');
|
||||
|
||||
Assert::that($credential)->string('credential value expected to be string')
|
||||
->notEmpty('credential value expected contained content');
|
||||
Assert::that($credential)->string('credential value expected to be string');
|
||||
Assert::that($error)->string('error value expected to be string');
|
||||
|
||||
return oxNew(WebauthnLogin::class, $credential, $error);
|
||||
|
@ -18,12 +18,10 @@ namespace D3\Webauthn\Setup;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||
use Exception;
|
||||
use OxidEsales\DoctrineMigrationWrapper\MigrationsBuilder;
|
||||
use OxidEsales\Eshop\Application\Controller\FrontendController;
|
||||
use OxidEsales\Eshop\Core\Config;
|
||||
use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface;
|
||||
use OxidEsales\Eshop\Core\DbMetaDataHandler;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
|
||||
use OxidEsales\Eshop\Core\SeoEncoder;
|
||||
use OxidEsales\Eshop\Core\Utils;
|
||||
use OxidEsales\Eshop\Core\UtilsView;
|
||||
@ -39,97 +37,25 @@ use Psr\Log\LoggerInterface;
|
||||
class Actions
|
||||
{
|
||||
use IsMockable;
|
||||
public const FIELDLENGTH_CREDID = 512;
|
||||
public const FIELDLENGTH_CREDENTIAL = 2000;
|
||||
|
||||
public $seo_de = 'sicherheitsschluessel';
|
||||
public $seo_en = 'en/key-authentication';
|
||||
public $seo_de = 'anmeldeschluessel';
|
||||
public $seo_en = 'en/login-keys';
|
||||
public $stdClassName = 'd3_account_webauthn';
|
||||
|
||||
/**
|
||||
* SQL statement, that will be executed only at the first time of module installation.
|
||||
*
|
||||
* @var string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected $createCredentialSql =
|
||||
"CREATE TABLE `d3wa_usercredentials` (
|
||||
`OXID` char(32) NOT NULL,
|
||||
`OXUSERID` char(32) NOT NULL,
|
||||
`OXSHOPID` int(11) NOT NULL,
|
||||
`NAME` varchar(100) NOT NULL,
|
||||
`CREDENTIALID` varchar(".self::FIELDLENGTH_CREDID.") NOT NULL,
|
||||
`CREDENTIAL` varchar(".self::FIELDLENGTH_CREDENTIAL.") NOT NULL,
|
||||
`OXTIMESTAMP` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`OXID`),
|
||||
KEY `CREDENTIALID_IDX` (`CREDENTIALID`),
|
||||
KEY `SHOPUSER_IDX` (`OXUSERID`,`OXSHOPID`) USING BTREE
|
||||
) ENGINE=InnoDB COMMENT='WebAuthn Credentials';";
|
||||
|
||||
/**
|
||||
* Execute the sql at the first time of the module installation.
|
||||
* @return void
|
||||
* @throws DatabaseConnectionException
|
||||
* @throws DatabaseErrorException
|
||||
*/
|
||||
public function setupModule()
|
||||
public function runModuleMigrations()
|
||||
{
|
||||
if (!$this->tableExists('d3wa_usercredentials')) {
|
||||
$this->executeSQL($this->createCredentialSql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if table exists
|
||||
*
|
||||
* @param string $sTableName table name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function tableExists(string $sTableName): bool
|
||||
{
|
||||
$oDbMetaDataHandler = d3GetOxidDIC()->get('d3ox.webauthn.'.DbMetaDataHandler::class);
|
||||
return $oDbMetaDataHandler->tableExists($sTableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DatabaseInterface|null
|
||||
* @throws DatabaseConnectionException
|
||||
*/
|
||||
protected function d3GetDb(): ?DatabaseInterface
|
||||
{
|
||||
/** @var DatabaseInterface $db */
|
||||
$db = d3GetOxidDIC()->get('d3ox.webauthn.'.DatabaseInterface::class.'.assoc');
|
||||
return $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes given sql statement.
|
||||
*
|
||||
* @param string $sSQL Sql to execute.
|
||||
* @throws DatabaseConnectionException
|
||||
* @throws DatabaseErrorException
|
||||
*/
|
||||
public function executeSQL(string $sSQL)
|
||||
{
|
||||
$this->d3GetDb()->execute($sSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if field exists in table
|
||||
*
|
||||
* @param string $sFieldName field name
|
||||
* @param string $sTableName table name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fieldExists(string $sFieldName, string $sTableName): bool
|
||||
{
|
||||
$oDbMetaDataHandler = d3GetOxidDIC()->get('d3ox.webauthn.'.DbMetaDataHandler::class);
|
||||
return $oDbMetaDataHandler->fieldExists($sFieldName, $sTableName);
|
||||
/** @var MigrationsBuilder $migrationsBuilder */
|
||||
$migrationsBuilder = d3GetOxidDIC()->get('d3ox.webauthn.'.MigrationsBuilder::class);
|
||||
$migrations = $migrationsBuilder->build();
|
||||
$migrations->execute('migrations:migrate', 'd3webauthn');
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate views for changed tables
|
||||
* @throws Exception
|
||||
*/
|
||||
public function regenerateViews()
|
||||
{
|
||||
@ -139,6 +65,7 @@ class Actions
|
||||
|
||||
/**
|
||||
* clear cache
|
||||
* @throws Exception
|
||||
*/
|
||||
public function clearCache()
|
||||
{
|
||||
@ -204,6 +131,7 @@ class Actions
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function seoUrl()
|
||||
{
|
||||
@ -220,9 +148,11 @@ class Actions
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function hasSeoUrl(): bool
|
||||
{
|
||||
/** @var SeoEncoder $seoEncoder */
|
||||
$seoEncoder = d3GetOxidDIC()->get('d3ox.webauthn.'.SeoEncoder::class);
|
||||
$seoUrl = $seoEncoder->getStaticUrl(
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.FrontendController::class)->getViewConfig()->getSelfLink() .
|
||||
@ -234,6 +164,7 @@ class Actions
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createSeoUrl()
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ class Events
|
||||
}
|
||||
|
||||
$actions = oxNew(Actions::class);
|
||||
$actions->setupModule();
|
||||
$actions->runModuleMigrations();
|
||||
$actions->regenerateViews();
|
||||
$actions->clearCache();
|
||||
$actions->seoUrl();
|
||||
|
@ -66,14 +66,15 @@ $logo = '<img src="https://logos.oxidmodule.com/d3logo.svg" alt="(D3)" style="he
|
||||
$aModule = [
|
||||
'id' => $sModuleId,
|
||||
'title' => [
|
||||
'de' => $logo.' zweiter Faktor - Passwortlose Anmeldung',
|
||||
'en' => $logo.' second factor - passwordless login',
|
||||
'de' => $logo.' zweiter Faktor - Passwortlose Anmeldung mit passkeys',
|
||||
'en' => $logo.' second factor - passwordless login with passkeys',
|
||||
],
|
||||
'description' => [
|
||||
'de' => 'Passwortlose Anmeldung für OXID eSales Shop (WebAuthn / FIDO2 basiert)',
|
||||
'en' => 'Passwordless login for OXID eSales shop (WebAuthn / FIDO2 based)',
|
||||
'de' => 'Passwortlose Anmeldung für OXID eSales Shop (mit WebAuthn / FIDO2 basierten passkeys)',
|
||||
'en' => 'Passwordless login for OXID eSales shop (with WebAuthn / FIDO2 based passkeys)',
|
||||
],
|
||||
'version' => '1.0.0.0',
|
||||
'thumbnail' => 'picture.svg',
|
||||
'author' => 'D³ Data Development (Inh.: Thomas Dartsch)',
|
||||
'email' => 'support@shopmodule.com',
|
||||
'url' => 'https://www.oxidmodule.com/',
|
||||
|
112
src/migration/data/Version20230209212939.php
Normal file
112
src/migration/data/Version20230209212939.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* https://www.d3data.de
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||
* @link https://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\DateTimeType;
|
||||
use Doctrine\DBAL\Types\IntegerType;
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20230209212939 extends AbstractMigration
|
||||
{
|
||||
public const FIELDLENGTH_CREDID = 512;
|
||||
public const FIELDLENGTH_CREDENTIAL = 2000;
|
||||
|
||||
public function getDescription() : string
|
||||
{
|
||||
return 'create credential database table';
|
||||
}
|
||||
|
||||
public function up(Schema $schema) : void
|
||||
{
|
||||
$this->connection->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
|
||||
|
||||
$table = !$schema->hasTable('d3wa_usercredentials') ?
|
||||
$schema->createTable('d3wa_usercredentials') :
|
||||
$schema->getTable('d3wa_usercredentials');
|
||||
|
||||
if (!$table->hasColumn('OXID')) {
|
||||
$table->addColumn('OXID', (new StringType())->getName())
|
||||
->setLength(32)
|
||||
->setFixed(true)
|
||||
->setNotnull(true);
|
||||
}
|
||||
|
||||
if (!$table->hasColumn('OXUSERID')) {
|
||||
$table->addColumn('OXUSERID', (new StringType())->getName())
|
||||
->setLength(32)
|
||||
->setFixed(true)
|
||||
->setNotnull(true);
|
||||
}
|
||||
|
||||
if (!$table->hasColumn('OXSHOPID')) {
|
||||
$table->addColumn('OXSHOPID', (new IntegerType())->getName())
|
||||
->setLength(11)
|
||||
->setNotnull(true);
|
||||
}
|
||||
|
||||
if (!$table->hasColumn('NAME')) {
|
||||
$table->addColumn('NAME', (new StringType())->getName())
|
||||
->setLength(100)
|
||||
->setFixed(false)
|
||||
->setNotnull(true);
|
||||
}
|
||||
|
||||
if (!$table->hasColumn('CREDENTIALID')) {
|
||||
$table->addColumn('CREDENTIALID', (new StringType())->getName())
|
||||
->setLength(self::FIELDLENGTH_CREDID)
|
||||
->setFixed(false)
|
||||
->setNotnull(true);
|
||||
}
|
||||
|
||||
if (!$table->hasColumn('CREDENTIAL')) {
|
||||
$table->addColumn('CREDENTIAL', (new StringType())->getName())
|
||||
->setLength(self::FIELDLENGTH_CREDENTIAL)
|
||||
->setFixed(false)
|
||||
->setNotnull(true);
|
||||
}
|
||||
|
||||
if (!$table->hasColumn('OXTIMESTAMP')) {
|
||||
$table->addColumn('OXTIMESTAMP', (new DateTimeType())->getName())
|
||||
->setType(new DateTimeType())
|
||||
->setNotnull(true)
|
||||
// can't set default value via default method
|
||||
->setColumnDefinition('timestamp DEFAULT CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP');
|
||||
}
|
||||
|
||||
if (!$table->hasPrimaryKey()) {
|
||||
$table->setPrimaryKey(['OXID']);
|
||||
}
|
||||
|
||||
if (!$table->hasIndex('SHOPUSER_IDX')) {
|
||||
$table->addIndex(['OXUSERID', 'OXSHOPID'], 'SHOPUSER_IDX');
|
||||
}
|
||||
|
||||
if (!$table->hasIndex('CREDENTIALID_IDX')) {
|
||||
$table->addIndex(['CREDENTIALID'], 'CREDENTIALID_IDX');
|
||||
}
|
||||
|
||||
$table->setComment('WebAuthn Credentials');
|
||||
}
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
{
|
||||
if ($schema->hasTable('d3wa_usercredentials')) {
|
||||
$schema->dropTable('d3wa_usercredentials');
|
||||
}
|
||||
}
|
||||
}
|
4
src/migration/migrations.yml
Executable file
4
src/migration/migrations.yml
Executable file
@ -0,0 +1,4 @@
|
||||
name: D3 Twofactor Passwordless
|
||||
migrations_namespace: D3\Webauthn\Migrations
|
||||
table_name: d3wa_migrations
|
||||
migrations_directory: data
|
@ -179,7 +179,7 @@ const requestCredentials = (publicKey) => {
|
||||
response: {
|
||||
authenticatorData: base64ArrayBuffer(authenticateInfo.response.authenticatorData),
|
||||
signature: base64ArrayBuffer(authenticateInfo.response.signature),
|
||||
userHandle: authenticateInfo.response.userHandle,
|
||||
userHandle: base64ArrayBuffer(authenticateInfo.response.userHandle),
|
||||
clientDataJSON: base64ArrayBuffer(authenticateInfo.response.clientDataJSON)
|
||||
},
|
||||
type: authenticateInfo.type
|
||||
|
65
src/picture.svg
Normal file
65
src/picture.svg
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="201px" height="124px" viewBox="0 0 201 124" enable-background="new 0 0 201 124" xml:space="preserve">
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="47.0591" y1="67.5117" x2="47.0591" y2="54.6143">
|
||||
<stop offset="0.0056" style="stop-color:#3266A9"/>
|
||||
<stop offset="1" style="stop-color:#0099FF"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M50.282,55.502c-0.784-0.592-2.104-0.888-3.961-0.888h-1.376l-2.283,12.898h1.779
|
||||
c3.76,0,6.032-2.245,6.815-6.733c0.134-0.871,0.202-1.642,0.202-2.313C51.457,57.081,51.064,56.093,50.282,55.502z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="65.9609" y1="49.104" x2="65.9609" y2="36.9434">
|
||||
<stop offset="0.0056" style="stop-color:#3266A9"/>
|
||||
<stop offset="1" style="stop-color:#0099FF"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M65.72,40.482c1.074,0,1.611,0.381,1.611,1.143c0,0.701-0.321,1.201-0.962,1.5
|
||||
c-0.209,0.119-0.366,0.194-0.471,0.224c-0.065,0.019-0.158,0.037-0.271,0.056c1.98,1.621,3.702,3.544,5.097,5.699
|
||||
c0.117-0.321,0.21-0.658,0.277-1.013l0.09-1.008c0-1.223-0.568-2.081-1.701-2.574c0.776-0.402,1.376-0.94,1.801-1.611
|
||||
c0.425-0.672,0.638-1.418,0.638-2.239c0-0.642-0.198-1.265-0.593-1.868c-0.396-0.605-0.98-1.049-1.757-1.333
|
||||
c-0.433-0.193-0.876-0.328-1.332-0.402c-0.456-0.075-1.003-0.113-1.645-0.113c-0.82,0-1.663,0.124-2.529,0.37
|
||||
c-0.865,0.246-1.6,0.563-2.204,0.952s-1.13,0.907-1.578,1.557c-0.036,0.052-0.066,0.109-0.101,0.163
|
||||
c1.196,0.534,2.341,1.163,3.426,1.874C63.947,40.943,64.68,40.482,65.72,40.482z"/>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="50.0576" y1="87.0566" x2="50.0576" y2="37.8525">
|
||||
<stop offset="0.0056" style="stop-color:#3266A9"/>
|
||||
<stop offset="1" style="stop-color:#0099FF"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_3_)" d="M70.725,49.104c-0.433,1.189-1.208,2.147-2.331,2.871c-1.425,0.918-3.182,1.377-5.271,1.377
|
||||
c-1.179,0-2.175-0.176-2.988-0.525c-0.813-0.35-1.444-0.864-1.891-1.543c-0.448-0.678-0.671-1.481-0.671-2.405l0.022-0.694
|
||||
l0.156-0.693h4.367l-0.028,0.179v0.179v0.246c0,1.164,0.628,1.746,1.884,1.746c0.635,0,1.201-0.217,1.696-0.649
|
||||
c0.495-0.434,0.742-0.94,0.742-1.522c0-0.522-0.194-0.887-0.582-1.097c-0.329-0.208-1.007-0.313-2.036-0.313l0.47-2.754
|
||||
l1.141-0.067c0.083-0.011,0.154-0.022,0.221-0.033c-0.674-0.551-1.378-1.067-2.11-1.546c-0.044,0.096-0.087,0.195-0.125,0.302
|
||||
h-4.185c0.192-0.837,0.49-1.56,0.884-2.175c-3.064-1.372-6.46-2.133-10.034-2.133c-13.588,0-24.603,11.014-24.603,24.601
|
||||
c0,13.59,11.015,24.604,24.603,24.604S74.66,76.043,74.66,62.453C74.66,57.532,73.214,52.949,70.725,49.104z M59.413,59.233
|
||||
l-0.168,1.275c-0.538,2.953-1.511,5.404-2.921,7.35c-1.298,1.835-3.016,3.179-5.153,4.028c-2.138,0.851-4.494,1.274-7.067,1.274
|
||||
H33.731l4.264-24.198h10.441c1.141,0,2.204,0.073,3.189,0.218c0.984,0.146,1.868,0.364,2.651,0.655
|
||||
c1.611,0.537,2.887,1.471,3.827,2.802c0.94,1.332,1.41,2.992,1.41,4.984L59.413,59.233z"/>
|
||||
</g>
|
||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="107.3027" y1="105.8555" x2="93.0727" y2="16.0106">
|
||||
<stop offset="0" style="stop-color:#B2B2B2;stop-opacity:0"/>
|
||||
<stop offset="0.2" style="stop-color:#B2B2B2"/>
|
||||
<stop offset="0.8" style="stop-color:#B2B2B2"/>
|
||||
<stop offset="1" style="stop-color:#B2B2B2;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<rect x="99.875" y="14.933" fill="url(#SVGID_4_)" width="0.625" height="92"/>
|
||||
<script xmlns=""></script>
|
||||
<g transform="translate(0.000000,500.000000) scale(0.100000,-0.100000)">
|
||||
<path d="M1423.34,4628.427c-43.174-8.984-79.238-44.297-90.264-88.091c-3.789-15.195-3.789-40.508,0-55.698
|
||||
c11.152-44.429,46.973-78.975,91.26-88.101c7.227-1.514,14.307-1.89,28.74-1.514c16.201,0.645,20.508,1.138,29.736,4.18
|
||||
c25.576,8.223,44.57,21.382,59.502,41.382c9.492,12.529,15.313,24.053,19.863,38.608c3.164,10.259,3.428,12.783,3.428,33.291
|
||||
s-0.264,23.037-3.428,33.291c-12.021,38.979-39.99,67.202-79.365,79.99c-9.609,3.164-12.9,3.545-30.996,3.921
|
||||
C1438.262,4629.946,1428.896,4629.565,1423.34,4628.427z"/>
|
||||
<path d="M1653.701,4484.13c-18.105-3.794-33.926-12.651-47.471-26.704c-25.059-25.688-32.646-64.8-18.984-98.345
|
||||
c6.963-17.217,22.666-35.186,38.35-44.053l7.715-4.434l0.137-70.869v-70.889l19.365-19.248l19.355-19.355l32.793,32.91l32.91,32.91
|
||||
l-19.619,19.619l-19.619,19.609l19.365,19.365c10.498,10.635,19.229,19.873,19.229,20.518c0,0.625-6.953,8.223-15.43,16.689
|
||||
c-8.486,8.486-15.195,15.713-14.932,15.957c0.371,0.264,5.176,2.91,10.752,6.074c21.006,11.65,37.842,33.418,43.662,56.338
|
||||
c3.047,11.89,3.428,34.043,0.635,45.43c-7.461,31.899-33.281,58.35-65.43,67.217
|
||||
C1686.738,4485.527,1663.691,4486.162,1653.701,4484.13z M1686.982,4442.114c6.211-4.312,12.285-15.063,12.285-21.89
|
||||
c0-6.465-5.322-16.973-10.518-20.894c-5.811-4.434-16.318-6.592-23.027-4.814c-10.889,3.042-19.492,14.175-19.619,25.313
|
||||
C1645.976,4440.844,1669.892,4453.876,1686.982,4442.114z"/>
|
||||
<path d="M1384.736,4352.246c-58.477-8.994-107.588-50.752-126.084-107.207c-6.318-19.492-7.461-29.365-7.461-66.836v-34.053
|
||||
h170.869h170.889v72.158v72.139l-10.137,9.629c-11.396,10.889-20.381,22.529-26.826,34.668l-4.307,8.242l-13.926,4.678
|
||||
c-22.656,7.588-33.418,8.613-91.133,8.477C1410.557,4354.003,1392.822,4353.505,1384.736,4352.246z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
14
src/tests/d3webauthn_config.php
Normal file
14
src/tests/d3webauthn_config.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* https://www.d3data.de
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||
* @link https://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
const D3WEBAUTHN_REQUIRE_MODCFG = false;
|
@ -17,15 +17,15 @@
|
||||
|
||||
namespace D3\Webauthn\tests\integration;
|
||||
|
||||
use D3\ModCfg\Application\Model\DependencyInjectionContainer\d3DicHandler;
|
||||
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
|
||||
use D3\DIContainerHandler\d3DicHandler;
|
||||
use Exception;
|
||||
use OxidEsales\Eshop\Application\Model\Article;
|
||||
use OxidEsales\Eshop\Application\Model\Rights;
|
||||
use OxidEsales\Eshop\Application\Model\User;
|
||||
use OxidEsales\Eshop\Core\Model\BaseModel;
|
||||
use OxidEsales\TestingLibrary\UnitTestCase;
|
||||
|
||||
abstract class integrationTestCase extends d3ModCfgUnitTestCase
|
||||
abstract class integrationTestCase extends UnitTestCase
|
||||
{
|
||||
/**
|
||||
* Set up fixture.
|
||||
|
@ -76,7 +76,7 @@ class passwordFrontendAuthTest extends integrationTestCase
|
||||
* @test
|
||||
* @dataProvider loginDataProvider
|
||||
*/
|
||||
public function testCheckLoginReturn($username, $password, $expected)
|
||||
public function testCheckLoginReturn($username, $password, $expected, $redirect = null)
|
||||
{
|
||||
$_POST['lgn_usr'] = $username;
|
||||
$_POST['lgn_pwd'] = $password;
|
||||
|
167
src/tests/integration/webauthnFrontendAuthTest.php
Normal file
167
src/tests/integration/webauthnFrontendAuthTest.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This Software is the property of Data Development and is protected
|
||||
* by copyright law - it is NOT Freeware.
|
||||
* Any unauthorized use of this software without a valid license
|
||||
* is a violation of the license agreement and will be prosecuted by
|
||||
* civil and criminal law.
|
||||
* http://www.shopmodule.com
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
|
||||
* @link http://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
namespace D3\Webauthn\tests\integration;
|
||||
|
||||
use D3\DIContainerHandler\d3DicHandler;
|
||||
use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
|
||||
use OxidEsales\Eshop\Core\Utils;
|
||||
|
||||
class webauthnFrontendAuthTest extends passwordFrontendAuthTest
|
||||
{
|
||||
protected $userList = [
|
||||
1 => 'userId1',
|
||||
2 => 'userId2',
|
||||
3 => 'userId3',
|
||||
4 => 'userId4',
|
||||
5 => 'userId5',
|
||||
];
|
||||
|
||||
protected $credentialList = [
|
||||
1 => 'credId1',
|
||||
2 => 'credId2',
|
||||
3 => 'credId3',
|
||||
4 => 'credId4',
|
||||
5 => 'credId5',
|
||||
];
|
||||
|
||||
public function createTestData()
|
||||
{
|
||||
parent::createTestData();
|
||||
|
||||
$this->createUser(
|
||||
$this->userList[5],
|
||||
[
|
||||
'oxactive' => 1,
|
||||
'oxrights' => 'malladmin',
|
||||
'oxshopid' => 1,
|
||||
'oxusername' => 'wawrongshopid@user.localhost',
|
||||
'oxpassword' => '$2y$10$QErMJNHQCoN03tfCUQDRfOvbwvqfzwWw1iI/7bC49fKQrPKoDdnaK', // 123456
|
||||
'oxstreet' => __CLASS__,
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
$this->createObject(
|
||||
PublicKeyCredential::class,
|
||||
$this->credentialList[1],
|
||||
[
|
||||
'oxuserid' => $this->userList[1],
|
||||
'oxshopid' => 1,
|
||||
'name' => __CLASS__,
|
||||
'credentialid' => 'ITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAAA==',
|
||||
'credential'=> 'TzozNDoiV2ViYXV0aG5cUHVibGljS2V5Q3JlZGVudGlhbFNvdXJjZSI6MTA6e3M6MjQ6IgAqAHB1YmxpY0tleUNyZWRlbnRpYWxJZCI7czo3MDoiITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAACI7czo3OiIAKgB0eXBlIjtzOjEwOiJwdWJsaWMta2V5IjtzOjEzOiIAKgB0cmFuc3BvcnRzIjthOjA6e31zOjE4OiIAKgBhdHRlc3RhdGlvblR5cGUiO3M6NDoibm9uZSI7czoxMjoiACoAdHJ1c3RQYXRoIjtPOjMzOiJXZWJhdXRoblxUcnVzdFBhdGhcRW1wdHlUcnVzdFBhdGgiOjA6e31zOjk6IgAqAGFhZ3VpZCI7TzozNToiUmFtc2V5XFV1aWRcTGF6eVxMYXp5VXVpZEZyb21TdHJpbmciOjE6e3M6Njoic3RyaW5nIjtzOjM2OiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiO31zOjIyOiIAKgBjcmVkZW50aWFsUHVibGljS2V5IjtzOjc3OiKlAQIDJiABIVggHucXfQh0acwpsffVRM02F7P57mVm6hPX/l8Pjbh0jOwiWCBRT5MMqa909tcXHqG/EKfjXXDd9UEisk+ZF7QSTfwv0CI7czoxMzoiACoAdXNlckhhbmRsZSI7czoxNDoib3hkZWZhdWx0YWRtaW4iO3M6MTA6IgAqAGNvdW50ZXIiO2k6NDI3MTtzOjEwOiIAKgBvdGhlclVJIjtOO30=',
|
||||
]
|
||||
);
|
||||
|
||||
$this->createObject(
|
||||
PublicKeyCredential::class,
|
||||
$this->credentialList[2],
|
||||
[
|
||||
'oxuserid' => $this->userList[2],
|
||||
'oxshopid' => 1,
|
||||
'name' => __CLASS__,
|
||||
'credentialid' => 'ITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAAA==',
|
||||
'credential'=> 'TzozNDoiV2ViYXV0aG5cUHVibGljS2V5Q3JlZGVudGlhbFNvdXJjZSI6MTA6e3M6MjQ6IgAqAHB1YmxpY0tleUNyZWRlbnRpYWxJZCI7czo3MDoiITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAACI7czo3OiIAKgB0eXBlIjtzOjEwOiJwdWJsaWMta2V5IjtzOjEzOiIAKgB0cmFuc3BvcnRzIjthOjA6e31zOjE4OiIAKgBhdHRlc3RhdGlvblR5cGUiO3M6NDoibm9uZSI7czoxMjoiACoAdHJ1c3RQYXRoIjtPOjMzOiJXZWJhdXRoblxUcnVzdFBhdGhcRW1wdHlUcnVzdFBhdGgiOjA6e31zOjk6IgAqAGFhZ3VpZCI7TzozNToiUmFtc2V5XFV1aWRcTGF6eVxMYXp5VXVpZEZyb21TdHJpbmciOjE6e3M6Njoic3RyaW5nIjtzOjM2OiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiO31zOjIyOiIAKgBjcmVkZW50aWFsUHVibGljS2V5IjtzOjc3OiKlAQIDJiABIVggHucXfQh0acwpsffVRM02F7P57mVm6hPX/l8Pjbh0jOwiWCBRT5MMqa909tcXHqG/EKfjXXDd9UEisk+ZF7QSTfwv0CI7czoxMzoiACoAdXNlckhhbmRsZSI7czoxNDoib3hkZWZhdWx0YWRtaW4iO3M6MTA6IgAqAGNvdW50ZXIiO2k6NDI3MTtzOjEwOiIAKgBvdGhlclVJIjtOO30=',
|
||||
]
|
||||
);
|
||||
|
||||
$this->createObject(
|
||||
PublicKeyCredential::class,
|
||||
$this->credentialList[3],
|
||||
[
|
||||
'oxuserid' => $this->userList[3],
|
||||
'oxshopid' => 1,
|
||||
'name' => __CLASS__,
|
||||
'credentialid' => 'ITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAAA==',
|
||||
'credential'=> 'TzozNDoiV2ViYXV0aG5cUHVibGljS2V5Q3JlZGVudGlhbFNvdXJjZSI6MTA6e3M6MjQ6IgAqAHB1YmxpY0tleUNyZWRlbnRpYWxJZCI7czo3MDoiITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAACI7czo3OiIAKgB0eXBlIjtzOjEwOiJwdWJsaWMta2V5IjtzOjEzOiIAKgB0cmFuc3BvcnRzIjthOjA6e31zOjE4OiIAKgBhdHRlc3RhdGlvblR5cGUiO3M6NDoibm9uZSI7czoxMjoiACoAdHJ1c3RQYXRoIjtPOjMzOiJXZWJhdXRoblxUcnVzdFBhdGhcRW1wdHlUcnVzdFBhdGgiOjA6e31zOjk6IgAqAGFhZ3VpZCI7TzozNToiUmFtc2V5XFV1aWRcTGF6eVxMYXp5VXVpZEZyb21TdHJpbmciOjE6e3M6Njoic3RyaW5nIjtzOjM2OiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiO31zOjIyOiIAKgBjcmVkZW50aWFsUHVibGljS2V5IjtzOjc3OiKlAQIDJiABIVggHucXfQh0acwpsffVRM02F7P57mVm6hPX/l8Pjbh0jOwiWCBRT5MMqa909tcXHqG/EKfjXXDd9UEisk+ZF7QSTfwv0CI7czoxMzoiACoAdXNlckhhbmRsZSI7czoxNDoib3hkZWZhdWx0YWRtaW4iO3M6MTA6IgAqAGNvdW50ZXIiO2k6NDI3MTtzOjEwOiIAKgBvdGhlclVJIjtOO30=',
|
||||
]
|
||||
);
|
||||
|
||||
$this->createObject(
|
||||
PublicKeyCredential::class,
|
||||
$this->credentialList[4],
|
||||
[
|
||||
'oxuserid' => $this->userList[4],
|
||||
'oxshopid' => 1,
|
||||
'name' => __CLASS__,
|
||||
'credentialid' => 'ITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAAA==',
|
||||
'credential'=> 'TzozNDoiV2ViYXV0aG5cUHVibGljS2V5Q3JlZGVudGlhbFNvdXJjZSI6MTA6e3M6MjQ6IgAqAHB1YmxpY0tleUNyZWRlbnRpYWxJZCI7czo3MDoiITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAACI7czo3OiIAKgB0eXBlIjtzOjEwOiJwdWJsaWMta2V5IjtzOjEzOiIAKgB0cmFuc3BvcnRzIjthOjA6e31zOjE4OiIAKgBhdHRlc3RhdGlvblR5cGUiO3M6NDoibm9uZSI7czoxMjoiACoAdHJ1c3RQYXRoIjtPOjMzOiJXZWJhdXRoblxUcnVzdFBhdGhcRW1wdHlUcnVzdFBhdGgiOjA6e31zOjk6IgAqAGFhZ3VpZCI7TzozNToiUmFtc2V5XFV1aWRcTGF6eVxMYXp5VXVpZEZyb21TdHJpbmciOjE6e3M6Njoic3RyaW5nIjtzOjM2OiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiO31zOjIyOiIAKgBjcmVkZW50aWFsUHVibGljS2V5IjtzOjc3OiKlAQIDJiABIVggHucXfQh0acwpsffVRM02F7P57mVm6hPX/l8Pjbh0jOwiWCBRT5MMqa909tcXHqG/EKfjXXDd9UEisk+ZF7QSTfwv0CI7czoxMzoiACoAdXNlckhhbmRsZSI7czoxNDoib3hkZWZhdWx0YWRtaW4iO3M6MTA6IgAqAGNvdW50ZXIiO2k6NDI3MTtzOjEwOiIAKgBvdGhlclVJIjtOO30=',
|
||||
]
|
||||
);
|
||||
|
||||
$this->createObject(
|
||||
PublicKeyCredential::class,
|
||||
$this->credentialList[5],
|
||||
[
|
||||
'oxuserid' => $this->userList[5],
|
||||
'oxshopid' => 2,
|
||||
'name' => __CLASS__,
|
||||
'credentialid' => 'ITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAAA==',
|
||||
'credential'=> 'TzozNDoiV2ViYXV0aG5cUHVibGljS2V5Q3JlZGVudGlhbFNvdXJjZSI6MTA6e3M6MjQ6IgAqAHB1YmxpY0tleUNyZWRlbnRpYWxJZCI7czo3MDoiITSNkDRdN1bfRrb9MDCNOfBNay7YqT3ZxWxxqIQWVvwN0tFOG7SN2JiCfcUfPMBhE9bTLU1Gbb/8+5eHyFR2d5DCrxAAACI7czo3OiIAKgB0eXBlIjtzOjEwOiJwdWJsaWMta2V5IjtzOjEzOiIAKgB0cmFuc3BvcnRzIjthOjA6e31zOjE4OiIAKgBhdHRlc3RhdGlvblR5cGUiO3M6NDoibm9uZSI7czoxMjoiACoAdHJ1c3RQYXRoIjtPOjMzOiJXZWJhdXRoblxUcnVzdFBhdGhcRW1wdHlUcnVzdFBhdGgiOjA6e31zOjk6IgAqAGFhZ3VpZCI7TzozNToiUmFtc2V5XFV1aWRcTGF6eVxMYXp5VXVpZEZyb21TdHJpbmciOjE6e3M6Njoic3RyaW5nIjtzOjM2OiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiO31zOjIyOiIAKgBjcmVkZW50aWFsUHVibGljS2V5IjtzOjc3OiKlAQIDJiABIVggHucXfQh0acwpsffVRM02F7P57mVm6hPX/l8Pjbh0jOwiWCBRT5MMqa909tcXHqG/EKfjXXDd9UEisk+ZF7QSTfwv0CI7czoxMzoiACoAdXNlckhhbmRsZSI7czoxNDoib3hkZWZhdWx0YWRtaW4iO3M6MTA6IgAqAGNvdW50ZXIiO2k6NDI3MTtzOjEwOiIAKgBvdGhlclVJIjtOO30=',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function cleanTestData()
|
||||
{
|
||||
parent::cleanTestData();
|
||||
|
||||
$this->deleteUser($this->userList[5]);
|
||||
|
||||
$this->deleteObject(PublicKeyCredential::class, $this->credentialList[1]);
|
||||
$this->deleteObject(PublicKeyCredential::class, $this->credentialList[2]);
|
||||
$this->deleteObject(PublicKeyCredential::class, $this->credentialList[3]);
|
||||
$this->deleteObject(PublicKeyCredential::class, $this->credentialList[4]);
|
||||
$this->deleteObject(PublicKeyCredential::class, $this->credentialList[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param $expected
|
||||
* @param $redirect
|
||||
* @return void
|
||||
* @dataProvider loginDataProvider
|
||||
*/
|
||||
public function testCheckLoginReturn($username, $password, $expected, $redirect = null)
|
||||
{
|
||||
$utilsMock = $this->getMockBuilder(Utils::class)
|
||||
->onlyMethods(['redirect'])
|
||||
->getMock();
|
||||
$utilsMock->expects($redirect ?: $this->never())->method('redirect')->willReturn(true);
|
||||
d3DicHandler::getInstance()->set('d3ox.webauthn.'.Utils::class, $utilsMock);
|
||||
|
||||
parent::testCheckLoginReturn($username, $password, $expected);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function loginDataProvider(): array
|
||||
{
|
||||
return [
|
||||
'not existing account' => ['unknown@user.localhost', '123456', 'user'],
|
||||
'missing password' => ['noadmin@user.localhost', null, 'user', $this->once()],
|
||||
'inactive account' => ['inactive@user.localhost', '123456', 'user'],
|
||||
'wrong shop account' => ['wrongshop@user.localhost', '123456', 'user'],
|
||||
'account ok' => ['admin@user.localhost', '123456', 'user'],
|
||||
'cred. wrong shopid' => ['wawrongshopid@user.localhost', null, 'user'],
|
||||
'credpass. wrong shopid'=> ['wawrongshopid@user.localhost', '123456', 'payment'],
|
||||
];
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">../Application</directory>
|
||||
<directory suffix=".php">../migration</directory>
|
||||
<directory suffix=".php">../Modules</directory>
|
||||
<directory suffix=".php">../Setup</directory>
|
||||
<exclude>
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
namespace D3\Webauthn\tests\unit\Application\Controller\Admin;
|
||||
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Development\CanAccessRestricted;
|
||||
use D3\TestingTools\Production\IsMockable;
|
||||
use D3\Webauthn\Application\Controller\Admin\d3user_webauthn;
|
||||
@ -172,7 +173,7 @@ class d3user_webauthnTest extends WAUnitTestCase
|
||||
])
|
||||
->getMock();
|
||||
$sutMock->expects($this->atLeastOnce())->method('setPageType');
|
||||
$sutMock->expects($this->atLeastOnce())->method('setAuthnRegister')->willThrowException(oxNew(WebauthnException::class));
|
||||
$sutMock->expects($this->atLeastOnce())->method('setAuthnRegister')->willThrowException(new InvalidArgumentException('msg', 20));
|
||||
|
||||
$this->callMethod(
|
||||
$sutMock,
|
||||
|
@ -144,11 +144,15 @@ class d3webauthnadminloginTest extends d3webauthnloginTest
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @dataProvider \D3\Webauthn\tests\unit\Application\Controller\d3webauthnloginTest::generateCredentialRequestFailedDataProvider()
|
||||
* @covers \D3\Webauthn\Application\Controller\Admin\d3webauthnadminlogin::generateCredentialRequest
|
||||
*/
|
||||
public function generateCredentialRequestFailed($redirectClass = 'login', $userVarName = WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER)
|
||||
{
|
||||
parent::generateCredentialRequestFailed($redirectClass, $userVarName);
|
||||
public function generateCredentialRequestFailed(
|
||||
$exception,
|
||||
$redirectClass = 'login',
|
||||
$userVarName = WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER
|
||||
) {
|
||||
parent::generateCredentialRequestFailed($exception, $redirectClass, $userVarName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,7 +187,7 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
/** @var LoggerInterface|MockObject $loggerMock */
|
||||
$loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']);
|
||||
$loggerMock->expects($this->never())->method('error')->willReturn(true);
|
||||
$loggerMock->expects($this->never())->method('debug')->willReturn(true);
|
||||
$loggerMock->method('debug')->willReturn(true);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.LoggerInterface::class, $loggerMock);
|
||||
|
||||
/** @var d3_account_webauthn|MockObject $oControllerMock */
|
||||
@ -210,9 +210,10 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @dataProvider canRequestNewCredentialCantGetCreationOptionsDataProvider
|
||||
* @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::requestNewCredential()
|
||||
*/
|
||||
public function canRequestNewCredentialCantGetCreationOptions()
|
||||
public function canRequestNewCredentialCantGetCreationOptions($exception)
|
||||
{
|
||||
$oUser = oxNew(User::class);
|
||||
$oUser->setId('foo');
|
||||
@ -225,7 +226,7 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
/** @var LoggerInterface|MockObject $loggerMock */
|
||||
$loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']);
|
||||
$loggerMock->expects($this->atLeastOnce())->method('error')->willReturn(true);
|
||||
$loggerMock->expects($this->atLeastOnce())->method('debug')->willReturn(true);
|
||||
$loggerMock->method('debug')->willReturn(true);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.LoggerInterface::class, $loggerMock);
|
||||
|
||||
/** @var d3_account_webauthn|MockObject $oControllerMock */
|
||||
@ -233,7 +234,7 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
->onlyMethods(['setAuthnRegister', 'setPageType', 'getUser'])
|
||||
->getMock();
|
||||
$oControllerMock->expects($this->atLeastOnce())->method('setAuthnRegister')
|
||||
->willThrowException(oxNew(WebauthnException::class));
|
||||
->willThrowException($exception);
|
||||
$oControllerMock->expects($this->never())->method('setPageType');
|
||||
$oControllerMock->method('getUser')->willReturn($oUser);
|
||||
|
||||
@ -245,6 +246,16 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function canRequestNewCredentialCantGetCreationOptionsDataProvider(): Generator
|
||||
{
|
||||
yield 'WebauthnException' => [oxNew(WebauthnException::class)];
|
||||
yield 'InvalidArgumentException' => [oxNew(InvalidArgumentException::class, 'msg', 20)];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @param $throwExc
|
||||
@ -334,7 +345,7 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
/** @var LoggerInterface|MockObject $loggerMock */
|
||||
$loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']);
|
||||
$loggerMock->expects($this->once())->method('error')->willReturn(true);
|
||||
$loggerMock->expects($this->once())->method('debug')->willReturn(true);
|
||||
$loggerMock->method('debug')->willReturn(true);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.LoggerInterface::class, $loggerMock);
|
||||
|
||||
/** @var User|MockObject $userMock */
|
||||
@ -425,7 +436,7 @@ class d3_account_webauthnTest extends WAUnitTestCase
|
||||
/** @var LoggerInterface|MockObject $loggerMock */
|
||||
$loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']);
|
||||
$loggerMock->expects($this->once())->method('error')->willReturn(true);
|
||||
$loggerMock->expects($this->once())->method('debug')->willReturn(true);
|
||||
$loggerMock->method('debug')->willReturn(true);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.LoggerInterface::class, $loggerMock);
|
||||
|
||||
/** @var User|MockObject $userMock */
|
||||
|
@ -15,6 +15,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\tests\unit\Application\Controller;
|
||||
|
||||
use Assert\InvalidArgumentException;
|
||||
use D3\TestingTools\Development\CanAccessRestricted;
|
||||
use D3\Webauthn\Application\Controller\d3webauthnlogin;
|
||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnException;
|
||||
@ -144,7 +145,7 @@ class d3webauthnloginTest extends WAUnitTestCase
|
||||
/** @var LoggerInterface|MockObject $loggerMock */
|
||||
$loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']);
|
||||
$loggerMock->expects($this->never())->method('error')->willReturn(true);
|
||||
$loggerMock->expects($this->never())->method('debug')->willReturn(true);
|
||||
$loggerMock->method('debug')->willReturn(true);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.LoggerInterface::class, $loggerMock);
|
||||
|
||||
/** @var Session|MockObject $sessionMock */
|
||||
@ -181,9 +182,14 @@ class d3webauthnloginTest extends WAUnitTestCase
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @dataProvider generateCredentialRequestFailedDataProvider
|
||||
* @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::generateCredentialRequest
|
||||
*/
|
||||
public function generateCredentialRequestFailed($redirectClass = 'start', $userVarName = WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER)
|
||||
public function generateCredentialRequestFailed(
|
||||
$exception,
|
||||
$redirectClass = 'start',
|
||||
$userVarName = WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER
|
||||
)
|
||||
{
|
||||
$currUserFixture = 'currentUserFixture';
|
||||
|
||||
@ -209,7 +215,7 @@ class d3webauthnloginTest extends WAUnitTestCase
|
||||
->onlyMethods(['getRequestOptions'])
|
||||
->getMock();
|
||||
$webAuthnMock->expects($this->once())->method('getRequestOptions')->with($currUserFixture)
|
||||
->willThrowException(oxNew(WebauthnException::class, 'foobar0'));
|
||||
->willThrowException($exception);
|
||||
d3GetOxidDIC()->set(Webauthn::class, $webAuthnMock);
|
||||
|
||||
/** @var Utils|MockObject $utilsMock */
|
||||
@ -233,6 +239,15 @@ class d3webauthnloginTest extends WAUnitTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function generateCredentialRequestFailedDataProvider(): Generator
|
||||
{
|
||||
yield 'WebauthnException' => [oxNew(WebauthnException::class, 'foobar0')];
|
||||
yield 'InvalidArgumentException' => [oxNew(InvalidArgumentException::class, 'foobar0', 20)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
|
@ -141,8 +141,8 @@ class PublicKeyCredentialTest extends WAUnitTestCase
|
||||
$this->canGetField(
|
||||
'credentialid',
|
||||
'getCredentialId',
|
||||
'credentialFixture',
|
||||
base64_decode('credentialFixture')
|
||||
base64_encode('credentialFixture'),
|
||||
'credentialFixture'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ class WebauthnTest extends WAUnitTestCase
|
||||
*/
|
||||
public function canGetOptionsDataProvider(): Generator
|
||||
{
|
||||
yield 'json encoded' => ['jsonstring'];
|
||||
yield 'json encoded' => [json_encode(['jsonstring'])];
|
||||
yield 'json failed' => [false];
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,9 @@ use D3\TestingTools\Development\CanAccessRestricted;
|
||||
use D3\Webauthn\Setup\Actions;
|
||||
use D3\Webauthn\tests\unit\WAUnitTestCase;
|
||||
use Exception;
|
||||
use OxidEsales\DoctrineMigrationWrapper\Migrations;
|
||||
use OxidEsales\DoctrineMigrationWrapper\MigrationsBuilder;
|
||||
use OxidEsales\Eshop\Application\Controller\FrontendController;
|
||||
use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface;
|
||||
use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database;
|
||||
use OxidEsales\Eshop\Core\DbMetaDataHandler;
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
use OxidEsales\Eshop\Core\SeoEncoder;
|
||||
@ -45,141 +45,35 @@ class ActionsTest extends WAUnitTestCase
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @param $tableExist
|
||||
* @param $expectedInvocation
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Setup\Actions::setupModule
|
||||
* @dataProvider canSetupModuleDataProvider
|
||||
* @covers \D3\Webauthn\Setup\Actions::runModuleMigrations()
|
||||
*/
|
||||
public function canSetupModule($tableExist, $expectedInvocation)
|
||||
public function canRunModuleMigrations()
|
||||
{
|
||||
/** @var Actions|MockObject $sut */
|
||||
$sut = $this->getMockBuilder(Actions::class)
|
||||
->onlyMethods(['tableExists', 'executeSQL'])
|
||||
->getMock();
|
||||
$sut->method('tableExists')->willReturn($tableExist);
|
||||
$sut->expects($expectedInvocation)->method('executeSQL')->willReturn(true);
|
||||
|
||||
$this->callMethod(
|
||||
$sut,
|
||||
'setupModule'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function canSetupModuleDataProvider(): array
|
||||
{
|
||||
return [
|
||||
'table exist' => [true, $this->never()],
|
||||
'table not exist' => [false, $this->once()],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Setup\Actions::tableExists
|
||||
*/
|
||||
public function canCheckTableExists()
|
||||
{
|
||||
$expected = true;
|
||||
|
||||
/** @var DbMetaDataHandler|MockObject $DbMetaDataMock */
|
||||
$DbMetaDataMock = $this->getMockBuilder(DbMetaDataHandler::class)
|
||||
->onlyMethods(['tableExists'])
|
||||
->getMock();
|
||||
$DbMetaDataMock->expects($this->once())->method('tableExists')->willReturn($expected);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.DbMetaDataHandler::class, $DbMetaDataMock);
|
||||
|
||||
/** @var Actions $sut */
|
||||
$sut = oxNew(Actions::class);
|
||||
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$this->callMethod(
|
||||
$sut,
|
||||
'tableExists',
|
||||
['testTable']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Setup\Actions::d3GetDb
|
||||
*/
|
||||
public function d3GetDbReturnsRightInstance()
|
||||
{
|
||||
$sut = oxNew(Actions::class);
|
||||
|
||||
$this->assertInstanceOf(
|
||||
DatabaseInterface::class,
|
||||
$this->callMethod(
|
||||
$sut,
|
||||
'd3GetDb'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Setup\Actions::executeSQL
|
||||
*/
|
||||
public function canExecuteSQL()
|
||||
{
|
||||
/** @var Database|MockObject $dbMock */
|
||||
$dbMock = $this->getMockBuilder(Database::class)
|
||||
/** @var Migrations|MockObject $migrationMock */
|
||||
$migrationMock = $this->getMockBuilder(Migrations::class)
|
||||
->onlyMethods(['execute'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$dbMock->expects($this->once())->method('execute');
|
||||
|
||||
$sut = $this->getMockBuilder(Actions::class)
|
||||
->onlyMethods(['d3GetDb'])
|
||||
->getMock();
|
||||
$sut->method('d3GetDb')->willReturn($dbMock);
|
||||
|
||||
$this->callMethod(
|
||||
$sut,
|
||||
'executeSQL',
|
||||
['query']
|
||||
$migrationMock->expects($this->atLeastOnce())->method('execute')->with(
|
||||
$this->identicalTo('migrations:migrate'),
|
||||
$this->identicalTo('d3webauthn')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Setup\Actions::fieldExists
|
||||
*/
|
||||
public function canCheckFieldExists()
|
||||
{
|
||||
$expected = true;
|
||||
|
||||
/** @var DbMetaDataHandler|MockObject $DbMetaDataMock */
|
||||
$DbMetaDataMock = $this->getMockBuilder(DbMetaDataHandler::class)
|
||||
->onlyMethods(['fieldExists'])
|
||||
/** @var MigrationsBuilder|MockObject $migrationsBuilderMock */
|
||||
$migrationsBuilderMock = $this->getMockBuilder(MigrationsBuilder::class)
|
||||
->onlyMethods(['build'])
|
||||
->getMock();
|
||||
$DbMetaDataMock->expects($this->once())->method('fieldExists')->willReturn($expected);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.DbMetaDataHandler::class, $DbMetaDataMock);
|
||||
$migrationsBuilderMock->method('build')->willReturn($migrationMock);
|
||||
d3GetOxidDIC()->set('d3ox.webauthn.'.MigrationsBuilder::class, $migrationsBuilderMock);
|
||||
|
||||
/** @var Actions $sut */
|
||||
$sut = oxNew(Actions::class);
|
||||
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$this->callMethod(
|
||||
$sut,
|
||||
'fieldExists',
|
||||
['testField', 'testTable']
|
||||
)
|
||||
'runModuleMigrations'
|
||||
);
|
||||
}
|
||||
|
||||
|
303
src/tests/unit/migration/data/Version20230209212939Test.php
Normal file
303
src/tests/unit/migration/data/Version20230209212939Test.php
Normal file
@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* https://www.d3data.de
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||
* @link https://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace D3\Webauthn\tests\unit\migration\data;
|
||||
|
||||
use D3\TestingTools\Development\CanAccessRestricted;
|
||||
use D3\Webauthn\Migrations\Version20230209212939;
|
||||
use D3\Webauthn\tests\unit\WAUnitTestCase;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\MySQL57Platform;
|
||||
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\MySqlSchemaManager;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\Migrations\Configuration\Configuration;
|
||||
use Doctrine\Migrations\Version\Version;
|
||||
use Generator;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use ReflectionException;
|
||||
|
||||
class Version20230209212939Test extends WAUnitTestCase
|
||||
{
|
||||
use CanAccessRestricted;
|
||||
|
||||
/** @var Version20230209212939 */
|
||||
protected $sut;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
/** @var AbstractPlatform|MockObject $databasePlatformMock */
|
||||
$databasePlatformMock = $this->getMockBuilder(MySQL57Platform::class)
|
||||
->getMock();
|
||||
|
||||
/** @var AbstractSchemaManager|MockObject $schemaManagerMock */
|
||||
$schemaManagerMock = $this->getMockBuilder(MySqlSchemaManager::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
/** @var Connection|MockObject $connectionMock */
|
||||
$connectionMock = $this->getMockBuilder(Connection::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getDatabasePlatform', 'getSchemaManager'])
|
||||
->getMock();
|
||||
$connectionMock->method('getDatabasePlatform')->willReturn($databasePlatformMock);
|
||||
$connectionMock->method('getSchemaManager')->willReturn($schemaManagerMock);
|
||||
|
||||
/** @var Configuration|MockObject $configurationMock */
|
||||
$configurationMock = $this->getMockBuilder(Configuration::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getConnection'])
|
||||
->getMock();
|
||||
$configurationMock->method('getConnection')->willReturn($connectionMock);
|
||||
|
||||
/** @var Version|MockObject $versionMock */
|
||||
$versionMock = $this->getMockBuilder(Version::class)
|
||||
->onlyMethods(['getConfiguration'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$versionMock->method('getConfiguration')->willReturn($configurationMock);
|
||||
|
||||
$this->sut = oxNew(Version20230209212939::class, $versionMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Migrations\Version20230209212939::getDescription
|
||||
*/
|
||||
public function canGetDescription()
|
||||
{
|
||||
$this->assertIsString(
|
||||
$this->callMethod(
|
||||
$this->sut,
|
||||
'getDescription'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Migrations\Version20230209212939::up
|
||||
* @dataProvider canUpTableDataProvider
|
||||
*/
|
||||
public function canUpTable($tableExist, $invocationCount)
|
||||
{
|
||||
/** @var Table|MockObject $tableMock */
|
||||
$tableMock = $this->getMockBuilder(Table::class)
|
||||
->onlyMethods(['hasColumn', 'hasPrimaryKey', 'hasIndex'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$tableMock->method('hasColumn')->willReturn(true);
|
||||
$tableMock->method('hasPrimaryKey')->willReturn(true);
|
||||
$tableMock->method('hasIndex')->willReturn(true);
|
||||
|
||||
/** @var Schema|MockObject $schemaMock */
|
||||
$schemaMock = $this->getMockBuilder(Schema::class)
|
||||
->onlyMethods(['hasTable', 'createTable', 'getTable'])
|
||||
->getMock();
|
||||
$schemaMock->method('hasTable')->willReturn($tableExist);
|
||||
$schemaMock->expects($invocationCount)->method('createTable')->willReturn($tableMock);
|
||||
$schemaMock->method('getTable')->willReturn($tableMock);
|
||||
|
||||
$this->callMethod(
|
||||
$this->sut,
|
||||
'up',
|
||||
[$schemaMock]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function canUpTableDataProvider(): Generator
|
||||
{
|
||||
yield 'table not exist' => [false, $this->once()];
|
||||
yield 'table exist' => [true, $this->never()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Migrations\Version20230209212939::up
|
||||
* @dataProvider canUpColumnDataProvider
|
||||
*/
|
||||
public function canUpColumn($columnExist, $invocationCount)
|
||||
{
|
||||
/** @var Column|MockObject $columnMock */
|
||||
$columnMock = $this->getMockBuilder(Column::class)
|
||||
->onlyMethods(['setLength'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$columnMock->method('setLength')->willReturnSelf();
|
||||
|
||||
/** @var Table|MockObject $tableMock */
|
||||
$tableMock = $this->getMockBuilder(Table::class)
|
||||
->onlyMethods(['hasColumn', 'addColumn', 'hasPrimaryKey', 'hasIndex'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$tableMock->method('hasColumn')->willReturn($columnExist);
|
||||
$tableMock->expects($invocationCount)->method('addColumn')->willReturn($columnMock);
|
||||
$tableMock->method('hasPrimaryKey')->willReturn(true);
|
||||
$tableMock->method('hasIndex')->willReturn(true);
|
||||
|
||||
/** @var Schema|MockObject $schemaMock */
|
||||
$schemaMock = $this->getMockBuilder(Schema::class)
|
||||
->onlyMethods(['hasTable', 'getTable'])
|
||||
->getMock();
|
||||
$schemaMock->method('hasTable')->willReturn(true);
|
||||
$schemaMock->method('getTable')->willReturn($tableMock);
|
||||
|
||||
$this->callMethod(
|
||||
$this->sut,
|
||||
'up',
|
||||
[$schemaMock]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function canUpColumnDataProvider(): Generator
|
||||
{
|
||||
yield 'column not exist' => [false, $this->atLeast(7)];
|
||||
yield 'column exist' => [true, $this->never()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Migrations\Version20230209212939::up
|
||||
* @dataProvider canUpPrimaryKeyDataProvider
|
||||
*/
|
||||
public function canUpPrimaryKey($pKeyExist, $invocationCount)
|
||||
{
|
||||
/** @var Table|MockObject $tableMock */
|
||||
$tableMock = $this->getMockBuilder(Table::class)
|
||||
->onlyMethods(['hasColumn', 'addColumn', 'hasPrimaryKey', 'hasIndex', 'setPrimaryKey'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$tableMock->method('hasColumn')->willReturn(true);
|
||||
$tableMock->method('hasPrimaryKey')->willReturn($pKeyExist);
|
||||
$tableMock->method('hasIndex')->willReturn(true);
|
||||
$tableMock->expects($invocationCount)->method('setPrimaryKey');
|
||||
|
||||
/** @var Schema|MockObject $schemaMock */
|
||||
$schemaMock = $this->getMockBuilder(Schema::class)
|
||||
->onlyMethods(['hasTable', 'getTable'])
|
||||
->getMock();
|
||||
$schemaMock->method('hasTable')->willReturn(true);
|
||||
$schemaMock->method('getTable')->willReturn($tableMock);
|
||||
|
||||
$this->callMethod(
|
||||
$this->sut,
|
||||
'up',
|
||||
[$schemaMock]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function canUpPrimaryKeyDataProvider(): Generator
|
||||
{
|
||||
yield 'pk not exist' => [false, $this->once()];
|
||||
yield 'pk exist' => [true, $this->never()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Migrations\Version20230209212939::up
|
||||
* @dataProvider canUpIndexDataProvider
|
||||
*/
|
||||
public function canUpIndex($indexExist, $invocationCount)
|
||||
{
|
||||
/** @var Table|MockObject $tableMock */
|
||||
$tableMock = $this->getMockBuilder(Table::class)
|
||||
->onlyMethods(['hasColumn', 'addColumn', 'hasPrimaryKey', 'hasIndex', 'addIndex', 'setComment'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$tableMock->method('hasColumn')->willReturn(true);
|
||||
$tableMock->method('hasPrimaryKey')->willReturn(true);
|
||||
$tableMock->method('hasIndex')->willReturn($indexExist);
|
||||
$tableMock->expects($invocationCount)->method('addIndex');
|
||||
$tableMock->expects($this->once())->method('setComment');
|
||||
|
||||
/** @var Schema|MockObject $schemaMock */
|
||||
$schemaMock = $this->getMockBuilder(Schema::class)
|
||||
->onlyMethods(['hasTable', 'getTable'])
|
||||
->getMock();
|
||||
$schemaMock->method('hasTable')->willReturn(true);
|
||||
$schemaMock->method('getTable')->willReturn($tableMock);
|
||||
|
||||
$this->callMethod(
|
||||
$this->sut,
|
||||
'up',
|
||||
[$schemaMock]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function canUpIndexDataProvider(): Generator
|
||||
{
|
||||
yield 'index not exist' => [false, $this->atLeast(2)];
|
||||
yield 'index exist' => [true, $this->never()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
* @covers \D3\Webauthn\Migrations\Version20230209212939::down
|
||||
* @dataProvider canDownTableDataProvider
|
||||
*/
|
||||
public function canDownTable($tableExist, $invocationCount)
|
||||
{
|
||||
/** @var Schema|MockObject $schemaMock */
|
||||
$schemaMock = $this->getMockBuilder(Schema::class)
|
||||
->onlyMethods(['hasTable', 'dropTable'])
|
||||
->getMock();
|
||||
$schemaMock->method('hasTable')->willReturn($tableExist);
|
||||
$schemaMock->expects($invocationCount)->method('dropTable');
|
||||
|
||||
$this->callMethod(
|
||||
$this->sut,
|
||||
'down',
|
||||
[$schemaMock]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public function canDownTableDataProvider(): Generator
|
||||
{
|
||||
yield 'table exist' => [true, $this->once()];
|
||||
yield 'table not exist' => [false, $this->never()];
|
||||
}
|
||||
}
|
8
wishlist.md
Normal file
8
wishlist.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Wish list for future releases
|
||||
|
||||
- a more intuitive login process (instead of simply having to leave the password field blank)
|
||||
- forcing the user to use Webauthn
|
||||
- General avoidance of passwords, login exclusively with FIDO2
|
||||
- However, a restore strategy is required in the event that a key is no longer available.
|
||||
- Alternatively, a random password unknown to the customer can be set, which is changed each time the customer logs on via Webauthn.
|
||||
- Implementation of resident keys for logging in completely without user input (no user name required any more)
|
Reference in New Issue
Block a user