From 41512ce51a5f231b5116e434c52dbac07ec44358 Mon Sep 17 00:00:00 2001 From: Sander Verkuil Date: Thu, 31 Mar 2022 16:33:56 +0200 Subject: [PATCH] Add a console user provider (#309) Also write some tests regarding the console user provider, to verify that it actually is working. Fixes #267 Co-authored-by: Damien Harper --- src/Event/ConsoleEventSubscriber.php | 47 ++++++++++++++ src/Resources/config/services.yaml | 8 +++ src/User/ConsoleUserProvider.php | 36 +++++++++++ tests/App/Command/CreatePostCommand.php | 50 +++++++++++++++ tests/App/Kernel.php | 2 + tests/App/config/services.yaml | 6 ++ tests/Console/ConsoleUserProviderTest.php | 78 +++++++++++++++++++++++ tests/DHAuditorBundleTest.php | 4 ++ 8 files changed, 231 insertions(+) create mode 100644 src/Event/ConsoleEventSubscriber.php create mode 100644 src/User/ConsoleUserProvider.php create mode 100644 tests/App/Command/CreatePostCommand.php create mode 100644 tests/App/config/services.yaml create mode 100644 tests/Console/ConsoleUserProviderTest.php diff --git a/src/Event/ConsoleEventSubscriber.php b/src/Event/ConsoleEventSubscriber.php new file mode 100644 index 00000000..3458368c --- /dev/null +++ b/src/Event/ConsoleEventSubscriber.php @@ -0,0 +1,47 @@ +consoleUserProvider = $consoleUserProvider; + $this->configuration = $configuration; + $this->provider = $provider; + } + + public static function getSubscribedEvents(): array + { + return [ + ConsoleEvents::COMMAND => 'registerConsoleUserProvider', + ConsoleEvents::TERMINATE => 'restoreDefaultUserProvider', + ]; + } + + public function registerConsoleUserProvider(ConsoleCommandEvent $commandEvent): void + { + $command = $commandEvent->getCommand(); + $this->consoleUserProvider->setCurrentCommand($command); + $this->configuration->setUserProvider($this->consoleUserProvider); + } + + public function restoreDefaultUserProvider(): void + { + $this->consoleUserProvider->setCurrentCommand(null); + $this->configuration->setUserProvider($this->provider); + } +} diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 308666dc..6276ea6b 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -77,6 +77,9 @@ services: arguments: ['@security.helper', '@DH\Auditor\Provider\Doctrine\Configuration'] dh_auditor.user_provider: '@DH\AuditorBundle\User\UserProvider' + DH\AuditorBundle\User\ConsoleUserProvider: + class: DH\AuditorBundle\User\ConsoleUserProvider + DH\AuditorBundle\Security\SecurityProvider: class: DH\AuditorBundle\Security\SecurityProvider arguments: ['@request_stack', '@security.firewall.map'] @@ -90,5 +93,10 @@ services: DH\AuditorBundle\Event\ViewerEventSubscriber: class: DH\AuditorBundle\Event\ViewerEventSubscriber arguments: ['@DH\Auditor\Auditor'] + tags: + - { name: kernel.event_subscriber } + + DH\AuditorBundle\Event\ConsoleEventSubscriber: + arguments: ['@DH\AuditorBundle\User\ConsoleUserProvider', '@DH\Auditor\Configuration', '@dh_auditor.user_provider'] tags: - { name: kernel.event_subscriber } \ No newline at end of file diff --git a/src/User/ConsoleUserProvider.php b/src/User/ConsoleUserProvider.php new file mode 100644 index 00000000..87c50c00 --- /dev/null +++ b/src/User/ConsoleUserProvider.php @@ -0,0 +1,36 @@ +currentCommand) { + return null; + } + + return new User( + 'command', + $this->currentCommand ?? '' + ); + } + + public function setCurrentCommand(?Command $command): void + { + if (null === $command) { + $this->currentCommand = null; + } else { + $this->currentCommand = $command->getName(); + } + } +} diff --git a/tests/App/Command/CreatePostCommand.php b/tests/App/Command/CreatePostCommand.php new file mode 100644 index 00000000..70144a09 --- /dev/null +++ b/tests/App/Command/CreatePostCommand.php @@ -0,0 +1,50 @@ +doctrineProvider = $doctrineProvider; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $io->write('Creating a new post'); + + $post = new Post(); + $post + ->setTitle('Blameable post') + ->setBody('yet another post') + ->setCreatedAt(new DateTimeImmutable('2020-01-17 22:17:34')) + ; + + $this->doctrineProvider->getAuditingServiceForEntity(Post::class)->getEntityManager()->persist($post); + $this->doctrineProvider->getAuditingServiceForEntity(Post::class)->getEntityManager()->flush(); + + // Symfony 4 compatibility + if (Kernel::MAJOR_VERSION >= 5) { + return self::SUCCESS; + } + + return 0; + } +} diff --git a/tests/App/Kernel.php b/tests/App/Kernel.php index 34b442ba..e8925980 100644 --- a/tests/App/Kernel.php +++ b/tests/App/Kernel.php @@ -41,6 +41,7 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa $container->setParameter('container.dumper.inline_factories', true); $confDir = $this->getProjectDir().'/config'; + $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); $loader->load($confDir.'/{packages}/sf6/*'.self::CONFIG_EXTS, 'glob'); } @@ -81,6 +82,7 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa $container->setParameter('container.dumper.inline_factories', true); $confDir = $this->getProjectDir().'/config'; + $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); $loader->load($confDir.'/{packages}/sf4_5/*'.self::CONFIG_EXTS, 'glob'); } diff --git a/tests/App/config/services.yaml b/tests/App/config/services.yaml new file mode 100644 index 00000000..0c94e5eb --- /dev/null +++ b/tests/App/config/services.yaml @@ -0,0 +1,6 @@ +services: + _defaults: + autowire: true + autoconfigure: true + + DH\AuditorBundle\Tests\App\Command\CreatePostCommand: \ No newline at end of file diff --git a/tests/Console/ConsoleUserProviderTest.php b/tests/Console/ConsoleUserProviderTest.php new file mode 100644 index 00000000..a9a920e0 --- /dev/null +++ b/tests/Console/ConsoleUserProviderTest.php @@ -0,0 +1,78 @@ +createAndInitDoctrineProvider(); + + // declare audited entites + $this->configureEntities(); + + // setup entity and audit schemas + $this->setupEntitySchemas(); + $this->setupAuditSchemas(); + } + + public function testBlameUser(): void + { + $auditingServices = [ + Post::class => $this->provider->getAuditingServiceForEntity(Post::class), + ]; + + $kernel = self::bootKernel(); + $application = new Application($kernel); + $application->setAutoExit(false); + $tester = new ApplicationTester($application); + + $tester->run(['app:post:create']); + + if (Kernel::MAJOR_VERSION >= 5) { + $tester->assertCommandIsSuccessful('Expect it to run'); + } + + $this->flushAll($auditingServices); + // get history + $entries = $this->createReader()->createQuery(Post::class)->execute(); + self::assertSame('app:post:create', $entries[0]->getUsername()); + self::assertSame('command', $entries[0]->getUserId()); + } + + protected function getBundleClass() + { + return DHAuditorBundle::class; + } + + private function createAndInitDoctrineProvider(): void + { + $this->provider = self::$container->get(DoctrineProvider::class); + } +} diff --git a/tests/DHAuditorBundleTest.php b/tests/DHAuditorBundleTest.php index 94dce0df..1160fdb4 100644 --- a/tests/DHAuditorBundleTest.php +++ b/tests/DHAuditorBundleTest.php @@ -12,6 +12,7 @@ use DH\Auditor\Provider\Doctrine\Persistence\Reader\Reader; use DH\AuditorBundle\Controller\ViewerController; use DH\AuditorBundle\DHAuditorBundle; +use DH\AuditorBundle\Event\ConsoleEventSubscriber; use DH\AuditorBundle\Twig\Extension\TwigExtension; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use Nyholm\BundleTest\BaseBundleTestCase; @@ -81,6 +82,9 @@ public function testInitBundle(): void self::assertTrue($container->has(\DH\AuditorBundle\Twig\Extension\TwigExtension::class)); self::assertInstanceOf(TwigExtension::class, $container->get(\DH\AuditorBundle\Twig\Extension\TwigExtension::class)); + + self::assertTrue($container->has(\DH\AuditorBundle\Event\ConsoleEventSubscriber::class)); + self::assertInstanceOf(ConsoleEventSubscriber::class, $container->get(\DH\AuditorBundle\Event\ConsoleEventSubscriber::class)); } protected function getBundleClass()