.../Compiler/AuthenticationProviderPass.php | 24 ++++ core/modules/rest/rest.services.yml | 5 +- .../modules/rest/src/Entity/ConfigDependencies.php | 34 +++--- core/modules/rest/src/RestServiceProvider.php | 60 --------- .../EventSubscriber/DefaultExceptionSubscriber.php | 134 +++++++++++++++++++++ .../Compiler/AuthenticationProviderPassTest.php | 44 +++++++ 6 files changed, 220 insertions(+), 81 deletions(-) diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/AuthenticationProviderPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/AuthenticationProviderPass.php new file mode 100644 index 0000000..d1a8407 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/AuthenticationProviderPass.php @@ -0,0 +1,24 @@ +findTaggedServiceIds('authentication_provider') as $service_id => $attributes) { + $authentication_provider = $attributes[0]['provider_id']; + if ($provider_tag = $container->getDefinition($service_id)->getTag('_provider')) { + $authentication_providers[$authentication_provider] = $provider_tag[0]['provider']; + } + } + $container->setParameter('authentication_providers', $authentication_providers); + } + +} diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index 207333f..bdc3e17 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -1,6 +1,3 @@ -parameters: - rest.dependencies.auth: [] - rest.dependencies.format: [] services: plugin.manager.rest: class: Drupal\rest\Plugin\Type\ResourcePluginManager @@ -35,4 +32,4 @@ services: arguments: ['rest'] rest_resource.dependencies: class: \Drupal\rest\Entity\ConfigDependencies - arguments: ['%rest.dependencies.format%', '%rest.dependencies.auth%'] + arguments: ['%serializer.format_providers%', '%authentication_providers%'] diff --git a/core/modules/rest/src/Entity/ConfigDependencies.php b/core/modules/rest/src/Entity/ConfigDependencies.php index a36a525..12cc380 100644 --- a/core/modules/rest/src/Entity/ConfigDependencies.php +++ b/core/modules/rest/src/Entity/ConfigDependencies.php @@ -11,30 +11,30 @@ class ConfigDependencies { /** - * A list of available rest formats. + * The serialization format providers, keyed by format. * * @var string[] */ - protected $availableRestFormats; + protected $formatProviders; /** - * A list of available authentications. + * The authentication providers, keyed by ID. * * @var string[] */ - protected $availableRestAuthentications; + protected $authProviders; /** * Creates a new ConfigDependencies instance. * - * @param string[] $available_rest_formats - * A list of available rest formats. - * @param string[] $available_rest_authentications - * A list of available authentications. + * @param string[] $format_providers + * The serialization format providers, keyed by format. + * @param string[] $auth_providers + * The authentication providers, keyed by ID. */ - public function __construct(array $available_rest_formats, array $available_rest_authentications) { - $this->availableRestFormats = $available_rest_formats; - $this->availableRestAuthentications = $available_rest_authentications; + public function __construct(array $format_providers, array $auth_providers) { + $this->formatProviders = $format_providers; + $this->authProviders = $auth_providers; } /** @@ -81,15 +81,15 @@ protected function calculateDependenciesForMethodGranularity(RestResourceConfigI // Add dependencies based on the supported authentication providers. foreach ($rest_config->getAuthenticationProviders($request_method) as $auth) { - if (isset($this->availableRestAuthentications[$auth])) { - $module_name = $this->availableRestAuthentications[$auth]; + if (isset($this->authProviders[$auth])) { + $module_name = $this->authProviders[$auth]; $dependencies['module'][] = $module_name; } } // Add dependencies based on the supported authentication formats. foreach ($rest_config->getFormats($request_method) as $format) { - if (isset($this->availableRestFormats[$format])) { - $module_name = $this->availableRestFormats[$format]; + if (isset($this->formatProviders[$format])) { + $module_name = $this->formatProviders[$format]; $dependencies['module'][] = $module_name; } } @@ -150,14 +150,14 @@ public function onDependencyRemovalForMethodGranularity(RestResourceConfigInterf foreach ($dependencies['module'] as $dep_module) { // Check if the removed dependency module contained an authentication // provider. - foreach ($this->availableRestAuthentications as $auth => $auth_module) { + foreach ($this->authProviders as $auth => $auth_module) { if ($dep_module != $auth_module) { continue; } $removed_auth[] = $auth; } // Check if the removed dependency module contained a format. - foreach ($this->availableRestFormats as $format => $format_module) { + foreach ($this->formatProviders as $format => $format_module) { if ($dep_module != $format_module) { continue; } diff --git a/core/modules/rest/src/RestServiceProvider.php b/core/modules/rest/src/RestServiceProvider.php deleted file mode 100644 index 190fac1..0000000 --- a/core/modules/rest/src/RestServiceProvider.php +++ /dev/null @@ -1,60 +0,0 @@ -findTaggedServiceIds('authentication_provider') as $service_id => $service_tags) { - $service_def = $container->findDefinition($service_id); - $service_class = $service_def->getClass(); - if (!empty($service_class)) { - // Extract module name based on class namespace. - list($namespace_base, $module_name) = explode('\\', $service_class); - if (!empty($namespace_base) && ucfirst(strtolower($namespace_base)) == 'Drupal') { - // Remove the 'authentication.' prefix from the provider ID. - $provider_id = substr($service_id, 15); - $auth[$provider_id] = $module_name; - } - } - } - if (!empty($auth)) { - // Register the dependency list in the container. - $container->setParameter('rest.dependencies.auth', $auth); - } - - $formats = []; - // Build a dependency list for available formats. - foreach ($container->findTaggedServiceIds('encoder') as $service_id => $service_tags) { - $service_def = $container->findDefinition($service_id); - $service_class = $service_def->getClass(); - if (!empty($service_class)) { - // Extract module name based on class namespace. - list($namespace_base, $module_name) = explode('\\', $service_class); - if (!empty($namespace_base) && ucfirst(strtolower($namespace_base)) == 'Drupal') { - foreach ($service_tags as $service_tag) { - if (isset($service_tag['format'])) { - $format = $service_tag['format']; - $formats[$format] = $module_name; - } - } - } - } - } - if (!empty($formats)) { - // Register the dependency list in the container. - $container->setParameter('rest.dependencies.format', $formats); - } - } -} diff --git a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php new file mode 100644 index 0000000..b4d59de --- /dev/null +++ b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php @@ -0,0 +1,134 @@ +serializer = $serializer; + $this->serializerFormats = $serializer_formats; + } + + /** + * {@inheritdoc} + */ + protected function getHandledFormats() { + return $this->serializerFormats; + } + + /** + * {@inheritdoc} + */ + protected static function getPriority() { + // This will fire after the most common HTML handler, since HTML requests + // are still more common than HTTP requests. + return -75; + } + + /** + * Handles a 400 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on400(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_BAD_REQUEST); + } + + /** + * Handles a 403 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on403(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_FORBIDDEN); + } + + /** + * Handles a 404 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on404(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_NOT_FOUND); + } + + /** + * Handles a 405 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on405(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_METHOD_NOT_ALLOWED); + } + + /** + * Handles a 406 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on406(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_NOT_ACCEPTABLE); + } + + /** + * Handles a 422 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on422(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_UNPROCESSABLE_ENTITY); + } + + /** + * Sets the Response for the exception event. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The current exception event. + * @param int $status + * The HTTP status code to set for the response. + */ + protected function setEventResponse(GetResponseForExceptionEvent $event, $status) { + $format = $event->getRequest()->getRequestFormat(); + $content = ['message' => $event->getException()->getMessage()]; + $encoded_content = $this->serializer->serialize($content, $format); + $response = new Response($encoded_content, $status); + $event->setResponse($response); + } + +} diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/AuthenticationProviderPassTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/AuthenticationProviderPassTest.php new file mode 100644 index 0000000..6f99d09 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/AuthenticationProviderPassTest.php @@ -0,0 +1,44 @@ +setDefinition('serializer', new Definition(Serializer::class, [[], []])); + + $definition = new Definition('TestClass'); + $definition->addTag('authentication_provider', ['provider_id' => 'bunny_auth']); + $definition->addTag('_provider', ['provider' => 'test_provider_a']); + $container->setDefinition('test_provider_a.authentication.bunny_auth', $definition); + + $definition = new Definition('TestClass'); + $definition->addTag('authentication_provider', ['provider_id' => 'llama_auth', 'priority' => 100]); + $definition->addTag('_provider', ['provider' => 'test_provider_a']); + $container->setDefinition('test_provider_a.authentication.llama_auth', $definition); + + $definition = new Definition('TestClass'); + $definition->addTag('authentication_provider', ['provider_id' => 'camel_auth', 'priority' => -100]); + $definition->addTag('_provider', ['provider' => 'test_provider_b']); + $container->setDefinition('test_provider_b.authentication.camel_auth', $definition); + + $compiler_pass = new AuthenticationProviderPass(); + $compiler_pass->process($container); + + $this->assertEquals(['bunny_auth' => 'test_provider_a', 'llama_auth' => 'test_provider_a', 'camel_auth' => 'test_provider_b'], $container->getParameter('authentication_providers')); + } + +}