diff --git a/composer.json b/composer.json index b067f8ed..89d25ece 100644 --- a/composer.json +++ b/composer.json @@ -137,9 +137,9 @@ ], "test:unit:pretty": "phpdbg -qrr vendor/bin/phpunit --coverage-html build/coverage --order-by=random", - "infect": "infection --threads=4 --min-msi=70 --log-verbosity=default --only-covered", - "infect:ci": "infection --threads=4 --min-msi=70 --log-verbosity=default --only-covered --coverage=build", - "infect:show": "infection --threads=4 --min-msi=70 --log-verbosity=default --only-covered --show-mutations", + "infect": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered", + "infect:ci": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --coverage=build", + "infect:show": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --show-mutations", "infect:test": [ "@test:unit:ci", "@infect:ci" diff --git a/module/Rest/src/Authentication/AuthenticationPluginManagerFactory.php b/module/Rest/src/Authentication/AuthenticationPluginManagerFactory.php index ae697a00..5326eee4 100644 --- a/module/Rest/src/Authentication/AuthenticationPluginManagerFactory.php +++ b/module/Rest/src/Authentication/AuthenticationPluginManagerFactory.php @@ -4,28 +4,12 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Rest\Authentication; use Interop\Container\ContainerInterface; -use Interop\Container\Exception\ContainerException; -use Zend\ServiceManager\Exception\ServiceNotCreatedException; -use Zend\ServiceManager\Exception\ServiceNotFoundException; -use Zend\ServiceManager\Factory\FactoryInterface; -class AuthenticationPluginManagerFactory implements FactoryInterface +class AuthenticationPluginManagerFactory { - /** - * Create an object - * - * @param ContainerInterface $container - * @param string $requestedName - * @param null|array $options - * @return object - * @throws ServiceNotFoundException if unable to resolve the service. - * @throws ServiceNotCreatedException if an exception is raised when - * creating a service. - * @throws ContainerException if any other error occurs - */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container): AuthenticationPluginManager { - $config = $container->get('config') ?? []; + $config = $container->has('config') ? $container->get('config') : []; return new AuthenticationPluginManager($container, $config['auth']['plugins'] ?? []); } } diff --git a/module/Rest/test/Authentication/AuthenticationPluginManagerFactoryTest.php b/module/Rest/test/Authentication/AuthenticationPluginManagerFactoryTest.php index 04f46211..6880cbd3 100644 --- a/module/Rest/test/Authentication/AuthenticationPluginManagerFactoryTest.php +++ b/module/Rest/test/Authentication/AuthenticationPluginManagerFactoryTest.php @@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Rest\Authentication; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Rest\Authentication\AuthenticationPluginManager; use Shlinkio\Shlink\Rest\Authentication\AuthenticationPluginManagerFactory; +use Shlinkio\Shlink\Rest\Authentication\Plugin\AuthenticationPluginInterface; use Zend\ServiceManager\ServiceManager; class AuthenticationPluginManagerFactoryTest extends TestCase @@ -18,12 +19,41 @@ class AuthenticationPluginManagerFactoryTest extends TestCase $this->factory = new AuthenticationPluginManagerFactory(); } - /** @test */ - public function serviceIsProperlyCreated() + /** + * @test + * @dataProvider provideConfigs + */ + public function serviceIsProperlyCreatedWithExpectedPlugins(?array $config, array $expectedPlugins): void { - $instance = $this->factory->__invoke(new ServiceManager(['services' => [ - 'config' => [], - ]]), ''); - $this->assertInstanceOf(AuthenticationPluginManager::class, $instance); + $instance = ($this->factory)(new ServiceManager(['services' => [ + 'config' => $config, + ]])); + + $this->assertEquals($expectedPlugins, $this->getPlugins($instance)); + } + + private function getPlugins(AuthenticationPluginManager $pluginManager): array + { + return (function () { + return $this->services; + })->call($pluginManager); + } + + public function provideConfigs(): iterable + { + yield [null, []]; + yield [[], []]; + yield [['auth' => []], []]; + yield [['auth' => [ + 'plugins' => [], + ]], []]; + yield [['auth' => [ + 'plugins' => [ + 'services' => $plugins = [ + 'foo' => $this->prophesize(AuthenticationPluginInterface::class)->reveal(), + 'bar' => $this->prophesize(AuthenticationPluginInterface::class)->reveal(), + ], + ], + ]], $plugins]; } } diff --git a/module/Rest/test/Middleware/AuthenticationMiddlewareTest.php b/module/Rest/test/Middleware/AuthenticationMiddlewareTest.php index 94e9b4d7..cd4ab889 100644 --- a/module/Rest/test/Middleware/AuthenticationMiddlewareTest.php +++ b/module/Rest/test/Middleware/AuthenticationMiddlewareTest.php @@ -13,6 +13,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Rest\Action\AuthenticateAction; use Shlinkio\Shlink\Rest\Authentication\Plugin\AuthenticationPluginInterface; use Shlinkio\Shlink\Rest\Authentication\RequestToHttpAuthPlugin; @@ -37,14 +38,19 @@ class AuthenticationMiddlewareTest extends TestCase private $middleware; /** @var ObjectProphecy */ private $requestToPlugin; - - /** @var callable */ - private $dummyMiddleware; + /** @var ObjectProphecy */ + private $logger; public function setUp(): void { $this->requestToPlugin = $this->prophesize(RequestToHttpAuthPluginInterface::class); - $this->middleware = new AuthenticationMiddleware($this->requestToPlugin->reveal(), [AuthenticateAction::class]); + $this->logger = $this->prophesize(LoggerInterface::class); + + $this->middleware = new AuthenticationMiddleware( + $this->requestToPlugin->reveal(), + [AuthenticateAction::class], + $this->logger->reveal() + ); } /** @@ -97,6 +103,10 @@ class AuthenticationMiddlewareTest extends TestCase RouteResult::fromRoute(new Route('bar', $this->getDummyMiddleware()), []) ); $fromRequest = $this->requestToPlugin->fromRequest(Argument::any())->willThrow($e); + $logWarning = $this->logger->warning('Invalid or no authentication provided. {e}', ['e' => $e])->will( + function () { + } + ); /** @var Response\JsonResponse $response */ $response = $this->middleware->process($request, $this->prophesize(RequestHandlerInterface::class)->reveal()); @@ -108,6 +118,7 @@ class AuthenticationMiddlewareTest extends TestCase implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS) ), $payload['message']); $fromRequest->shouldHaveBeenCalledOnce(); + $logWarning->shouldHaveBeenCalledOnce(); } public function provideExceptions(): iterable @@ -124,12 +135,15 @@ class AuthenticationMiddlewareTest extends TestCase RouteResult::class, RouteResult::fromRoute(new Route('bar', $this->getDummyMiddleware()), []) ); + $e = VerifyAuthenticationException::withError('the_error', 'the_message'); $plugin = $this->prophesize(AuthenticationPluginInterface::class); - $verify = $plugin->verify($request)->willThrow( - VerifyAuthenticationException::withError('the_error', 'the_message') - ); + $verify = $plugin->verify($request)->willThrow($e); $fromRequest = $this->requestToPlugin->fromRequest(Argument::any())->willReturn($plugin->reveal()); + $logWarning = $this->logger->warning('Authentication verification failed. {e}', ['e' => $e])->will( + function () { + } + ); /** @var Response\JsonResponse $response */ $response = $this->middleware->process($request, $this->prophesize(RequestHandlerInterface::class)->reveal()); @@ -139,6 +153,7 @@ class AuthenticationMiddlewareTest extends TestCase $this->assertEquals('the_message', $payload['message']); $verify->shouldHaveBeenCalledOnce(); $fromRequest->shouldHaveBeenCalledOnce(); + $logWarning->shouldHaveBeenCalledOnce(); } /** @test */