diff --git a/config/install/relaxed.settings.yml b/config/install/relaxed.settings.yml index c646fd03..f316b86c 100644 --- a/config/install/relaxed.settings.yml +++ b/config/install/relaxed.settings.yml @@ -14,6 +14,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth POST: supported_formats: - json @@ -45,6 +51,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth PUT: supported_formats: - json @@ -64,6 +76,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:changes: GET: supported_formats: @@ -71,6 +89,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:doc: HEAD: supported_formats: @@ -85,6 +109,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth PUT: supported_formats: - json @@ -105,6 +135,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:local:doc: HEAD: supported_formats: @@ -118,6 +154,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth PUT: supported_formats: - json @@ -131,6 +173,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:session: GET: supported_formats: @@ -138,6 +186,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:ensure_full_commit: POST: supported_formats: @@ -145,6 +199,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:all_dbs: GET: supported_formats: @@ -152,6 +212,12 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth relaxed:all_docs: GET: supported_formats: @@ -159,3 +225,9 @@ resources: supported_auth: - cookie - basic_auth + OPTIONS: + supported_formats: + - json + supported_auth: + - cookie + - basic_auth diff --git a/relaxed.services.yml b/relaxed.services.yml index 02b14e02..04425eef 100644 --- a/relaxed.services.yml +++ b/relaxed.services.yml @@ -101,4 +101,9 @@ services: class: Drupal\relaxed\CouchdbReplicator arguments: ['@config.factory'] tags: - - {name: workspace_replicator, priority: 10} \ No newline at end of file + - {name: workspace_replicator, priority: 10} + relaxed.options_request_listener: + class: Drupal\relaxed\EventSubscriber\OptionsRequestSubscriber + arguments: ['@router.route_provider'] + tags: + - { name: event_subscriber } \ No newline at end of file diff --git a/src/EventSubscriber/OptionsRequestSubscriber.php b/src/EventSubscriber/OptionsRequestSubscriber.php new file mode 100644 index 00000000..1d752e51 --- /dev/null +++ b/src/EventSubscriber/OptionsRequestSubscriber.php @@ -0,0 +1,77 @@ +routeProvider = $routeProvider; + } + + /** + * Tries to handle the options request. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The request event. + */ + public function onRequest(GetResponseEvent $event) { + if ($event->getRequest()->isMethod('OPTIONS')) { + $routes = $this->routeProvider->getRouteCollectionForRequest($event->getRequest()); + // In case we don't have any routes, a 403 should be thrown by the normal + // request handling. + $methods = []; + if (count($routes) > 0) { + $current_route_name = Url::createFromRequest($event->getRequest())->getRouteName(); + $current_route = $this->routeProvider->getRouteByName($current_route_name); + foreach ($routes as $route) { + if ($current_route->getPath() !== $route->getPath()) { + continue; + } + $methods = array_merge($methods, $route->getMethods()); + } + $response = new Response('', 200, ['Allow' => implode(', ', $methods)]); + $event->setResponse($response); + } + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + // Set a high priority so its executed before routing. + $events[KernelEvents::REQUEST][] = ['onRequest', 1000] ; + return $events; + } + +} diff --git a/src/Plugin/rest/resource/ChangesResource.php b/src/Plugin/rest/resource/ChangesResource.php index ae4abed3..96c628fb 100644 --- a/src/Plugin/rest/resource/ChangesResource.php +++ b/src/Plugin/rest/resource/ChangesResource.php @@ -7,6 +7,7 @@ namespace Drupal\relaxed\Plugin\rest\resource; +use Drupal\multiversion\Entity\WorkspaceInterface; use Drupal\relaxed\Changes\Changes; use Drupal\rest\ResourceResponse; use Symfony\Component\HttpFoundation\Request; @@ -27,8 +28,14 @@ */ class ChangesResource extends ResourceBase { + /** + * @param $workspace + * + * @return ResourceResponse + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ public function get($workspace) { - if (is_string($workspace)) { + if (!$workspace instanceof WorkspaceInterface) { throw new NotFoundHttpException(); } diff --git a/src/Plugin/rest/resource/ResourceBase.php b/src/Plugin/rest/resource/ResourceBase.php index 48697cf8..1c33c55d 100644 --- a/src/Plugin/rest/resource/ResourceBase.php +++ b/src/Plugin/rest/resource/ResourceBase.php @@ -1,15 +1,54 @@ addCacheableDependency($workspace); + + return $response; + } + + /** + * {@inheritdoc} + */ + public function availableMethods() { + $methods = parent::availableMethods(); + + // Indiscriminately patch in OPTIONS as an accepted method, to facilitate + // CORS for all RELAXed endpoints. + if (!in_array('OPTIONS', $methods)) { + $methods[] = 'OPTIONS'; + } + + return $methods; + } + /** * {@inheritdoc} */ @@ -29,7 +68,7 @@ public function routes() { // Allow pull or push permissions depending on the method. $permissions = 'perform push replication'; - if ($method === 'GET') { + if (in_array($method, ['GET', 'OPTIONS'])) { $permissions .= '+perform pull replication'; } @@ -81,6 +120,7 @@ public function routes() { break; case 'GET': + case 'OPTIONS': $collection->add("$route_name.$method", $route); break;