Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/jsonapi/src/EventSubscriber/ResourceResponseValidator.php @ 5:12f9dff5fda9 tip
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:34:47 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
4:a9cd425dd02b | 5:12f9dff5fda9 |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\jsonapi\EventSubscriber; | |
4 | |
5 use Drupal\Component\Serialization\Json; | |
6 use Drupal\Core\Extension\ModuleHandlerInterface; | |
7 use JsonSchema\Validator; | |
8 use Psr\Log\LoggerInterface; | |
9 use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
10 use Symfony\Component\HttpFoundation\Request; | |
11 use Symfony\Component\HttpFoundation\Response; | |
12 use Symfony\Component\HttpKernel\Event\FilterResponseEvent; | |
13 use Symfony\Component\HttpKernel\KernelEvents; | |
14 use Symfony\Component\Serializer\SerializerInterface; | |
15 | |
16 /** | |
17 * Response subscriber that validates a JSON:API response. | |
18 * | |
19 * This must run after ResourceResponseSubscriber. | |
20 * | |
21 * @internal JSON:API maintains no PHP API. The API is the HTTP API. This class | |
22 * may change at any time and could break any dependencies on it. | |
23 * | |
24 * @see https://www.drupal.org/project/jsonapi/issues/3032787 | |
25 * @see jsonapi.api.php | |
26 * | |
27 * @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber | |
28 */ | |
29 class ResourceResponseValidator implements EventSubscriberInterface { | |
30 | |
31 /** | |
32 * The serializer. | |
33 * | |
34 * @var \Symfony\Component\Serializer\SerializerInterface | |
35 */ | |
36 protected $serializer; | |
37 | |
38 /** | |
39 * The JSON:API logger channel. | |
40 * | |
41 * @var \Psr\Log\LoggerInterface | |
42 */ | |
43 protected $logger; | |
44 | |
45 /** | |
46 * The schema validator. | |
47 * | |
48 * This property will only be set if the validator library is available. | |
49 * | |
50 * @var \JsonSchema\Validator|null | |
51 */ | |
52 protected $validator; | |
53 | |
54 /** | |
55 * The module handler. | |
56 * | |
57 * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
58 */ | |
59 protected $moduleHandler; | |
60 | |
61 /** | |
62 * The application's root file path. | |
63 * | |
64 * @var string | |
65 */ | |
66 protected $appRoot; | |
67 | |
68 /** | |
69 * Constructs a ResourceResponseValidator object. | |
70 * | |
71 * @param \Symfony\Component\Serializer\SerializerInterface $serializer | |
72 * The serializer. | |
73 * @param \Psr\Log\LoggerInterface $logger | |
74 * The JSON:API logger channel. | |
75 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
76 * The module handler. | |
77 * @param string $app_root | |
78 * The application's root file path. | |
79 */ | |
80 public function __construct(SerializerInterface $serializer, LoggerInterface $logger, ModuleHandlerInterface $module_handler, $app_root) { | |
81 $this->serializer = $serializer; | |
82 $this->logger = $logger; | |
83 $this->moduleHandler = $module_handler; | |
84 $this->appRoot = $app_root; | |
85 } | |
86 | |
87 /** | |
88 * {@inheritdoc} | |
89 */ | |
90 public static function getSubscribedEvents() { | |
91 $events[KernelEvents::RESPONSE][] = ['onResponse']; | |
92 return $events; | |
93 } | |
94 | |
95 /** | |
96 * Sets the validator service if available. | |
97 */ | |
98 public function setValidator(Validator $validator = NULL) { | |
99 if ($validator) { | |
100 $this->validator = $validator; | |
101 } | |
102 elseif (class_exists(Validator::class)) { | |
103 $this->validator = new Validator(); | |
104 } | |
105 } | |
106 | |
107 /** | |
108 * Validates JSON:API responses. | |
109 * | |
110 * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event | |
111 * The event to process. | |
112 */ | |
113 public function onResponse(FilterResponseEvent $event) { | |
114 $response = $event->getResponse(); | |
115 if (strpos($response->headers->get('Content-Type'), 'application/vnd.api+json') === FALSE) { | |
116 return; | |
117 } | |
118 | |
119 $this->doValidateResponse($response, $event->getRequest()); | |
120 } | |
121 | |
122 /** | |
123 * Wraps validation in an assert to prevent execution in production. | |
124 * | |
125 * @see self::validateResponse | |
126 */ | |
127 public function doValidateResponse(Response $response, Request $request) { | |
128 if (PHP_MAJOR_VERSION >= 7 || assert_options(ASSERT_ACTIVE)) { | |
129 assert($this->validateResponse($response, $request), 'A JSON:API response failed validation (see the logs for details). Please report this in the issue queue on drupal.org'); | |
130 } | |
131 } | |
132 | |
133 /** | |
134 * Validates a response against the JSON:API specification. | |
135 * | |
136 * @param \Symfony\Component\HttpFoundation\Response $response | |
137 * The response to validate. | |
138 * @param \Symfony\Component\HttpFoundation\Request $request | |
139 * The request containing info about what to validate. | |
140 * | |
141 * @return bool | |
142 * FALSE if the response failed validation, otherwise TRUE. | |
143 */ | |
144 protected function validateResponse(Response $response, Request $request) { | |
145 // If the validator isn't set, then the validation library is not installed. | |
146 if (!$this->validator) { | |
147 return TRUE; | |
148 } | |
149 | |
150 // Do not use Json::decode here since it coerces the response into an | |
151 // associative array, which creates validation errors. | |
152 $response_data = json_decode($response->getContent()); | |
153 if (empty($response_data)) { | |
154 return TRUE; | |
155 } | |
156 | |
157 $schema_ref = sprintf( | |
158 'file://%s/schema.json', | |
159 implode('/', [ | |
160 $this->appRoot, | |
161 $this->moduleHandler->getModule('jsonapi')->getPath(), | |
162 ]) | |
163 ); | |
164 $generic_jsonapi_schema = (object) ['$ref' => $schema_ref]; | |
165 | |
166 return $this->validateSchema($generic_jsonapi_schema, $response_data); | |
167 } | |
168 | |
169 /** | |
170 * Validates a string against a JSON Schema. It logs any possible errors. | |
171 * | |
172 * @param object $schema | |
173 * The JSON Schema object. | |
174 * @param string $response_data | |
175 * The JSON string to validate. | |
176 * | |
177 * @return bool | |
178 * TRUE if the string is a valid instance of the schema. FALSE otherwise. | |
179 */ | |
180 protected function validateSchema($schema, $response_data) { | |
181 $this->validator->check($response_data, $schema); | |
182 $is_valid = $this->validator->isValid(); | |
183 if (!$is_valid) { | |
184 $this->logger->debug("Response failed validation.\nResponse:\n@data\n\nErrors:\n@errors", [ | |
185 '@data' => Json::encode($response_data), | |
186 '@errors' => Json::encode($this->validator->getErrors()), | |
187 ]); | |
188 } | |
189 return $is_valid; | |
190 } | |
191 | |
192 } |