Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / rest / src / Plugin / ResourceBase.php
1 <?php
2
3 namespace Drupal\rest\Plugin;
4
5 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6 use Drupal\Core\Plugin\PluginBase;
7 use Psr\Log\LoggerInterface;
8 use Symfony\Component\DependencyInjection\ContainerInterface;
9 use Symfony\Component\Routing\Route;
10 use Symfony\Component\Routing\RouteCollection;
11
12 /**
13  * Common base class for resource plugins.
14  *
15  * Note that this base class' implementation of the permissions() method
16  * generates a permission for every method for a resource. If your resource
17  * already has its own access control mechanism, you should opt out from this
18  * default permissions() method by overriding it.
19  *
20  * @see \Drupal\rest\Annotation\RestResource
21  * @see \Drupal\rest\Plugin\Type\ResourcePluginManager
22  * @see \Drupal\rest\Plugin\ResourceInterface
23  * @see plugin_api
24  *
25  * @ingroup third_party
26  */
27 abstract class ResourceBase extends PluginBase implements ContainerFactoryPluginInterface, ResourceInterface {
28
29   /**
30    * The available serialization formats.
31    *
32    * @var array
33    */
34   protected $serializerFormats = [];
35
36   /**
37    * A logger instance.
38    *
39    * @var \Psr\Log\LoggerInterface
40    */
41   protected $logger;
42
43   /**
44    * Constructs a Drupal\rest\Plugin\ResourceBase object.
45    *
46    * @param array $configuration
47    *   A configuration array containing information about the plugin instance.
48    * @param string $plugin_id
49    *   The plugin_id for the plugin instance.
50    * @param mixed $plugin_definition
51    *   The plugin implementation definition.
52    * @param array $serializer_formats
53    *   The available serialization formats.
54    * @param \Psr\Log\LoggerInterface $logger
55    *   A logger instance.
56    */
57   public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger) {
58     parent::__construct($configuration, $plugin_id, $plugin_definition);
59     $this->serializerFormats = $serializer_formats;
60     $this->logger = $logger;
61   }
62
63   /**
64    * {@inheritdoc}
65    */
66   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
67     return new static(
68       $configuration,
69       $plugin_id,
70       $plugin_definition,
71       $container->getParameter('serializer.formats'),
72       $container->get('logger.factory')->get('rest')
73     );
74   }
75
76   /**
77    * Implements ResourceInterface::permissions().
78    *
79    * Every plugin operation method gets its own user permission. Example:
80    * "restful delete entity:node" with the title "Access DELETE on Node
81    * resource".
82    */
83   public function permissions() {
84     $permissions = [];
85     $definition = $this->getPluginDefinition();
86     foreach ($this->availableMethods() as $method) {
87       $lowered_method = strtolower($method);
88       $permissions["restful $lowered_method $this->pluginId"] = [
89         'title' => $this->t('Access @method on %label resource', ['@method' => $method, '%label' => $definition['label']]),
90       ];
91     }
92     return $permissions;
93   }
94
95   /**
96    * {@inheritdoc}
97    */
98   public function routes() {
99     $collection = new RouteCollection();
100
101     $definition = $this->getPluginDefinition();
102     $canonical_path = isset($definition['uri_paths']['canonical']) ? $definition['uri_paths']['canonical'] : '/' . strtr($this->pluginId, ':', '/') . '/{id}';
103     $create_path = isset($definition['uri_paths']['https://www.drupal.org/link-relations/create']) ? $definition['uri_paths']['https://www.drupal.org/link-relations/create'] : '/' . strtr($this->pluginId, ':', '/');
104
105     $route_name = strtr($this->pluginId, ':', '.');
106
107     $methods = $this->availableMethods();
108     foreach ($methods as $method) {
109       $route = $this->getBaseRoute($canonical_path, $method);
110
111       switch ($method) {
112         case 'POST':
113           $route->setPath($create_path);
114           $collection->add("$route_name.$method", $route);
115           break;
116
117         case 'GET':
118         case 'HEAD':
119           // Restrict GET and HEAD requests to the media type specified in the
120           // HTTP Accept headers.
121           foreach ($this->serializerFormats as $format_name) {
122             // Expose one route per available format.
123             $format_route = clone $route;
124             $format_route->addRequirements(['_format' => $format_name]);
125             $collection->add("$route_name.$method.$format_name", $format_route);
126           }
127           break;
128
129         default:
130           $collection->add("$route_name.$method", $route);
131           break;
132       }
133     }
134
135     return $collection;
136   }
137
138   /**
139    * Provides predefined HTTP request methods.
140    *
141    * Plugins can override this method to provide additional custom request
142    * methods.
143    *
144    * @return array
145    *   The list of allowed HTTP request method strings.
146    */
147   protected function requestMethods() {
148     return [
149       'HEAD',
150       'GET',
151       'POST',
152       'PUT',
153       'DELETE',
154       'TRACE',
155       'OPTIONS',
156       'CONNECT',
157       'PATCH',
158     ];
159   }
160
161   /**
162    * {@inheritdoc}
163    */
164   public function availableMethods() {
165     $methods = $this->requestMethods();
166     $available = [];
167     foreach ($methods as $method) {
168       // Only expose methods where the HTTP request method exists on the plugin.
169       if (method_exists($this, strtolower($method))) {
170         $available[] = $method;
171       }
172     }
173     return $available;
174   }
175
176   /**
177    * Gets the base route for a particular method.
178    *
179    * @param string $canonical_path
180    *   The canonical path for the resource.
181    * @param string $method
182    *   The HTTP method to be used for the route.
183    *
184    * @return \Symfony\Component\Routing\Route
185    *   The created base route.
186    */
187   protected function getBaseRoute($canonical_path, $method) {
188     return new Route($canonical_path, [
189       '_controller' => 'Drupal\rest\RequestHandler::handle',
190     ],
191       $this->getBaseRouteRequirements($method),
192       [],
193       '',
194       [],
195       // The HTTP method is a requirement for this route.
196       [$method]
197     );
198   }
199
200   /**
201    * Gets the base route requirements for a particular method.
202    *
203    * @param $method
204    *   The HTTP method to be used for the route.
205    *
206    * @return array
207    *   An array of requirements for parameters.
208    */
209   protected function getBaseRouteRequirements($method) {
210     $lower_method = strtolower($method);
211     // Every route MUST have requirements that result in the access manager
212     // having access checks to check. If it does not, the route is made
213     // inaccessible. So, we default to granting access to everyone. If a
214     // permission exists, then we add that below. The access manager requires
215     // that ALL access checks must grant access, so this still results in
216     // correct behavior.
217     $requirements = [
218       '_access' => 'TRUE',
219     ];
220
221     // Only specify route requirements if the default permission exists. For any
222     // more advanced route definition, resource plugins extending this base
223     // class must override this method.
224     $permission = "restful $lower_method $this->pluginId";
225     if (isset($this->permissions()[$permission])) {
226       $requirements['_permission'] = $permission;
227     }
228
229     return $requirements;
230   }
231
232 }