Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Update / UpdateKernel.php
1 <?php
2
3 namespace Drupal\Core\Update;
4
5 use Drupal\Core\DrupalKernel;
6 use Drupal\Core\Session\AnonymousUserSession;
7 use Drupal\Core\Site\Settings;
8 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
9 use Symfony\Component\HttpFoundation\ParameterBag;
10 use Symfony\Component\HttpFoundation\Request;
11 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
12
13 /**
14  * Defines a kernel which is used primarily to run the update of Drupal.
15  *
16  * We use a dedicated kernel + front controller (update.php) in order to be able
17  * to repair Drupal if it is in a broken state.
18  *
19  * @see update.php
20  * @see \Drupal\system\Controller\DbUpdateController
21  */
22 class UpdateKernel extends DrupalKernel {
23
24   /**
25    * {@inheritdoc}
26    */
27   public function discoverServiceProviders() {
28     parent::discoverServiceProviders();
29
30     $this->serviceProviderClasses['app']['update_kernel'] = 'Drupal\Core\Update\UpdateServiceProvider';
31   }
32
33   /**
34    * {@inheritdoc}
35    */
36   protected function initializeContainer() {
37     // Always force a container rebuild, in order to be able to override some
38     // services, see \Drupal\Core\Update\UpdateServiceProvider.
39     $this->containerNeedsRebuild = TRUE;
40     $container = parent::initializeContainer();
41     return $container;
42   }
43
44   /**
45    * {@inheritdoc}
46    */
47   protected function cacheDrupalContainer(array $container_definition) {
48     // Don't save this particular container to cache, so it does not leak into
49     // the main site at all.
50     return FALSE;
51   }
52
53   /**
54    * {@inheritdoc}
55    */
56   public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
57     try {
58       static::bootEnvironment();
59
60       // First boot up basic things, like loading the include files.
61       $this->initializeSettings($request);
62       $this->boot();
63       $container = $this->getContainer();
64       /** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
65       $request_stack = $container->get('request_stack');
66       $request_stack->push($request);
67       $this->preHandle($request);
68
69       // Handle the actual request. We need the session both for authentication
70       // as well as the DB update, like
71       // \Drupal\system\Controller\DbUpdateController::batchFinished.
72       $this->bootSession($request, $type);
73       $result = $this->handleRaw($request);
74       $this->shutdownSession($request);
75
76       return $result;
77     }
78     catch (\Exception $e) {
79       return $this->handleException($e, $request, $type);
80     }
81   }
82
83   /**
84    * Generates the actual result of update.php.
85    *
86    * The actual logic of the update is done in the db update controller.
87    *
88    * @param \Symfony\Component\HttpFoundation\Request $request
89    *   The incoming request.
90    *
91    * @return \Symfony\Component\HttpFoundation\Response
92    *   A response object.
93    *
94    * @see \Drupal\system\Controller\DbUpdateController
95    */
96   protected function handleRaw(Request $request) {
97     $container = $this->getContainer();
98
99     $this->handleAccess($request, $container);
100
101     /** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
102     $controller_resolver = $container->get('controller_resolver');
103
104     /** @var callable $db_update_controller */
105     $db_update_controller = $controller_resolver->getControllerFromDefinition('\Drupal\system\Controller\DbUpdateController::handle');
106
107     $this->setupRequestMatch($request);
108
109     /** @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver */
110     $argument_resolver = $container->get('http_kernel.controller.argument_resolver');
111     $arguments = $argument_resolver->getArguments($request, $db_update_controller);
112     return call_user_func_array($db_update_controller, $arguments);
113   }
114
115   /**
116    * Boots up the session.
117    *
118    * This method + shutdownSession() basically simulates what
119    * \Drupal\Core\StackMiddleware\Session does.
120    *
121    * @param \Symfony\Component\HttpFoundation\Request $request
122    *   The incoming request.
123    */
124   protected function bootSession(Request $request) {
125     $container = $this->getContainer();
126     /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
127     $session = $container->get('session');
128     $session->start();
129     $request->setSession($session);
130   }
131
132   /**
133    * Ensures that the session is saved.
134    *
135    * @param \Symfony\Component\HttpFoundation\Request $request
136    *   The incoming request.
137    */
138   protected function shutdownSession(Request $request) {
139     if ($request->hasSession()) {
140       $request->getSession()->save();
141     }
142   }
143
144   /**
145    * Set up the request with fake routing data for update.php.
146    *
147    * This fake routing data is needed in order to make batch API work properly.
148    *
149    * @param \Symfony\Component\HttpFoundation\Request $request
150    *   The incoming request.
151    */
152   protected function setupRequestMatch(Request $request) {
153     $path = $request->getPathInfo();
154     $args = explode('/', ltrim($path, '/'));
155
156     $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'system.db_update');
157     $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $this->getContainer()->get('router.route_provider')->getRouteByName('system.db_update'));
158     $op = $args[0] ?: 'info';
159     $request->attributes->set('op', $op);
160     $request->attributes->set('_raw_variables', new ParameterBag(['op' => $op]));
161   }
162
163   /**
164    * Checks if the current user has rights to access updates page.
165    *
166    * If the current user does not have the rights, an exception is thrown.
167    *
168    * @param \Symfony\Component\HttpFoundation\Request $request
169    *   The incoming request.
170    *
171    * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
172    *   Thrown when update.php should not be accessible.
173    */
174   protected function handleAccess(Request $request) {
175     /** @var \Drupal\Core\Authentication\AuthenticationManager $authentication_manager */
176     $authentication_manager = $this->getContainer()->get('authentication');
177     $account = $authentication_manager->authenticate($request) ?: new AnonymousUserSession();
178
179     /** @var \Drupal\Core\Session\AccountProxyInterface $current_user */
180     $current_user = $this->getContainer()->get('current_user');
181     $current_user->setAccount($account);
182
183     /** @var \Drupal\system\Access\DbUpdateAccessCheck $db_update_access */
184     $db_update_access = $this->getContainer()->get('access_check.db_update');
185
186     if (!Settings::get('update_free_access', FALSE) && !$db_update_access->access($account)->isAllowed()) {
187       throw new AccessDeniedHttpException('In order to run update.php you need to either have "Administer software updates" permission or have set $settings[\'update_free_access\'] in your settings.php.');
188     }
189   }
190
191 }