diff --git a/core/authorize.php b/core/authorize.php index f347ba5..fe39394 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -48,10 +48,12 @@ function authorize_access_denied_page() { * The killswitch in settings.php overrides all else, otherwise, the user must * have access to the 'administer software updates' permission. * - * @return + * @return bool * TRUE if the current user can run authorize.php, and FALSE if not. */ function authorize_access_allowed() { + require_once DRUPAL_ROOT . '/' . settings()->get('session_inc', 'core/includes/session.inc'); + drupal_session_initialize(); return settings()->get('allow_authorize_operations', TRUE) && user_access('administer software updates'); } @@ -65,7 +67,7 @@ function authorize_access_allowed() { // We prepare only a minimal bootstrap. This includes the database and // variables, however, so we have access to the class autoloader. -drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); +drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); // This must go after drupal_bootstrap(), which unsets globals! global $conf; diff --git a/core/core.services.yml b/core/core.services.yml index 5a60f4f..ace0a62 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -519,3 +519,23 @@ services: class: Zend\Feed\Writer\Extension\Threading\Renderer\Entry feed.writer.wellformedwebrendererentry: class: Zend\Feed\Writer\Extension\WellFormedWeb\Renderer\Entry + authentication: + class: Drupal\Core\Authentication\AuthenticationManager + authentication.cookie: + class: Drupal\Core\Authentication\Provider\Cookie + tags: + - { name: authentication_provider, priority: 0 } + authentication.http_basic: + class: Drupal\Core\Authentication\Provider\HttpBasic + arguments: [@config.factory] + tags: + - { name: authentication_provider, priority: 100 } + authentication_subscriber: + class: Drupal\Core\EventSubscriber\AuthenticationSubscriber + tags: + - { name: event_subscriber } + arguments: ['@authentication'] + access_check.authentication_provider: + class: Drupal\Core\Access\AuthenticationProviderAccessCheck + tags: + - { name: access_check } diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index cf0e3dd..226bfbf 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -158,11 +158,6 @@ const DRUPAL_BOOTSTRAP_VARIABLES = 4; /** - * Sixth bootstrap phase: initialize session handling. - */ -const DRUPAL_BOOTSTRAP_SESSION = 5; - -/** * Eighth bootstrap phase: load code for subsystems and modules. */ const DRUPAL_BOOTSTRAP_CODE = 6; @@ -1770,7 +1765,6 @@ function drupal_anonymous_user() { * - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page. * - DRUPAL_BOOTSTRAP_DATABASE: Initializes the database layer. * - DRUPAL_BOOTSTRAP_VARIABLES: Initializes the variable system. - * - DRUPAL_BOOTSTRAP_SESSION: Initializes session handling. * - DRUPAL_BOOTSTRAP_CODE: Loads code for subsystems and modules. * - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input * data. @@ -1789,7 +1783,6 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) { DRUPAL_BOOTSTRAP_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_VARIABLES, - DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_CODE, DRUPAL_BOOTSTRAP_FULL, ); @@ -1839,11 +1832,6 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) { _drupal_bootstrap_variables(); break; - case DRUPAL_BOOTSTRAP_SESSION: - require_once DRUPAL_ROOT . '/' . settings()->get('session_inc', 'core/includes/session.inc'); - drupal_session_initialize(); - break; - case DRUPAL_BOOTSTRAP_CODE: require_once __DIR__ . '/common.inc'; _drupal_bootstrap_code(); diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 1ecd819..5f2ccc7 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1746,6 +1746,8 @@ function install_load_profile(&$install_state) { */ function install_bootstrap_full() { drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + require_once DRUPAL_ROOT . '/' . settings()->get('session_inc', 'core/includes/session.inc'); + drupal_session_initialize(); } /** diff --git a/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php b/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php new file mode 100644 index 0000000..799dc00 --- /dev/null +++ b/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php @@ -0,0 +1,52 @@ +attributes->get('_authentication_provider'); + if (!empty($auth_provider_triggered)) { + $allowed_auth_providers = $route->getOption('_auth'); + if (empty($allowed_auth_providers)) { + return NULL; + } + $check = in_array($auth_provider_triggered, $allowed_auth_providers); + + // Set user as anonymous as he should not be logged in because he logged in + // with not allowed authentication provider. + if ($check == FALSE) { + global $user; + $user = drupal_anonymous_user(); + } + + return $check; + } + } +} diff --git a/core/lib/Drupal/Core/Authentication/AuthenticationManager.php b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php new file mode 100644 index 0000000..99529b3 --- /dev/null +++ b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php @@ -0,0 +1,212 @@ +providers[$provider_id] = $provider; + $this->providerPriorities[$provider_id] = $priority; + } + + /** + * Compares providers by their priority. + * + * This is a uksort() callback allowing to sort the providers by their keys + * based on their priorities. + * + * @param string $a + * A provider key. + * @param string $b + * Another provider key. + * + * @return int + * The integer indicating the sort order. + */ + public function compareProviderPriority($a, $b) { + $priority_a = isset($this->providerPriorities[$a]) ? $this->providerPriorities[$a] : 0; + $priority_b = isset($this->providerPriorities[$b]) ? $this->providerPriorities[$b] : 0; + if ($priority_a == $priority_b) { + return 0; + } + return ($priority_a > $priority_b) ? -1 : 1; + } + + /** + * Compares providers by their reversed priority. + * + * This is a uksort() callback allowing to sort the providers by their keys + * based on their reversed priorities. + * + * @param string $a + * A provider key. + * @param string $b + * Another provider key. + * + * @return int + * + */ + public function compareProviderReversedPriority($a, $b) { + return $this->compareProviderPriority($a, $b) * -1; + } + + /** + * {@inheritdoc} + */ + public function applies(Request $request) { + return TRUE; + } + + /** + * Authenticate user. + * + * Iterate the available providers according th their priority. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * + * @return \Drupal\Core\Session\AccountInterface + * The account interface of the authenticated user. Defaults to a anonymous. + */ + public function authenticate(Request $request) { + global $user; + + // Ensure providers get triggered according to their priority. + uksort($this->providers, array($this, 'compareProviderPriority')); + + // Iterate the availlable providers. + foreach ($this->providers as $provider_id => $provider) { + if ($provider->applies($request)) { + // Provider felt responsible for this request – trigger authentication. + $this->account = $provider->authenticate($request); + $this->triggeredProvider = $provider_id; + break; + } + } + + // No provider returned a valid account - assume anonymous. + if (!$this->account) { + $this->account = drupal_anonymous_user(); + } + + // No provider felt responsible – assume the one with the least priority + // should have. + if (!$this->triggeredProvider) { + end($this->providers); + $this->triggeredProvider = key($this->providers); + } + + // Set the global user to the account returned by the triggered provider. + $user = $this->account; + + // Save the ID of the triggered provider to the request so that it can be + // accessed in \Drupal\Core\Access\AuthenticationProviderAccessCheck. + $request->attributes->set('_authentication_provider', $this->triggeredProvider); + + return $this->account; + } + + /** + * Cleans up the authentication. + * + * Allow the triggered provider to clean up before the response is sent, e.g. + * trigger a session commit. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * + * @see \Drupal\Core\Authentication\Provider\Cookie::cleanup() + */ + public function cleanup(Request $request) { + if (empty($this->providers[$this->triggeredProvider])) { + return; + } + $this->providers[$this->triggeredProvider]->cleanup($request); + } + + /** + * {@inheritdoc} + */ + public function handleException(GetResponseForExceptionEvent $event) { + $request = $event->getRequest(); + $route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT); + $allowed_auth_providers = $route ? $route->getOption('_auth') : array_keys($this->providers); + + // Ensure providers get triggered according to their reversed priority. + uksort($this->providers, array($this, 'compareProviderReversedPriority')); + + foreach ($this->providers as $provider_id => $provider) { + if (!in_array($provider_id, $allowed_auth_providers)) { + continue; + } + + if ($provider->handleException($event) === TRUE) { + break; + } + } + } +} diff --git a/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php b/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php new file mode 100644 index 0000000..31aafe9 --- /dev/null +++ b/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php @@ -0,0 +1,67 @@ +get('session_inc', 'core/includes/session.inc'); + drupal_session_initialize(); + if (drupal_session_started()) { + return $user; + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function cleanup(Request $request) { + drupal_session_commit(); + } + + /** + * {@inheritdoc} + */ + public function handleException(GetResponseForExceptionEvent $event) { + $exception = $event->getException(); + if (user_is_anonymous() && get_class($exception) == 'Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException') { + $event->setResponse(new RedirectResponse(url('user/login', array( + 'absolute' => TRUE, + 'query' => array( + 'destination' => $event->getRequest()->get('system_path'), + ), + )))); + return TRUE; + } + return FALSE; + } +} diff --git a/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php b/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php new file mode 100644 index 0000000..9307731 --- /dev/null +++ b/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php @@ -0,0 +1,85 @@ +configFactory = $config_factory; + } + + /** + * {@inheritdoc} + */ + public function applies(Request $request) { + $username = $request->headers->get('PHP_AUTH_USER'); + $password = $request->headers->get('PHP_AUTH_PW'); + return isset($username) && isset($password); + } + + /** + * {@inheritdoc} + */ + public function authenticate(Request $request) { + $username = $request->headers->get('PHP_AUTH_USER'); + $password = $request->headers->get('PHP_AUTH_PW'); + $uid = user_authenticate($username, $password); + if ($uid) { + return user_load($uid); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function cleanup(Request $request) {} + + /** + * {@inheritdoc} + */ + public function handleException(GetResponseForExceptionEvent $event) { + $exception = $event->getException(); + if (user_is_anonymous() && get_class($exception) == 'Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException') { + if (!$this->applies($event->getRequest())) { + $site_name = $this->configFactory->get('system.site')->get('name'); + global $base_url; + $challenge = String::format('Basic realm="@realm"', array( + '@realm' => !empty($site_name) ? $site_name : $base_url, + )); + $event->setException(new UnauthorizedHttpException($challenge, 'No authentication credentials provided.', $exception)); + } + return TRUE;; + } + return FALSE; + } +} diff --git a/core/lib/Drupal/Core/Controller/ExceptionController.php b/core/lib/Drupal/Core/Controller/ExceptionController.php index c9a661b..9e7d77e 100644 --- a/core/lib/Drupal/Core/Controller/ExceptionController.php +++ b/core/lib/Drupal/Core/Controller/ExceptionController.php @@ -42,12 +42,12 @@ public function __construct(ContentNegotiation $negotiation) { /** * Handles an exception on a request. * - * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception + * @param \Symfony\Component\HttpKernel\Exception\FlattenException $exception * The flattened exception. - * @param Symfony\Component\HttpFoundation\Request $request + * @param \Symfony\Component\HttpFoundation\Request $request * The request that generated the exception. * - * @return Symfony\Component\HttpFoundation\Response + * @return \Symfony\Component\HttpFoundation\Response * A response object to be sent to the server. */ public function execute(FlattenException $exception, Request $request) { @@ -57,7 +57,7 @@ public function execute(FlattenException $exception, Request $request) { return $this->$method($exception, $request); } - return new Response('A fatal error occurred: ' . $exception->getMessage(), $exception->getStatusCode()); + return new Response('A fatal error occurred: ' . $exception->getMessage(), $exception->getStatusCode(), $exception->getHeaders()); } /** diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index 9af0c74..b5aaa93 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -18,6 +18,7 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass; use Drupal\Core\DependencyInjection\Compiler\RegisterStringTranslatorsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; @@ -67,6 +68,8 @@ public function build(ContainerBuilder $container) { // Add the compiler pass that will process the tagged breadcrumb builder // services. $container->addCompilerPass(new RegisterBreadcrumbBuilderPass()); + // Add the compiler pass that will process tagged authentication services. + $container->addCompilerPass(new RegisterAuthenticationPass()); } /** diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterAuthenticationPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterAuthenticationPass.php new file mode 100644 index 0000000..dd975a9 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterAuthenticationPass.php @@ -0,0 +1,44 @@ +hasDefinition('authentication')) { + return; + } + // Get the authentication manager. + $matcher = $container->getDefinition('authentication'); + // Iterate all autentication providers and add them to the manager. + foreach ($container->findTaggedServiceIds('authentication_provider') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $matcher->addMethodCall('addProvider', array( + $id, + new Reference($id), + $priority, + )); + } + } +} diff --git a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php new file mode 100644 index 0000000..f9cf972 --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php @@ -0,0 +1,94 @@ +authenticationManager = $authentication_manager; + } + + /** + * Authenticates user on request. + * + * @see \Drupal\Core\Authentication\AuthenticationProviderInterface::authenticate() + */ + public function onKernelRequestAuthenticate(GetResponseEvent $event) { + if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { + $request = $event->getRequest(); + $this->authenticationManager->authenticate($request); + } + } + + /** + * Triggers authentication clean up on response. + * + * @see \Drupal\Core\Authentication\AuthenticationProviderInterface::cleanup() + */ + public function onRespond(FilterResponseEvent $event) { + if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { + $request = $event->getRequest(); + + $this->authenticationManager->cleanup($request); + } + } + + /** + * Pass exception handling to authentication manager. + * + * @param GetResponseForExceptionEvent $event + */ + public function onException(GetResponseForExceptionEvent $event) { + if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { + $this->authenticationManager->handleException($event); + } + } + + /** + * {@inheritdoc} + * + * The priority for request must be higher than the highest event subscriber + * accessing the global $user. + * The priority for the response must be as low as possible allowing e.g the + * Cookie provider to send all relevant session data to the user. + */ + public static function getSubscribedEvents() { + // Priority must be higher than LanguageRequestSubscriber as LanguageManager + // access global $user in case language module enabled. + $events[KernelEvents::REQUEST][] = array('onKernelRequestAuthenticate', 300); + $events[KernelEvents::RESPONSE][] = array('onRespond', 0); + $events[KernelEvents::EXCEPTION][] = array('onException', 0); + return $events; + } +} diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index 0b9bbad..98eef05 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -91,8 +91,6 @@ public function onRespond(FilterResponseEvent $event) { // @todo Revisit whether or not this is still appropriate now that the // Response object does its own cache control processing and we intend to // use partial page caching more extensively. - // Commit the user session, if needed. - drupal_session_commit(); // Attach globally-declared headers to the response object so that Symfony // can send them for us correctly. diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index d3ba7b3..975788a 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -876,6 +876,7 @@ protected function prepareEnvironment() { $this->originalUser = isset($user) ? clone $user : NULL; // Ensure that the current session is not changed by the new environment. + require_once DRUPAL_ROOT . '/' . settings()->get('session_inc', 'core/includes/session.inc'); drupal_save_session(FALSE); // Run all tests as a anonymous user by default, web tests will replace that // during the test set up. diff --git a/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php b/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php new file mode 100644 index 0000000..dea9359 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php @@ -0,0 +1,86 @@ + 'HttpBasic authentication', + 'description' => 'Tests for HttpBasic authentication provider.', + 'group' => 'Authentication', + ); + } + + /** + * Test http basic authentication. + */ + public function testHttpBasic() { + $account = $this->drupalCreateUser(); + + $this->basicAuthGet('router_test/test11', $account->name, $account->pass_raw); + $this->assertText($account->name, 'Account name is displayed.'); + $this->assertResponse('200', 'HTTP response is OK'); + $this->curlClose(); + + $this->basicAuthGet('router_test/test11', $account->name, $this->randomName()); + $this->assertNoText($account->name, 'Bad basic auth credentials do not authenticate the user.'); + $this->assertResponse('200', 'HTTP response is OK'); + $this->curlClose(); + + $this->drupalGet('router_test/test11'); + $this->assertResponse('401', 'Not authenticated on the route that allows only http_basic. Prompt to authenticate received.'); + } + + /** + * Does HTTP basic auth request. + * + * We do not use \Drupal\simpletest\WebTestBase::drupalGet because we need to + * set curl settings for basic authentication. + * + * @param string $path + * The request path. + * @param string $username + * The user name to authenticate with. + * @param string $password + * The password. + * + * @return string + * Curl output. + */ + protected function basicAuthGet($path, $username, $password) { + $out = $this->curlExec( + array( + CURLOPT_HTTPGET => TRUE, + CURLOPT_URL => url($path, array('absolute' => TRUE)), + CURLOPT_NOBODY => FALSE, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => $username . ':' . $password, + ) + ); + + $this->verbose('GET request to: ' . $path . + '
' . $out); + + return $out; + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php index b613121..9de96b0 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -93,6 +93,10 @@ protected function setUp() { // Load the Update API. require_once DRUPAL_ROOT . '/core/includes/update.inc'; + // Load Session API. + require_once DRUPAL_ROOT . '/core/includes/session.inc'; + drupal_session_initialize(); + // Reset flags. $this->upgradedSite = FALSE; $this->upgradeErrors = array(); diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestContent.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestContent.php index 56d567b..22a07cf 100644 --- a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestContent.php +++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestContent.php @@ -19,4 +19,15 @@ public function test1() { return 'abcde'; } + /** + * Provides example content for route specific authentication. + * + * @returns string + * The user name of the current logged in user. + */ + public function test11() { + global $user; + return isset($user->name) ? $user->name : ''; + } + } diff --git a/core/modules/system/tests/modules/router_test/router_test.routing.yml b/core/modules/system/tests/modules/router_test/router_test.routing.yml index 2a989df..9e481de 100644 --- a/core/modules/system/tests/modules/router_test/router_test.routing.yml +++ b/core/modules/system/tests/modules/router_test/router_test.routing.yml @@ -60,3 +60,10 @@ router_test_10: _content: '\Drupal\router_test\TestContent::test1' requirements: _access: 'TRUE' + +router_test_11: + pattern: '/router_test/test11' + options: + _auth: [ 'http_basic' ] + defaults: + _content: '\Drupal\router_test\TestContent::test11' diff --git a/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php b/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php index 45234a8..44e2882 100644 --- a/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php +++ b/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php @@ -65,7 +65,7 @@ public function onKernelResponseSessionTest(FilterResponseEvent $event) { */ static function getSubscribedEvents() { $events[KernelEvents::RESPONSE][] = array('onKernelResponseSessionTest', 300); - $events[KernelEvents::REQUEST][] = array('onKernelRequestSessionTest', 300); + $events[KernelEvents::REQUEST][] = array('onKernelRequestSessionTest', 100); return $events; } diff --git a/core/update.php b/core/update.php index b73e3ce..f97a973 100644 --- a/core/update.php +++ b/core/update.php @@ -427,7 +427,9 @@ function update_check_requirements($skip_warnings = FALSE) { update_prepare_d8_bootstrap(); // Determine if the current user has access to run update.php. -drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); +drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); +require_once DRUPAL_ROOT . '/' . settings()->get('session_inc', 'core/includes/session.inc'); +drupal_session_initialize(); // A request object from the HTTPFoundation to tell us about the request. // @todo These two lines were copied from index.php which has its own todo about