Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / rest / src / Entity / ConfigDependencies.php
1 <?php
2
3 namespace Drupal\rest\Entity;
4
5 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
6 use Drupal\rest\RestResourceConfigInterface;
7 use Symfony\Component\DependencyInjection\ContainerInterface;
8
9 /**
10  * Calculates rest resource config dependencies.
11  *
12  * @internal
13  */
14 class ConfigDependencies implements ContainerInjectionInterface {
15
16   /**
17    * The serialization format providers, keyed by format.
18    *
19    * @var string[]
20    */
21   protected $formatProviders;
22
23   /**
24    * The authentication providers, keyed by ID.
25    *
26    * @var string[]
27    */
28   protected $authProviders;
29
30   /**
31    * Creates a new ConfigDependencies instance.
32    *
33    * @param string[] $format_providers
34    *   The serialization format providers, keyed by format.
35    * @param string[] $auth_providers
36    *   The authentication providers, keyed by ID.
37    */
38   public function __construct(array $format_providers, array $auth_providers) {
39     $this->formatProviders = $format_providers;
40     $this->authProviders = $auth_providers;
41   }
42
43   /**
44    * {@inheritdoc}
45    */
46   public static function create(ContainerInterface $container) {
47     return new static(
48       $container->getParameter('serializer.format_providers'),
49       $container->getParameter('authentication_providers')
50     );
51   }
52
53   /**
54    * Calculates dependencies of a specific rest resource configuration.
55    *
56    * This function returns dependencies in a non-sorted, non-unique manner. It
57    * is therefore the caller's responsibility to sort and remove duplicates
58    * from the result prior to saving it with the configuration or otherwise
59    * using it in a way that requires that. For example,
60    * \Drupal\rest\Entity\RestResourceConfig::calculateDependencies() does this
61    * via its \Drupal\Core\Entity\DependencyTrait::addDependency() method.
62    *
63    * @param \Drupal\rest\RestResourceConfigInterface $rest_config
64    *   The rest configuration.
65    *
66    * @return string[][]
67    *   Dependencies keyed by dependency type.
68    *
69    * @see \Drupal\rest\Entity\RestResourceConfig::calculateDependencies()
70    */
71   public function calculateDependencies(RestResourceConfigInterface $rest_config) {
72     $granularity = $rest_config->get('granularity');
73
74     // Dependency calculation is the same for either granularity, the most
75     // notable difference is that for the 'resource' granularity, the same
76     // authentication providers and formats are supported for every method.
77     switch ($granularity) {
78       case RestResourceConfigInterface::METHOD_GRANULARITY:
79         $methods = $rest_config->getMethods();
80         break;
81       case RestResourceConfigInterface::RESOURCE_GRANULARITY:
82         $methods = array_slice($rest_config->getMethods(), 0, 1);
83         break;
84       default:
85         throw new \InvalidArgumentException('Invalid granularity specified.');
86     }
87
88     // The dependency lists for authentication providers and formats
89     // generated on container build.
90     $dependencies = [];
91     foreach ($methods as $request_method) {
92       // Add dependencies based on the supported authentication providers.
93       foreach ($rest_config->getAuthenticationProviders($request_method) as $auth) {
94         if (isset($this->authProviders[$auth])) {
95           $module_name = $this->authProviders[$auth];
96           $dependencies['module'][] = $module_name;
97         }
98       }
99       // Add dependencies based on the supported authentication formats.
100       foreach ($rest_config->getFormats($request_method) as $format) {
101         if (isset($this->formatProviders[$format])) {
102           $module_name = $this->formatProviders[$format];
103           $dependencies['module'][] = $module_name;
104         }
105       }
106     }
107
108     return $dependencies;
109   }
110
111   /**
112    * Informs the entity that entities it depends on will be deleted.
113    *
114    * @param \Drupal\rest\RestResourceConfigInterface $rest_config
115    *   The rest configuration.
116    * @param array $dependencies
117    *   An array of dependencies that will be deleted keyed by dependency type.
118    *   Dependency types are, for example, entity, module and theme.
119    *
120    * @return bool
121    *   TRUE if the entity has been changed as a result, FALSE if not.
122    *
123    * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval()
124    */
125   public function onDependencyRemoval(RestResourceConfigInterface $rest_config, array $dependencies) {
126     $granularity = $rest_config->get('granularity');
127     switch ($granularity) {
128       case RestResourceConfigInterface::METHOD_GRANULARITY:
129         return $this->onDependencyRemovalForMethodGranularity($rest_config, $dependencies);
130       case RestResourceConfigInterface::RESOURCE_GRANULARITY:
131         return $this->onDependencyRemovalForResourceGranularity($rest_config, $dependencies);
132       default:
133         throw new \InvalidArgumentException('Invalid granularity specified.');
134     }
135   }
136
137   /**
138    * Informs the entity that entities it depends on will be deleted.
139    *
140    * @param \Drupal\rest\RestResourceConfigInterface $rest_config
141    *   The rest configuration.
142    * @param array $dependencies
143    *   An array of dependencies that will be deleted keyed by dependency type.
144    *   Dependency types are, for example, entity, module and theme.
145    *
146    * @return bool
147    *   TRUE if the entity has been changed as a result, FALSE if not.
148    */
149   protected function onDependencyRemovalForMethodGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
150     $changed = FALSE;
151     // Only module-related dependencies can be fixed. All other types of
152     // dependencies cannot, because they were not generated based on supported
153     // authentication providers or formats.
154     if (isset($dependencies['module'])) {
155       // Try to fix dependencies.
156       $removed_auth = array_keys(array_intersect($this->authProviders, $dependencies['module']));
157       $removed_formats = array_keys(array_intersect($this->formatProviders, $dependencies['module']));
158       $configuration_before = $configuration = $rest_config->get('configuration');
159       if (!empty($removed_auth) || !empty($removed_formats)) {
160         // Try to fix dependency problems by removing affected
161         // authentication providers and formats.
162         foreach (array_keys($rest_config->get('configuration')) as $request_method) {
163           foreach ($removed_formats as $format) {
164             if (in_array($format, $rest_config->getFormats($request_method), TRUE)) {
165               $configuration[$request_method]['supported_formats'] = array_diff($configuration[$request_method]['supported_formats'], $removed_formats);
166             }
167           }
168           foreach ($removed_auth as $auth) {
169             if (in_array($auth, $rest_config->getAuthenticationProviders($request_method), TRUE)) {
170               $configuration[$request_method]['supported_auth'] = array_diff($configuration[$request_method]['supported_auth'], $removed_auth);
171             }
172           }
173           if (empty($configuration[$request_method]['supported_auth'])) {
174             // Remove the key if there are no more authentication providers
175             // supported by this request method.
176             unset($configuration[$request_method]['supported_auth']);
177           }
178           if (empty($configuration[$request_method]['supported_formats'])) {
179             // Remove the key if there are no more formats supported by this
180             // request method.
181             unset($configuration[$request_method]['supported_formats']);
182           }
183           if (empty($configuration[$request_method])) {
184             // Remove the request method altogether if it no longer has any
185             // supported authentication providers or formats.
186             unset($configuration[$request_method]);
187           }
188         }
189       }
190       if ($configuration_before != $configuration && !empty($configuration)) {
191         $rest_config->set('configuration', $configuration);
192         // Only mark the dependencies problems as fixed if there is any
193         // configuration left.
194         $changed = TRUE;
195       }
196     }
197     // If the dependency problems are not marked as fixed at this point they
198     // should be related to the resource plugin and the config entity should
199     // be deleted.
200     return $changed;
201   }
202
203   /**
204    * Informs the entity that entities it depends on will be deleted.
205    *
206    * @param \Drupal\rest\RestResourceConfigInterface $rest_config
207    *   The rest configuration.
208    * @param array $dependencies
209    *   An array of dependencies that will be deleted keyed by dependency type.
210    *   Dependency types are, for example, entity, module and theme.
211    *
212    * @return bool
213    *   TRUE if the entity has been changed as a result, FALSE if not.
214    */
215   protected function onDependencyRemovalForResourceGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
216     $changed = FALSE;
217     // Only module-related dependencies can be fixed. All other types of
218     // dependencies cannot, because they were not generated based on supported
219     // authentication providers or formats.
220     if (isset($dependencies['module'])) {
221       // Try to fix dependencies.
222       $removed_auth = array_keys(array_intersect($this->authProviders, $dependencies['module']));
223       $removed_formats = array_keys(array_intersect($this->formatProviders, $dependencies['module']));
224       $configuration_before = $configuration = $rest_config->get('configuration');
225       if (!empty($removed_auth) || !empty($removed_formats)) {
226         // All methods support the same formats and authentication providers, so
227         // get those for whichever the first listed method is.
228         $first_method = $rest_config->getMethods()[0];
229
230         // Try to fix dependency problems by removing affected
231         // authentication providers and formats.
232         foreach ($removed_formats as $format) {
233           if (in_array($format, $rest_config->getFormats($first_method), TRUE)) {
234             $configuration['formats'] = array_diff($configuration['formats'], $removed_formats);
235           }
236         }
237         foreach ($removed_auth as $auth) {
238           if (in_array($auth, $rest_config->getAuthenticationProviders($first_method), TRUE)) {
239             $configuration['authentication'] = array_diff($configuration['authentication'], $removed_auth);
240           }
241         }
242         if (empty($configuration['authentication'])) {
243           // Remove the key if there are no more authentication providers
244           // supported.
245           unset($configuration['authentication']);
246         }
247         if (empty($configuration['formats'])) {
248           // Remove the key if there are no more formats supported.
249           unset($configuration['formats']);
250         }
251         if (empty($configuration['authentication']) || empty($configuration['formats'])) {
252           // If there no longer are any supported authentication providers or
253           // formats, this REST resource can no longer function, and so we
254           // cannot fix this config entity to keep it working.
255           $configuration = [];
256         }
257       }
258       if ($configuration_before != $configuration && !empty($configuration)) {
259         $rest_config->set('configuration', $configuration);
260         // Only mark the dependencies problems as fixed if there is any
261         // configuration left.
262         $changed = TRUE;
263       }
264     }
265     // If the dependency problems are not marked as fixed at this point they
266     // should be related to the resource plugin and the config entity should
267     // be deleted.
268     return $changed;
269   }
270
271 }