protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); $resource_id = $input->getArgument('resource-id'); $rest_resources = $this->getRestResources(); $rest_resources_ids = array_merge(array_keys($rest_resources['enabled']), array_keys($rest_resources['disabled'])); if (!$resource_id) { $resource_id = $io->choiceNoList($this->trans('commands.rest.enable.arguments.resource-id'), $rest_resources_ids); } $this->validateRestResource($resource_id, $rest_resources_ids, $this->getTranslator()); $input->setArgument('resource-id', $resource_id); // Calculate states available by resource and generate the question $plugin = $this->pluginManagerRest->getInstance(['id' => $resource_id]); $states = $plugin->availableMethods(); $state = $io->choice($this->trans('commands.rest.enable.arguments.states'), $states); $io->writeln($this->trans('commands.rest.enable.messages.selected-state') . ' ' . $state); // Get Authentication Provider and generate the question $authenticationProviders = $this->authenticationCollector->getSortedProviders(); $authenticationProvidersSelected = $io->choice($this->trans('commands.rest.enable.messages.authentication-providers'), array_keys($authenticationProviders), 0, true); $io->writeln($this->trans('commands.rest.enable.messages.selected-authentication-providers') . ' ' . implode(', ', $authenticationProvidersSelected)); $rest_settings = $this->getRestDrupalConfig(); $rest_settings[$resource_id][$state]['supported_formats'] = $formats; $rest_settings[$resource_id][$state]['supported_auth'] = $authenticationProvidersSelected; $config = $this->configFactory->getEditable('rest.settings'); $config->set('resources', $rest_settings); $config->save(); return 0; }
private function restDetail(DrupalStyle $io, $resource_id) { $config = $this->getRestDrupalConfig(); $plugin = $this->pluginManagerRest->getInstance(['id' => $resource_id]); if (empty($plugin)) { $io->error(sprintf($this->trans('commands.rest.debug.messages.not-found'), $resource_id)); return false; } $resource = $plugin->getPluginDefinition(); $configuration = []; $configuration[] = [$this->trans('commands.rest.debug.messages.id'), $resource['id']]; $configuration[] = [$this->trans('commands.rest.debug.messages.label'), (string) $resource['label']]; $configuration[] = [$this->trans('commands.rest.debug.messages.canonical_url'), $resource['uri_paths']['canonical']]; $configuration[] = [$this->trans('commands.rest.debug.messages.status'), isset($config[$resource['id']]) ? $this->trans('commands.rest.debug.messages.enabled') : $this->trans('commands.rest.debug.messages.disabled')]; $configuration[] = [$this->trans(sprintf('commands.rest.debug.messages.provider', $resource['provider']))]; $io->comment($resource_id); $io->newLine(); $io->table([], $configuration, 'compact'); $tableHeader = [$this->trans('commands.rest.debug.messages.rest-state'), $this->trans('commands.rest.debug.messages.supported-formats'), $this->trans('commands.rest.debug.messages.supported_auth')]; $tableRows = []; foreach ($config[$resource['id']] as $method => $settings) { $tableRows[] = [$method, implode(', ', $settings['supported_formats']), implode(', ', $settings['supported_auth'])]; } $io->table($tableHeader, $tableRows); }
/** * Returns an array of REST permissions. * * @return array */ public function permissions() { $permissions = []; $resources = $this->configFactory->get('rest.settings')->get('resources'); if ($resources && ($enabled = array_intersect_key($this->restPluginManager->getDefinitions(), $resources))) { foreach ($enabled as $key => $resource) { $plugin = $this->restPluginManager->getInstance(['id' => $key]); $permissions = array_merge($permissions, $plugin->permissions()); } } return $permissions; }
/** * Adds routes to enabled REST resources. * * @param \Drupal\Core\Routing\RouteBuildEvent $event * The route building event. */ public function dynamicRoutes(RouteBuildEvent $event) { $collection = $event->getRouteCollection(); $enabled_resources = $this->config->get('rest.settings')->load()->get('resources'); // Iterate over all enabled resource plugins. foreach ($enabled_resources as $id => $enabled_methods) { $plugin = $this->manager->getInstance(array('id' => $id)); foreach ($plugin->routes() as $route) { $operation = $route->getRequirement('_operation'); if ($operation) { $collection->add("services.{$operation}", $route); } } } }
/** * Implements \Drupal\Core\Form\FormInterface::buildForm(). * * @var array $form * The form array. * @var array $form_state * The $form_state array. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * @var string $resource_id * A string that identfies the REST resource. * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ public function buildForm(array $form, FormStateInterface $form_state, $resource_id = NULL) { $plugin = $this->resourcePluginManager->getInstance(array('id' => $resource_id)); if (empty($plugin)) { throw new NotFoundHttpException(); } $config = \Drupal::config('rest.settings')->get('resources') ?: array(); $methods = $plugin->availableMethods(); $pluginDefinition = $plugin->getPluginDefinition(); $form['#tree'] = TRUE; $form['resource_id'] = array('#type' => 'value', '#value' => $resource_id); $form['title'] = array('#markup' => '<h2>' . t('Settings for resource @label', array('@label' => $pluginDefinition['label'])) . '</h2>'); $form['description'] = array('#markup' => '<p>' . t('Here you can restrict which HTTP methods should this resource support.' . ' And within each method, the available serialization formats and ' . 'authentication providers.') . '</p>'); $form['note'] = array('#markup' => '<p>' . t('<b>Note:</b> Leaving all formats unchecked will enable all of them, while leaving all authentication providers unchecked will default to <code>cookie</code>') . '</p>'); $form['methods'] = array('#type' => 'container'); foreach ($methods as $method) { $group = array(); $group[$method] = array('#title' => $method, '#type' => 'checkbox', '#default_value' => isset($config[$resource_id][$method])); $group['settings'] = array('#type' => 'container', '#attributes' => array('style' => 'padding-left:20px')); // Available formats $enabled_formats = array(); if (isset($config[$resource_id][$method]['supported_formats'])) { $enabled_formats = $config[$resource_id][$method]['supported_formats']; } $group['settings']['formats'] = array('#title' => 'Supported formats', '#type' => 'checkboxes', '#options' => array_combine($this->formats, $this->formats), '#multiple' => TRUE, '#default_value' => $enabled_formats); // Authentication providers. $enabled_auth = array(); if (isset($config[$resource_id][$method]['supported_auth'])) { $enabled_auth = $config[$resource_id][$method]['supported_auth']; } $group['settings']['auth'] = array('#title' => 'Authentication providers', '#type' => 'checkboxes', '#options' => array_combine($this->authenticationProviders, $this->authenticationProviders), '#multiple' => TRUE, '#default_value' => $enabled_auth); $form['methods'][$method] = $group; } return parent::buildForm($form, $form_state); }
protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); $resource_id = $input->getArgument('resource-id'); $rest_resources = $this->getRestResources(); $rest_resources_ids = array_merge(array_keys($rest_resources['enabled']), array_keys($rest_resources['disabled'])); if (!$resource_id) { $resource_id = $io->choiceNoList($this->trans('commands.rest.enable.arguments.resource-id'), $rest_resources_ids); } $this->validateRestResource($resource_id, $rest_resources_ids, $this->translator); $input->setArgument('resource-id', $resource_id); // Calculate states available by resource and generate the question. $plugin = $this->pluginManagerRest->getInstance(['id' => $resource_id]); $methods = $plugin->availableMethods(); $method = $io->choice($this->trans('commands.rest.enable.arguments.methods'), $methods); $io->writeln($this->trans('commands.rest.enable.messages.selected-method') . ' ' . $method); $format = $io->choice($this->trans('commands.rest.enable.arguments.formats'), $this->formats); $io->writeln($this->trans('commands.rest.enable.messages.selected-format') . ' ' . $format); // Get Authentication Provider and generate the question $authenticationProviders = $this->authenticationCollector->getSortedProviders(); $authenticationProvidersSelected = $io->choice($this->trans('commands.rest.enable.messages.authentication-providers'), array_keys($authenticationProviders), 0, true); $io->writeln($this->trans('commands.rest.enable.messages.selected-authentication-providers') . ' ' . implode(', ', $authenticationProvidersSelected)); $format_resource_id = str_replace(':', '.', $resource_id); $config = $this->entityManager->getStorage('rest_resource_config')->load($format_resource_id); if (!$config) { $config = $this->entityManager->getStorage('rest_resource_config')->create(['id' => $format_resource_id, 'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY, 'configuration' => []]); } $configuration = $config->get('configuration') ?: []; $configuration[$method] = ['supported_formats' => [$format], 'supported_auth' => $authenticationProvidersSelected]; $config->set('configuration', $configuration); $config->save(); $message = sprintf($this->trans('commands.rest.enable.messages.success'), $resource_id); $io->info($message); return true; }
/** * Alters existing routes for a specific collection. * * @param \Symfony\Component\Routing\RouteCollection $collection * The route collection for adding routes. * @return array */ protected function alterRoutes(RouteCollection $collection) { $routes = array(); // Silently ignore resources that are in the settings but are not defined on // the plugin manager currently. That avoids exceptions when REST module is // enabled before another module that provides the resource plugin specified // in the settings. // @todo Remove in https://www.drupal.org/node/2308745 $resources = $this->config->get('rest.settings')->get('resources') ?: array(); $enabled_resources = array_intersect_key($resources, $this->manager->getDefinitions()); if (count($resources) != count($enabled_resources)) { trigger_error('rest.settings lists resources relying on the following missing plugins: ' . implode(', ', array_keys(array_diff_key($resources, $enabled_resources)))); } // Iterate over all enabled resource plugins. foreach ($enabled_resources as $id => $enabled_methods) { $plugin = $this->manager->getInstance(array('id' => $id)); foreach ($plugin->routes() as $name => $route) { // @todo: Are multiple methods possible here? $methods = $route->getMethods(); // Only expose routes where the method is enabled in the configuration. if ($methods && ($method = $methods[0]) && $method && isset($enabled_methods[$method])) { $route->setRequirement('_access_rest_csrf', 'TRUE'); // Check that authentication providers are defined. if (empty($enabled_methods[$method]['supported_auth']) || !is_array($enabled_methods[$method]['supported_auth'])) { $this->logger->error('At least one authentication provider must be defined for resource @id', array(':id' => $id)); continue; } // Check that formats are defined. if (empty($enabled_methods[$method]['supported_formats']) || !is_array($enabled_methods[$method]['supported_formats'])) { $this->logger->error('At least one format must be defined for resource @id', array(':id' => $id)); continue; } // If the route has a format requirement, then verify that the // resource has it. $format_requirement = $route->getRequirement('_format'); if ($format_requirement && !in_array($format_requirement, $enabled_methods[$method]['supported_formats'])) { continue; } // The configuration seems legit at this point, so we set the // authentication provider and add the route. $route->setOption('_auth', $enabled_methods[$method]['supported_auth']); $routes["rest.{$name}"] = $route; $collection->add("rest.{$name}", $route); } } } }
/** * Alters existing routes for a specific collection. * * @param \Symfony\Component\Routing\RouteCollection $collection * The route collection for adding routes. * @return array */ protected function alterRoutes(RouteCollection $collection) { $routes = array(); $enabled_resources = $this->config->get('rest.settings')->get('resources') ?: array(); // Iterate over all enabled resource plugins. foreach ($enabled_resources as $id => $enabled_methods) { $plugin = $this->manager->getInstance(array('id' => $id)); foreach ($plugin->routes() as $name => $route) { // @todo: Are multiple methods possible here? $methods = $route->getMethods(); // Only expose routes where the method is enabled in the configuration. if ($methods && ($method = $methods[0]) && $method && isset($enabled_methods[$method])) { $route->setRequirement('_access_rest_csrf', 'TRUE'); // Check that authentication providers are defined. if (empty($enabled_methods[$method]['supported_auth']) || !is_array($enabled_methods[$method]['supported_auth'])) { $this->logger->error('At least one authentication provider must be defined for resource @id', array(':id' => $id)); continue; } // Check that formats are defined. if (empty($enabled_methods[$method]['supported_formats']) || !is_array($enabled_methods[$method]['supported_formats'])) { $this->logger->error('At least one format must be defined for resource @id', array(':id' => $id)); continue; } // If the route has a format requirement, then verify that the // resource has it. $format_requirement = $route->getRequirement('_format'); if ($format_requirement && !in_array($format_requirement, $enabled_methods[$method]['supported_formats'])) { continue; } // The configuration seems legit at this point, so we set the // authentication provider and add the route. $route->setOption('_auth', $enabled_methods[$method]['supported_auth']); $routes["rest.{$name}"] = $route; $collection->add("rest.{$name}", $route); } } } }
/** * Disables a resource. * * @param string $resource_id * The identifier or the REST resource. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return \Drupal\Core\Ajax\AjaxResponse|\Symfony\Component\HttpFoundation\RedirectResponse * Redirects back to the listing page. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * Access is denied, if the token is invalid or missing. */ public function disable($resource_id, Request $request) { if (!\Drupal::csrfToken()->validate($request->query->get('token'), 'restui_disable')) { throw new AccessDeniedHttpException(); } $config = \Drupal::configFactory()->getEditable('rest.settings'); $resources = $config->get('resources') ?: array(); $plugin = $this->resourcePluginManager->getInstance(array('id' => $resource_id)); if (!empty($plugin)) { // disable the resource. unset($resources[$resource_id]); $config->set('resources', $resources); $config->save(); // Rebuild routing cache. $this->routeBuilder->rebuild(); drupal_set_message(t('The resource was disabled successfully.')); } // Redirect back to the page. return new RedirectResponse($this->urlGenerator->generate('restui.list', array(), TRUE)); }
/** * {@inheritdoc} */ public function importContent($module) { $created = array(); $folder = drupal_get_path('module', $module) . "/content"; if (file_exists($folder)) { $file_map = array(); foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { $reflection = new \ReflectionClass($entity_type->getClass()); // We are only interested in importing content entities. if ($reflection->implementsInterface('\\Drupal\\Core\\Config\\Entity\\ConfigEntityInterface')) { continue; } if (!file_exists($folder . '/' . $entity_type_id)) { continue; } $files = $this->scanner()->scan($folder . '/' . $entity_type_id); // Default content uses drupal.org as domain. // @todo Make this use a uri like default-content:. $this->linkManager->setLinkDomain(static::LINK_DOMAIN); // Parse all of the files and sort them in order of dependency. foreach ($files as $file) { $contents = $this->parseFile($file); // Decode the file contents. $decoded = $this->serializer->decode($contents, 'hal_json'); // Get the link to this entity. $self = $decoded['_links']['self']['href']; // Throw an exception when this URL already exists. if (isset($file_map[$self])) { $args = array('@href' => $self, '@first' => $file_map[$self]->uri, '@second' => $file->uri); // Reset link domain. $this->linkManager->setLinkDomain(FALSE); throw new \Exception(SafeMarkup::format('Default content with href @href exists twice: @first @second', $args)); } // Store the entity type with the file. $file->entity_type_id = $entity_type_id; // Store the file in the file map. $file_map[$self] = $file; // Create a vertex for the graph. $vertex = $this->getVertex($self); $this->graph[$vertex->link]['edges'] = []; if (empty($decoded['_embedded'])) { // No dependencies to resolve. continue; } // Here we need to resolve our dependencies; foreach ($decoded['_embedded'] as $embedded) { foreach ($embedded as $item) { $edge = $this->getVertex($item['_links']['self']['href']); $this->graph[$vertex->link]['edges'][$edge->link] = TRUE; } } } } // @todo what if no dependencies? $sorted = $this->sortTree($this->graph); foreach ($sorted as $link => $details) { if (!empty($file_map[$link])) { $file = $file_map[$link]; $entity_type_id = $file->entity_type_id; $resource = $this->resourcePluginManager->getInstance(array('id' => 'entity:' . $entity_type_id)); $definition = $resource->getPluginDefinition(); $contents = $this->parseFile($file); $class = $definition['serialization_class']; $entity = $this->serializer->deserialize($contents, $class, 'hal_json', array('request_method' => 'POST')); $entity->enforceIsNew(TRUE); $entity->save(); $created[$entity->uuid()] = $entity; } } $this->eventDispatcher->dispatch(DefaultContentEvents::IMPORT, new ImportEvent($created, $module)); } // Reset the tree. $this->resetTree(); // Reset link domain. $this->linkManager->setLinkDomain(FALSE); return $created; }