Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 @@
|
||||
[![deutsche Version](https://logos.oxidmodule.com/de2_xs.svg)](README.md)
|
||||
[![english version](https://logos.oxidmodule.com/en2_xs.svg)](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).
|
||||
![Passwordless login for OXID eShop](src/logo.png)
|
||||
|
||||
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 @@
|
||||
[![deutsche Version](https://logos.oxidmodule.com/de2_xs.svg)](README.md)
|
||||
[![english version](https://logos.oxidmodule.com/en2_xs.svg)](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).
|
||||
![Passwortloses Anmelden fĂĽr OXID eShop](src/logo.png)
|
||||
|
||||
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)) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($credential);
|
||||
/** @var Webauthn $webauthn */
|
||||
$webauthn = d3GetOxidDIC()->get(Webauthn::class);
|
||||
$webauthn->saveAuthn($credential, Registry::getRequest()->getRequestEscapedParameter('keyname'));
|
||||
}
|
||||
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, $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;
|
||||
@ -60,11 +62,11 @@ class d3webauthnadminlogin extends AdminController
|
||||
public function render(): string
|
||||
{
|
||||
if (d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_AUTH)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_AUTH)
|
||||
) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=admin_start');
|
||||
} elseif (!d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER)
|
||||
) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=login');
|
||||
}
|
||||
@ -107,11 +109,18 @@ class d3webauthnadminlogin extends AdminController
|
||||
$this->addTplParam('isAdmin', isAdmin());
|
||||
} catch (WebauthnException $e) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->setVariable(WebauthnConf::GLOBAL_SWITCH, true);
|
||||
->setVariable(WebauthnConf::GLOBAL_SWITCH, true);
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class)->addErrorToDisplay($e);
|
||||
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)) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.LoggerInterface::class)->debug($credential);
|
||||
$webauthn = d3GetOxidDIC()->get(Webauthn::class);
|
||||
$webauthn->saveAuthn($credential, d3GetOxidDIC()->get('d3ox.webauthn.'.Request::class)->getRequestEscapedParameter('keyname'));
|
||||
}
|
||||
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;
|
||||
@ -62,9 +63,9 @@ class d3webauthnlogin extends FrontendController
|
||||
public function render(): string
|
||||
{
|
||||
if (d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) ||
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) ||
|
||||
!d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER)
|
||||
->hasVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER)
|
||||
) {
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect('index.php?cl=start');
|
||||
}
|
||||
@ -72,7 +73,7 @@ class d3webauthnlogin extends FrontendController
|
||||
$this->generateCredentialRequest();
|
||||
|
||||
$this->addTplParam('navFormParams', d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class)
|
||||
->getVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS));
|
||||
->getVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS));
|
||||
|
||||
return $this->d3CallMockableFunction([FrontendController::class, 'render']);
|
||||
}
|
||||
@ -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);
|
||||
/** @var d3_User_Webauthn $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$userId = null;
|
||||
|
||||
try {
|
||||
/** @var d3_User_Webauthn $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$userId = $this->getUserId();
|
||||
|
||||
$this->handleErrorMessage();
|
||||
@ -158,10 +159,11 @@ class WebauthnLogin
|
||||
{
|
||||
/** @var UtilsView $myUtilsView */
|
||||
$myUtilsView = d3GetOxidDIC()->get('d3ox.webauthn.'.UtilsView::class);
|
||||
/** @var d3_User_Webauthn $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$userId = null;
|
||||
|
||||
try {
|
||||
/** @var d3_User_Webauthn $user */
|
||||
$user = d3GetOxidDIC()->get('d3ox.webauthn.'.User::class);
|
||||
$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.',
|
||||
];
|
||||
|
@ -135,4 +135,11 @@ services:
|
||||
- 'getDb'
|
||||
arguments:
|
||||
- 2
|
||||
shared: true
|
||||
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,29 +66,27 @@ 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)) {
|
||||
$session = d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
|
||||
$this->getClassKey() != 'd3webauthnlogin' ? $this->getClassKey() : 'start'
|
||||
);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER,
|
||||
$userId
|
||||
);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_NAVPARAMS,
|
||||
$this->getParent()->getNavigationParams()
|
||||
);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
|
||||
$this->getParent()->getViewConfig()->getNavFormParams()
|
||||
);
|
||||
if ($this->d3CanUseWebauthn($lgn_user, $userId) && $this->d3HasWebauthnButNotLoggedin($userId)) {
|
||||
$session = d3GetOxidDIC()->get('d3ox.webauthn.'.Session::class);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
|
||||
$this->getClassKey() != 'd3webauthnlogin' ? $this->getClassKey() : 'start'
|
||||
);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER,
|
||||
$userId
|
||||
);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_NAVPARAMS,
|
||||
$this->getParent()->getNavigationParams()
|
||||
);
|
||||
$session->setVariable(
|
||||
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
|
||||
$this->getParent()->getViewConfig()->getNavFormParams()
|
||||
);
|
||||
|
||||
$sUrl = d3GetOxidDIC()->get('d3ox.webauthn.'.Config::class)->getShopHomeUrl() . 'cl=d3webauthnlogin';
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect($sUrl);
|
||||
}
|
||||
$sUrl = d3GetOxidDIC()->get('d3ox.webauthn.'.Config::class)->getShopHomeUrl() . 'cl=d3webauthnlogin';
|
||||
d3GetOxidDIC()->get('d3ox.webauthn.'.Utils::class)->redirect($sUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
BIN
src/logo.png
Normal file
BIN
src/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
@ -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' => 'logo.png',
|
||||
'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
|
||||
|
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']
|
||||
)
|
||||
$this->callMethod(
|
||||
$sut,
|
||||
'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)
|
Loading…
Reference in New Issue
Block a user