Skip to content

Commit

Permalink
Split User Repository Registration mode (#385)
Browse files Browse the repository at this point in the history
Split User Repository Registration mode
  • Loading branch information
Spomky authored Mar 11, 2023
1 parent dcd5852 commit c000f97
Show file tree
Hide file tree
Showing 15 changed files with 72 additions and 53 deletions.
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ parameters:
message: '#Strict comparison using === between mixed and null will always evaluate to false\.#'
path: src/metadata-service/src/Statement/StatusReport.php
count: 1
- '#Fetching class constant class of deprecated class Webauthn\\Bundle\\Repository\\PublicKeyCredentialUserEntityRepository.*#'
- '#.*Binding.*#'
- '#Parameter \#\d+ \$.* of .* expects .*, .* given\.#'
- '#Property .* does not accept .*\|false\.#'
Expand Down
4 changes: 2 additions & 2 deletions src/symfony/src/Controller/AssertionControllerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\Bundle\CredentialOptionsBuilder\ProfileBasedRequestOptionsBuilder;
use Webauthn\Bundle\CredentialOptionsBuilder\PublicKeyCredentialRequestOptionsBuilder;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Security\Handler\FailureHandler;
use Webauthn\Bundle\Security\Handler\RequestOptionsHandler;
use Webauthn\Bundle\Security\Handler\SuccessHandler;
Expand All @@ -32,7 +32,7 @@ public function __construct(
private readonly PublicKeyCredentialRequestOptionsFactory $publicKeyCredentialRequestOptionsFactory,
private readonly PublicKeyCredentialLoader $publicKeyCredentialLoader,
private readonly AuthenticatorAssertionResponseValidator $attestationResponseValidator,
private readonly PublicKeyCredentialUserEntityRepository $publicKeyCredentialUserEntityRepository,
private readonly PublicKeyCredentialUserEntityRepositoryInterface $publicKeyCredentialUserEntityRepository,
private readonly PublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository
) {
$this->logger = new NullLogger();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs;
use Webauthn\Bundle\Dto\ServerPublicKeyCredentialRequestOptionsRequest;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory;
use Webauthn\PublicKeyCredentialDescriptor;
use Webauthn\PublicKeyCredentialRequestOptions;
Expand All @@ -25,7 +25,7 @@ final class ProfileBasedRequestOptionsBuilder implements PublicKeyCredentialRequ
public function __construct(
private readonly SerializerInterface $serializer,
private readonly ValidatorInterface $validator,
private readonly PublicKeyCredentialUserEntityRepository $userEntityRepository,
private readonly PublicKeyCredentialUserEntityRepositoryInterface $userEntityRepository,
private readonly PublicKeyCredentialSourceRepository $credentialSourceRepository,
private readonly PublicKeyCredentialRequestOptionsFactory $publicKeyCredentialRequestOptionsFactory,
private readonly string $profile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
use Webauthn\Bundle\CredentialOptionsBuilder\ProfileBasedCreationOptionsBuilder;
use Webauthn\Bundle\CredentialOptionsBuilder\ProfileBasedRequestOptionsBuilder;
use Webauthn\Bundle\DependencyInjection\Compiler\DynamicRouteCompilerPass;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Security\Guesser\RequestBodyUserEntityGuesser;
use Webauthn\Bundle\Security\Handler\DefaultCreationOptionsHandler;
use Webauthn\Bundle\Security\Handler\DefaultFailureHandler;
Expand Down Expand Up @@ -465,7 +465,7 @@ private function getAssertionOptionsBuilderId(
->setArguments([
new Reference(SerializerInterface::class),
new Reference(ValidatorInterface::class),
new Reference(PublicKeyCredentialUserEntityRepository::class),
new Reference(PublicKeyCredentialUserEntityRepositoryInterface::class),
new Reference(PublicKeyCredentialSourceRepository::class),
new Reference(PublicKeyCredentialRequestOptionsFactory::class),
$config['profile'],
Expand Down
4 changes: 3 additions & 1 deletion src/symfony/src/DependencyInjection/WebauthnExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use Webauthn\Bundle\DependencyInjection\Compiler\LoggerSetterCompilerPass;
use Webauthn\Bundle\Doctrine\Type as DbalType;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Service\PublicKeyCredentialCreationOptionsFactory;
use Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory;
use Webauthn\Counter\CounterChecker;
Expand Down Expand Up @@ -92,6 +93,7 @@ public function load(array $configs, ContainerBuilder $container): void

$container->setAlias(PublicKeyCredentialSourceRepository::class, $config['credential_repository']);
$container->setAlias(PublicKeyCredentialUserEntityRepository::class, $config['user_repository']);
$container->setAlias(PublicKeyCredentialUserEntityRepositoryInterface::class, $config['user_repository']);

if ($config['token_binding_support_handler'] !== null) {
$container->setAlias(TokenBindingHandler::class, $config['token_binding_support_handler']);
Expand Down Expand Up @@ -242,7 +244,7 @@ private function loadRequestControllersSupport(ContainerBuilder $container, arra
->setArguments([
new Reference(SerializerInterface::class),
new Reference(ValidatorInterface::class),
new Reference(PublicKeyCredentialUserEntityRepository::class),
new Reference(PublicKeyCredentialUserEntityRepositoryInterface::class),
new Reference(PublicKeyCredentialSourceRepository::class),
new Reference(PublicKeyCredentialRequestOptionsFactory::class),
$requestConfig['profile'],
Expand Down
14 changes: 14 additions & 0 deletions src/symfony/src/Repository/CanRegisterUserEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Webauthn\Bundle\Repository;

use Webauthn\PublicKeyCredentialUserEntity;

interface CanRegisterUserEntity
{
public function generateNextUserEntityId(): string;

public function saveUserEntity(PublicKeyCredentialUserEntity $userEntity): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* This dummy Public Key User Entity Repository is set to allow the bundle to be installed even if the real repository
* is not set in the configuration file. This class shall be replaced in favour of your own implementation.
*/
class DummyPublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepository, CanLogData
class DummyPublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepositoryInterface, CanLogData
{
public function __construct(
private LoggerInterface $logger = new NullLogger()
Expand Down Expand Up @@ -45,24 +45,4 @@ public function findOneByUserHandle(string $userHandle): ?PublicKeyCredentialUse
'You are using the DummyPublicKeyCredentialUserEntityRepository service. Please create your own repository'
);
}

public function generateNextUserEntityId(): string
{
$this->logger->critical(
'Please change the Public Key Credential User Entity Repository in the bundle configuration. See https://webauthn-doc.spomky-labs.com/the-webauthn-server/the-symfony-way#repositories-1'
);
throw new RuntimeException(
'You are using the DummyPublicKeyCredentialUserEntityRepository service. Please create your own repository'
);
}

public function saveUserEntity(PublicKeyCredentialUserEntity $userEntity): void
{
$this->logger->critical(
'Please change the Public Key Credential User Entity Repository in the bundle configuration. See https://webauthn-doc.spomky-labs.com/the-webauthn-server/the-symfony-way#repositories-1'
);
throw new RuntimeException(
'You are using the DummyPublicKeyCredentialUserEntityRepository service. Please create your own repository'
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@

namespace Webauthn\Bundle\Repository;

use Webauthn\PublicKeyCredentialUserEntity;

interface PublicKeyCredentialUserEntityRepository
/**
* @deprecated since 4.6.0, to be removed in 5.0.0. Use {@link PublicKeyCredentialUserEntityRepositoryInterface} and {@link CanRegisterUserEntity} instead.
*/
interface PublicKeyCredentialUserEntityRepository extends PublicKeyCredentialUserEntityRepositoryInterface, CanRegisterUserEntity
{
public function findOneByUsername(string $username): ?PublicKeyCredentialUserEntity;

public function findOneByUserHandle(string $userHandle): ?PublicKeyCredentialUserEntity;

public function generateNextUserEntityId(): string;

public function saveUserEntity(PublicKeyCredentialUserEntity $userEntity): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Webauthn\Bundle\Repository;

use Webauthn\PublicKeyCredentialUserEntity;

interface PublicKeyCredentialUserEntityRepositoryInterface
{
public function findOneByUsername(string $username): ?PublicKeyCredentialUserEntity;

public function findOneByUserHandle(string $userHandle): ?PublicKeyCredentialUserEntity;
}
8 changes: 4 additions & 4 deletions src/symfony/src/Resources/config/security.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\Bundle\DependencyInjection\Factory\Security\WebauthnFactory;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Security\Authorization\Voter\IsUserPresentVoter;
use Webauthn\Bundle\Security\Authorization\Voter\IsUserVerifiedVoter;
use Webauthn\Bundle\Security\Guesser\CurrentUserEntityGuesser;
Expand Down Expand Up @@ -47,7 +47,7 @@
), abstract_arg(
'Options Storage'
), abstract_arg('Secured Relying Party IDs'), service(PublicKeyCredentialSourceRepository::class), service(
PublicKeyCredentialUserEntityRepository::class
PublicKeyCredentialUserEntityRepositoryInterface::class
), service(PublicKeyCredentialLoader::class), service(
AuthenticatorAssertionResponseValidator::class
), service(AuthenticatorAttestationResponseValidator::class), ]
Expand All @@ -56,11 +56,11 @@
->args([[], // Firewall settings
abstract_arg('Firewall name'), service('security.http_utils'), ]);
$container->set(CurrentUserEntityGuesser::class)->args(
[service(TokenStorageInterface::class), service(PublicKeyCredentialUserEntityRepository::class)]
[service(TokenStorageInterface::class), service(PublicKeyCredentialUserEntityRepositoryInterface::class)]
);
$container->set(RequestBodyUserEntityGuesser::class)->args(
[service(SerializerInterface::class), service(ValidatorInterface::class), service(
PublicKeyCredentialUserEntityRepository::class
PublicKeyCredentialUserEntityRepositoryInterface::class
), ]
);
};
4 changes: 2 additions & 2 deletions src/symfony/src/Resources/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use Webauthn\Bundle\Controller\DummyControllerFactory;
use Webauthn\Bundle\Repository\DummyPublicKeyCredentialSourceRepository;
use Webauthn\Bundle\Repository\DummyPublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Routing\Loader;
use Webauthn\Bundle\Service\PublicKeyCredentialCreationOptionsFactory;
use Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory;
Expand Down Expand Up @@ -135,7 +135,7 @@
service(PublicKeyCredentialRequestOptionsFactory::class),
service(PublicKeyCredentialLoader::class),
service(AuthenticatorAssertionResponseValidator::class),
service(PublicKeyCredentialUserEntityRepository::class),
service(PublicKeyCredentialUserEntityRepositoryInterface::class),
service(PublicKeyCredentialSourceRepository::class),
]);

Expand Down
4 changes: 2 additions & 2 deletions src/symfony/src/Security/Guesser/CurrentUserEntityGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Webauthn\Bundle\Exception\MissingUserEntityException;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\PublicKeyCredentialUserEntity;

final class CurrentUserEntityGuesser implements UserEntityGuesser
{
public function __construct(
private readonly TokenStorageInterface $tokenStorage,
private readonly PublicKeyCredentialUserEntityRepository $userEntityRepository
private readonly PublicKeyCredentialUserEntityRepositoryInterface $userEntityRepository
) {
}

Expand Down
14 changes: 11 additions & 3 deletions src/symfony/src/Security/Guesser/RequestBodyUserEntityGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Webauthn\Bundle\Dto\ServerPublicKeyCredentialCreationOptionsRequest;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Exception\MissingUserEntityException;
use Webauthn\Bundle\Repository\CanRegisterUserEntity;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Exception\InvalidDataException;
use Webauthn\PublicKeyCredentialUserEntity;

Expand All @@ -18,7 +20,7 @@ final class RequestBodyUserEntityGuesser implements UserEntityGuesser
public function __construct(
private readonly SerializerInterface $serializer,
private readonly ValidatorInterface $validator,
private readonly PublicKeyCredentialUserEntityRepository $userEntityRepository
private readonly PublicKeyCredentialUserEntityRepositoryInterface $userEntityRepository
) {
}

Expand Down Expand Up @@ -48,8 +50,14 @@ public function findUserEntity(Request $request): PublicKeyCredentialUserEntity
}

$existingUserEntity = $this->userEntityRepository->findOneByUsername($dto->username);
if ($existingUserEntity !== null) {
return $existingUserEntity;
}

return $existingUserEntity ?? PublicKeyCredentialUserEntity::create(
if (! $this->userEntityRepository instanceof CanRegisterUserEntity) {
throw MissingUserEntityException::create('Unable to find the user entity');
}
return PublicKeyCredentialUserEntity::create(
$dto->username,
$this->userEntityRepository->generateNextUserEntityId(),
$dto->displayName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Webauthn\Bundle\Security\Http\Authenticator;

use LogicException;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -23,7 +24,8 @@
use Webauthn\AuthenticatorAttestationResponse;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\Bundle\Exception\MissingUserEntityException;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository;
use Webauthn\Bundle\Repository\CanRegisterUserEntity;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Security\Authentication\Token\WebauthnToken;
use Webauthn\Bundle\Security\Http\Authenticator\Passport\Credentials\WebauthnCredentials;
use Webauthn\Bundle\Security\Storage\OptionsStorage;
Expand Down Expand Up @@ -51,7 +53,7 @@ public function __construct(
private readonly OptionsStorage $optionsStorage,
private readonly array $securedRelyingPartyIds,
private readonly PublicKeyCredentialSourceRepository $credentialSourceRepository,
private readonly PublicKeyCredentialUserEntityRepository $credentialUserEntityRepository,
private readonly PublicKeyCredentialUserEntityRepositoryInterface $credentialUserEntityRepository,
private readonly PublicKeyCredentialLoader $publicKeyCredentialLoader,
private readonly AuthenticatorAssertionResponseValidator $assertionResponseValidator,
private readonly AuthenticatorAttestationResponseValidator $attestationResponseValidator
Expand Down Expand Up @@ -203,6 +205,9 @@ private function processWithAssertion(Request $request): Passport

private function processWithAttestation(Request $request): Passport
{
if (! $this->credentialUserEntityRepository instanceof CanRegisterUserEntity) {
throw new LogicException('The credential source repository must implement CanRegisterUserEntity');
}
try {
$format = method_exists(
$request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
use ParagonIE\ConstantTime\Base64UrlSafe;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Uid\Uuid;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository as PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\Bundle\Repository\CanRegisterUserEntity;
use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface;
use Webauthn\PublicKeyCredentialUserEntity;

final class PublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepositoryInterface
final class PublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepositoryInterface, CanRegisterUserEntity
{
public function __construct(
private readonly CacheItemPoolInterface $cacheItemPool
Expand Down

0 comments on commit c000f97

Please sign in to comment.