Problem/Motivation

In #2205295: Objectify session handler and #2228341: Objectify session management functions + remove session.inc the procedural code from session.inc has been converted to SessionHandler and SessionManager. However the new classes still have too many responsibilities. For example both of them access and set the global $user object. Setting the current user is clearly the responsibility of the authorization manager service.

In order to decouple session management from user authentication, we need a way to pass user-information read from the session table by the SessionHandler through to the user authentication system.

The way this is done in Symfony differs a little bit from the Drupal implementation. For example, the Symfony documentation states that...

Once the user is logged in, the entire User object is serialized into the session.

as well as:

In theory, only the id needs to be serialized, because the refreshUser() method refreshes the user on each request by using the id (as explained above). However in practice, this means that the User object is reloaded from the database on each request using the id from the serialized object.

In the Drupal implementation the session metadata (user id, hostname, timestamp) is stored separately from session data. Therefore we need a way to pass this information along with the session.

Proposed resolution

Register necessary Symfony session services and associate the session object to the request from within a http middleware that also cleans up the session afterwards.

In a second step (likely followup) refactor the SessionHandler and SessionManager to act on the session metadata bag instead of manipulating the global $user directly. Also move the responsibility of actually changing the user to the authentication manager.

Remaining tasks

User interface changes

API changes

Follow-up from #2228341: Objectify session management functions + remove session.inc.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Task because its part of the continuoes work on sessions.
Disruption Nearly none, beside people using the various objects directly, but people saving session data don't have to change their code.
CommentFileSizeAuthor
#119 2229145-symfony-session-facade-118.patch20.72 KBneclimdul
#119 2229145-interdiff-118.txt2.09 KBneclimdul
#116 2229145-symfony-session-facade-116.patch19.19 KBtadityar
#112 2229145-interdiff-112.txt1.14 KBneclimdul
#112 2229145-symfony-session-facade-112.patch21.3 KBneclimdul
#110 2229145-interdiff-110.txt2.62 KBneclimdul
#110 2229145-symfony-session-facade.patch21.1 KBneclimdul
#104 interdiff.txt3.76 KBznerol
#104 2229145-symfony-session-facade-follow-up-103.diff20.72 KBznerol
#95 d8.run-tests.patch1.11 KBalexpott
#95 2229145.95.patch21.41 KBalexpott
#83 register_symfony-2229145-82.patch20.3 KBalmaudoh
#75 register_symfony-2229145-75.patch20.27 KBneclimdul
#68 register_symfony-2229145-68.patch20.4 KBjoelpittet
#65 2229145-symfony-session-facade-65.patch20.4 KBneclimdul
#65 interdiff-2229145-65.txt511 bytesneclimdul
#61 interdiff-61.txt693 bytesneclimdul
#61 2229145-symfony-session-facade-61.patch20.43 KBneclimdul
#57 Screenshot 2014-12-31 10.07.05.png105.17 KBlarowlan
#57 Screenshot 2014-12-31 10.06.43.png80.49 KBlarowlan
#55 symfony-session.55.patch21.09 KBlarowlan
#55 interdiff.txt1.26 KBlarowlan
#54 symfony-session.54.patch21.25 KBlarowlan
#54 interdiff.txt5.29 KBlarowlan
#51 interdiff.txt1.11 KBznerol
#51 2229145-symfony-session-facade-51.diff20.23 KBznerol
#48 2229145-symfony-session-facade-48.diff19.62 KBznerol
#45 2229145-symfony-session-facade-45.diff68.02 KBznerol
#39 interdiff.txt752 bytesznerol
#39 2229145-symfony-session-facade-39.diff19.55 KBznerol
#37 interdiff.txt5.18 KBznerol
#37 2229145-symfony-session-facade-37.diff19.54 KBznerol
#35 2229145-symfony-session-facade-35.diff17.54 KBznerol
#35 interdiff.txt600 bytesznerol
#34 interdiff.txt2.32 KBznerol
#34 2229145-symfony-session-facade-34.diff17.54 KBznerol
#32 interdiff.txt5.17 KBznerol
#32 2229145-symfony-session-facade-32.diff18.69 KBznerol
#29 interdiff.txt1.52 KBznerol
#29 2229145-symfony-session-facade-28.diff18.79 KBznerol
#28 interdiff.txt2.36 KBznerol
#28 2229145-symfony-session-facade-27.diff18.04 KBznerol
#26 interdiff.txt1.29 KBznerol
#26 2229145-symfony-session-facade-26.diff16.67 KBznerol
#24 interdiff.txt924 bytesznerol
#24 2229145-symfony-session-facade-23.diff16.06 KBznerol
#20 interdiff.txt883 bytesznerol
#20 2229145-symfony-session-facade-18.diff16.13 KBznerol
#18 interdiff.txt883 bytesznerol
#18 drupal-no_core_in_component_test-2282673-38.patch8.18 KBznerol
#16 2229145-symfony-session-facade-16.diff15.26 KBznerol
#13 2229145-symfony-session-facade.diff15.23 KBznerol
#6 SymfonySessions.patch46.83 KBneclimdul
#5 SymfonySessions.patch43.37 KBneclimdul
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

znerol’s picture

Status: Reviewed & tested by the community » Active
Issue tags: -PHP 5.4, -API change +symfony
Parent issue: » #1858196: [meta] Leverage Symfony Session components
znerol’s picture

I realize that the Symfony Session requires a service implementing SessionStorageInterface, therefore we first need to adapt the Drupal SessionManager to the SessionStorageInterface.

znerol’s picture

Status: Active » Postponed
jibran’s picture

Status: Postponed » Active

Back to active again.

neclimdul’s picture

Status: Active » Needs review
FileSize
43.37 KB

Here you go. Session tests and phpunit tests pass. Lets see what the test bot can do with everything else.

I wouldn't say this is a perfect patch. I wasn't able to completely get rid of the manager interface which I see as sort of a sign we haven't completely made it through the conversion but this meets the goals of this issue at least.

neclimdul’s picture

FileSize
46.83 KB

Woops, missed a file.

neclimdul’s picture

https://github.com/neclimdul/drupal/commits/symfony_session for commits to get here. Tried to make the steps I took as clean and explicit as possible.

The last submitted patch, 5: SymfonySessions.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 6: SymfonySessions.patch, failed testing.

znerol’s picture

I think we need to get in #2272987: Do not persist session manager before.

joelpittet’s picture

Issue tags: +Needs reroll

#2272987: Do not persist session manager Is in, this needs a re-roll.

znerol’s picture

znerol’s picture

Status: Needs work » Needs review
FileSize
15.23 KB

Tried to reroll #6 but instead started over because of too many conflicts. Also I believe we really should keep issues like this scoped, i.e. no renames and no refactorings if they are not strictly required to fulfill the goal.

I agree that we should find a dedicated home for the mixed-mode-ssl stuff, I also agree that the session write protection needs to be decoupled (see #2338727: Replace static SessionManager::$enabled property with WriteSafeSessionHandler class and resolve hidden circular dependency between SessionManager and SessionHandler) and indeed, the session handler stack needs to be exposed in the container at some point. But let's focus on the introduction of the Symfony Session facade in order to have the API in place.

Instead of using a subscriber, I propose to introduce a stack middleware. This will turn green as soon as #2230121: Remove exit() from FormBuilder is resolved.

znerol’s picture

Status: Needs review » Needs work

The last submitted patch, 13: 2229145-symfony-session-facade.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
15.26 KB

Reroll.

Status: Needs review » Needs work

The last submitted patch, 16: 2229145-symfony-session-facade-16.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
8.18 KB
883 bytes

This looks like trouble caused by persisting services (request referencing the old session across container rebuild). Let's see whether this is true.

Status: Needs review » Needs work

The last submitted patch, 18: drupal-no_core_in_component_test-2282673-38.patch, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
16.13 KB
883 bytes

Wrong patch.

Status: Needs review » Needs work

The last submitted patch, 20: 2229145-symfony-session-facade-18.diff, failed testing.

The last submitted patch, 20: 2229145-symfony-session-facade-18.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
16.06 KB
924 bytes

Only access the session service if the previous request had a session.

Status: Needs review » Needs work

The last submitted patch, 24: 2229145-symfony-session-facade-23.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
16.67 KB
1.29 KB

Now that the cookie provider pulls the session from the request, it is necessary to also ensure that the session is set on the request from within prepareLegacyRequest().

Status: Needs review » Needs work

The last submitted patch, 26: 2229145-symfony-session-facade-26.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
18.04 KB
2.36 KB

Prevent that the session service is accessed in the early installer.

znerol’s picture

Change Authentication\Provider\Cookie::apply() to return TRUE only if there is a session on the request.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
    @@ -18,27 +18,10 @@
    +    return $request->hasSession();;
    

    Let's add more semicolons, what about 12? :)

  2. +++ b/core/lib/Drupal/Core/DrupalKernel.php
    @@ -707,6 +710,14 @@ protected function initializeContainer($rebuild = FALSE) {
    +    if (($request_stack = $this->container->get('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE))) {
    +      if ($request = $request_stack->getCurrentRequest()) {
    +        if ($request->hasSession()) {
    +          $request->setSession($this->container->get('session'));
    +        }
    +      }
    +    }
    

    It would be great to explain why we have to still set the session here. Isn't that covered by the session middleware + prepareLegacyRequest?

  3. +++ b/core/lib/Drupal/Core/DrupalKernelInterface.php
    @@ -101,13 +101,15 @@ public function handlePageCache(Request $request);
    +   *   Whether or not the session should accessible from the request.
    

    There is certainly a verb missing in here :) Can you explain when you would actually not need this here? Would drush be an example of it?

  4. +++ b/core/lib/Drupal/Core/StackMiddleware/Session.php
    @@ -0,0 +1,63 @@
    +   * @param \Symfony\Component\HttpFoundation\Session\Session $session
    

    ... so we cannot typehint the interface because of SessionInterface::getFlashBagpretty neat!

  5. +++ b/core/lib/Drupal/Core/StackMiddleware/Session.php
    @@ -0,0 +1,63 @@
    +    if ($type === self::MASTER_REQUEST) {
    +      $request->setSession($this->session);
    +    }
    ...
    +    if ($type === self::MASTER_REQUEST) {
    +      $this->session->save();
    +    }
    

    Can you add some short comment about why we check for just the master request?

  6. +++ b/core/modules/user/user.module
    @@ -1522,6 +1522,7 @@ function user_logout() {
     
    +  $user->setAccount($GLOBALS['user'] = new AnonymousUserSession());
    

    So this should have been done before?

martin107’s picture

Issue tags: -Needs reroll
znerol’s picture

  1. Better use too many than too few :)
  2. Comment added
  3. Let's try something different. The only reason for $use_session is the early installer lacking a database. On closer inspection it seems to me that it is not really appropriate to call prepareLegacyRequest() there at all because it tries to boot the kernel a second time, pushes the request on the stack again and whatnot. Instead we could just sidestep prepareLegacyRequest() and directly call preHandle().
  4. Pull request #12420
  5. Well, I frankly do not know. This is what Symfony does in its SessionListener. Session is as global as something can be in PHP, so there is not much of a point dragging that through the request stack?
  6. Interesting question. The aim here is to replace the direct call to session_destroy() with its Symfony equivalent which is Session::invalidate(). Seems like there is something missing in SessionManager::regenerate when $destroy parameter is TRUE.
znerol’s picture

Status: Needs review » Needs work

Uhm, #12375

znerol’s picture

Status: Needs work » Needs review
FileSize
17.54 KB
2.32 KB

In Symfony the session is intended to be destroyed with Session::invalidate(). Regrettably this method is currently broken (see Symfony issue #12375) and may lead to the creation of spurious session records in the database. Therefore revert to use session_destroy() instead and also revert the changes to SessionManager::regenerate().

znerol’s picture

After the request has been handled, save the session which is present on the request.

Crell’s picture

+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -569,8 +569,9 @@ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch =
+    // Setup services which are normally initialized from within stack
+    // middleware or during the request kernel event.
+    $request->setSession($this->container->get('session'));

I'm not entirely clear on this comment. I read it as "we should do this in a middleware but we'll do it here instead", which seems odd because we do have a middleware.

Other than that, I'm happy here. znerol++

znerol’s picture

Re #30.4, let's just type-hint SessionInterface. After all this is what $request->setSession() and it is also the declared return type of $request->getSession(). As pointed out in pr 12420, Symfony itself wrongly expects the getFlashBag() method in several places without checking for Session instead of SessionInterface.

Re #30.5 I talked to @Tobion (Symfony) about this and he pointed out that in Symfony, the code initiating a subrequest is responsible for carrying over the session. This is done automatically when using $request->duplicate() because that uses cloning (One example is the Controller::forward() in Symfony FrameworkBundle. The occurrences of Request::create() in non-test Symfony code are not quite consistent. For example InlineFragmentRenderer::createSubRequest() carries the session over to the subrequest while Esi::handle() does not.

In Drupal there are two different use-cases where Request::create is called. First, there are occurrences where the created request object is passed to HttpKernel::handle(). Those include DefaultExceptionHtmlSubscriber::makeSubrequest() and CommentController::commentPermalink(). In the long run those should maybe use $request->duplicate and pass in the controller directly, just like Controller::forward of the FrameworkBundle. This is only possible though when system paths can be eliminated entirely. In the meantime the session should be carried over to the subrequest in those two instances.

The second use case is resolving paths to routes. This is done in PathValidator::getUrl, Shortcut::preSave() and PathBasedBreadcrumbBuilder::getRequestForPath(). The latter two in fact should be using the PathValidator instead. In addition AccessAwareRouter::match() instantiates a new request. Unfortunately there is no reference to the parent request and thus carrying over the session is impossible in that case. Granted that PathValidator::getPathAttributes() calls $router->match(), there is no way to propagate the session for any instance mentioned in this paragraph. As of #2331079: Use RouteMatch in access-checks and remove RequestHelper::duplicate() access checks which require access to the request are only when actually checking an incoming request. That means that the $request is not available to access checkers when validating paths in the first place, therefore IMHO it is legit to not propagate the session in that case.

Re #36 the function context line of this hunk is a bit confusing. That specific comment is in prepareLegacyRequest() not in handle().

Status: Needs review » Needs work

The last submitted patch, 37: 2229145-symfony-session-facade-37.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
19.55 KB
752 bytes

Fix copy paste accident.

Crell’s picture

Status: Needs review » Reviewed & tested by the community

Let's do then.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs issue summary update, +Needs change record, +Needs change record updates

This issue is a normal task so we need to outline how it fits within the allowable Drupal 8 beta criteria. Can someone add Drupal 8 beta phase evaluation template to the issue summary.

And we need a change record here that details the 8.x to 8.x changes as well as going through the existing CRs and making sure the 7.x to 8.x is covered and correct.

dawehner’s picture

Issue summary: View changes
Priority: Normal » Major
Status: Needs work » Reviewed & tested by the community

Raised the priority.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Needs change record

Afaics we will need to update https://www.drupal.org/node/2228871? Can someone else confirm and attach any other CRs that need updating.

znerol’s picture

Status: Needs work » Needs review
Issue tags: -Needs issue summary update

Re #43 yes, 2228871 needs to be updated. Additionally filed https://www.drupal.org/node/2380327.

Removing the "Needs issue summary update" tag, this was addressed in #42 I guess.

znerol’s picture

Reroll.

Status: Needs review » Needs work

The last submitted patch, 45: 2229145-symfony-session-facade-45.diff, failed testing.

jibran’s picture

Issue tags: +Needs reroll

Sorry but #45 was not a proper reroll

znerol’s picture

Oh, right. Let's try again.

znerol’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 48: 2229145-symfony-session-facade-48.diff, failed testing.

znerol’s picture

Status: Needs work » Needs review
FileSize
20.23 KB
1.11 KB

Replicate the changes introduced into prepareLegacyRequest by #2362227: Replace all instances of current_path() to the installer.

dawehner’s picture

  1. +++ b/core/includes/install.core.inc
    @@ -414,13 +415,11 @@ function install_begin_request(&$install_state) {
    -  // After setting up a custom and finite module list in a custom low-level
    -  // bootstrap like here, ensure to use ModuleHandler::loadAll() so that
    -  // ModuleHandler::isLoaded() returns TRUE, since that is a condition being
    -  // checked by other subsystems (e.g., the theme system).
    -  $module_handler->loadAll();
     
    -  $kernel->prepareLegacyRequest($request);
    +  // Load all modules and perform request related initialization.
    +  $kernel->preHandle($request);
    +  $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
    +  $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
    

    Can you make at least a @see prepareLegacyRequest from which this code is copied from?

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
    @@ -111,6 +111,11 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $path, $s
    +        // Carry over the session to the subrequest.
    +        if ($session = $request->getSession()) {
    +          $sub_request->setSession($session);
    +        }
    

    So everyone who does a subrequest would need to do that? Given that this sounds like a generic functionality we need somewhere.

larowlan’s picture

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
    @@ -111,6 +111,11 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $path, $s
    +        // Carry over the session to the subrequest.
    +        if ($session = $request->getSession()) {
    +          $sub_request->setSession($session);
    +        }
    
    +++ b/core/modules/comment/src/Controller/CommentController.php
    @@ -130,6 +130,10 @@ public function commentPermalink(Request $request, CommentInterface $comment) {
    +      // Carry over the session to the subrequest.
    +      if ($session = $request->getSession()) {
    +        $redirect_request->setSession($session);
    +      }
    

    I agree with @dawehner - can we automate this with an event subscriber?

  2. +++ b/core/lib/Drupal/Core/Session/SessionManager.php
    @@ -329,7 +329,7 @@ protected function getSessionDataMask() {
    -      $mask[$key] = empty($_SESSION[$key]);
    +      $mask[$key] = !empty($_SESSION[$key]);
    

    This is a fairly major change in logic - can you elaborate why it is needed?

larowlan’s picture

FileSize
5.29 KB
21.25 KB

Fixes #52 - attempts to add event subscriber to hand down session from parent requests to sub-requests.

larowlan’s picture

Issue tags: -Needs reroll +CriticalADay
FileSize
1.26 KB
21.09 KB
  1. +++ b/core/lib/Drupal/Core/EventSubscriber/SubRequestSessionSubscriber.php
    @@ -0,0 +1,66 @@
    +use Drupal\Core\Authentication\AuthenticationProviderInterface;
    ...
    +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
    +use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
    

    unneeded

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/SubRequestSessionSubscriber.php
    @@ -0,0 +1,66 @@
    +  public function __construct(RequestStack $request_stack) {
    

    help if you added this to core.services.yml

larowlan’s picture

so how in the world did #54 pass?

larowlan’s picture

the subscriber seems to work - manual testing of comment permalink

znerol’s picture

Re #52.2 and the subsequent changes by @larowlan: This is answered in #37. I do not see any reason why we should deviate from Symfony in this regard. Should Drupal decide to ignore that, then there is a much better solution than introducing SubRequestSessionSubscriber: Kernel middlewares are also executed on subrequests, and hence the Session middleware just could take care of populating the request session regardless of request type.

Re #53.2 this is a bugfix on untested code. Currently there is no non-metadata session bag and therefore this bug went undetected before.

larowlan’s picture

So we need 51 with the docs changes of 54?

neclimdul’s picture

I was going to do the reroll but I ran into a problem.

@see doesn't clearly convey any meaning in this context without reading this issue. Especially since its missing so much of the logic from that method so any documentation you could gleam from that method looses meaning. I think it would be better to write clearer documentation of what we're doing and then if we really feel strongly document that it is similar to or loosely based on the other method.

neclimdul’s picture

FileSize
20.43 KB
693 bytes

I played around to better understand how to document this and the routing properties don't seem necessary. I guess the routing properties are sort of a "just in case" sort of thing.

Needed a reroll after #2347877: Move DrupalKernel::initializeCookieGlobals() into a SessionConfiguration service so that's done too.

cpj’s picture

I reviewed the code and have tested the patch from #61 in a similar fashion to #57. I have also stepped through the code in the debugger of my IDE to understand what the StackMiddleWare/Session is doing. As far as I can tell is works as expected, but what about the additional code in #55 ? Is this still needed (I think so...)

cpj’s picture

I've just noticed that the class explanation text, lines 14 - 16 of StackMiddleware/Session.php will need to change. The current text is the same as StackMiddleware/PageCache.php

cpj’s picture

In #55, line 10

+++ b/core/core.services.yml
@@ -1030,6 +1035,21 @@ services:
+  session.subrequest_session_subscrber:

Typo - I assume this should be session.subrequest_session_subscriber

Does anything use this service yet ?

neclimdul’s picture

FileSize
511 bytes
20.4 KB

So we need 51 with the docs changes of 54?

I made the assumption that we didn't need #55

znerol’s picture

I made the assumption that we didn't need #55

I agree.

cpj’s picture

@neclimdul & @znerol - thanks for the clarification. So I review & tested #65 with the latest HEAD and it works in our environment, so this looks good-to-go to me

joelpittet’s picture

#65 re-roll patch -p1 worked, but git apply didn't.

Is there any manual testing we could do to confirm this is good to go? It looks great:)

cpj’s picture

Is there some reason why this can't be marked as "Reviewed & tested by the community" ?

joelpittet’s picture

@cpj nope, if you have reviewed and tested, then you can set the status;)

almaudoh’s picture

Let me take a look at it...

almaudoh’s picture

Issue summary: View changes
cpj’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 68: register_symfony-2229145-68.patch, failed testing.

neclimdul’s picture

Status: Needs work » Needs review
FileSize
20.27 KB

Another re-roll. no changes.

joelpittet’s picture

Status: Needs review » Reviewed & tested by the community

Re-RTBC as per #73 and I double checked #68 vs #75 to see if there were any sneaky re-roll accident and there were not so if testbot agrees it will stay.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 75: register_symfony-2229145-75.patch, failed testing.

Status: Needs work » Needs review
joelpittet’s picture

Status: Needs review » Reviewed & tested by the community

Testbot hickup, thanks @larowlan

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll
git ac https://www.drupal.org/files/issues/register_symfony-2229145-75.patch
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 20761  100 20761    0     0  78620      0 --:--:-- --:--:-- --:--:-- 79240
error: patch failed: core/core.services.yml:991
error: core/core.services.yml: patch does not apply
alexpott’s picture

      $session_manager = \Drupal::service('session_manager');
      // If the password has been changed, delete all open sessions for the
      // user and recreate the current one.
      if ($this->pass->value != $this->original->pass->value) {
        $session_manager->delete($this->id());
        if ($this->id() == \Drupal::currentUser()->id()) {
          $session_manager->regenerate();
        }
      }

      // If the user was blocked, delete the user's sessions to force a logout.
      if ($this->original->status->value != $this->status->value && $this->status->value == 0) {
        $session_manager->delete($this->id());
      }

Does this code in \Drupal\user\Entity\User need to change too?

almaudoh’s picture

Status: Needs work » Needs review

Straight re-roll

almaudoh’s picture

Well...the actual patch

joelpittet’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs reroll

Back to RTBC.

joelpittet’s picture

Thank you for rerolling @almaudoh

almaudoh’s picture

Issue summary: View changes

The change #81was already in the patch. So I'll mark this RTBC per #79.

joelpittet’s picture

Oh I missed that, @almaudoh, do you know if the other $session_manager variables are supposed to change as well for ->delete() calls? @alexpott may not have been refering that so just checking.

  $session_manager->delete($this->id());

In \Drupal\user\Entity\User

almaudoh’s picture

Status: Reviewed & tested by the community » Needs review

Good point @joelpittet

almaudoh’s picture

Status: Needs review » Reviewed & tested by the community

Right now, there is still a lot of coupling between SessionManager, SessionHandler and Sessions which would be decoupled in a followup (mentioned in the IS), these others would be in that follow up.

znerol’s picture

We cannot move $session_manager->delete($this->id()); into the SessionHandler because this is a method which cannot be implemented easily by some session storage implementations (e.g. memcache).

I'd rather move the delete method into a UserSessionStorageHelper class+service which third party storage modules may choose to provide or not.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed b926f5d and pushed to 8.0.x. Thanks!

Thanks for adding the beta evaluation to the issue summary.

I've published the change record - I think we need to update https://www.drupal.org/node/2228871

  • alexpott committed b926f5d on 8.0.x
    Issue #2229145 by znerol, neclimdul, larowlan, joelpittet, almaudoh:...
alexpott’s picture

Status: Fixed » Needs work

Drats... this breaks run-tests without a database. Which is about to be used to remove the drush dependency from testbots. I have to revert because we shouldn't be creating critical followup of major issues.

  • alexpott committed 91ae64a on 8.0.x
    Revert "Issue #2229145 by znerol, neclimdul, larowlan, joelpittet,...
alexpott’s picture

Status: Needs work » Needs review
FileSize
21.41 KB
1.11 KB

Here's the fix. The small patch is the interdiff.

alexpott’s picture

The issue to remove drush from the testbots is #2206501: Remove dependency on Drush from test reviews

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/core.services.yml
@@ -475,6 +475,11 @@ services:
+  http_middleware.session:
+    class: Drupal\Core\StackMiddleware\Session
+    arguments: ['@session']
+    tags:
+      - { name: http_middleware, priority: 50 }

@@ -1089,6 +1094,15 @@ services:
+  session:
+    class: Symfony\Component\HttpFoundation\Session\Session
+    arguments: ['@session_manager', '@session.attribute_bag', '@session.flash_bag']

This is problematic because session_manager depends on the database so we'll content to the database even we we don't need to.

znerol’s picture

we'll content to the database even we we don't need to.

Could you explain what you mean. I do understand that.

almaudoh’s picture

#97:

we'll content to the database even we we don't need to

I think the concern is that we would tend to make writes to the session table in the db even when there is no session or nothing in the session.

Looking thru the code, there's nothing that tells me that would happen. But there's nothing that assures me it won't happen either. However, I know one of the follow ups to be created for decoupling authentication from session management would handle this.

@znerol: You may probably have a better answer than mine.

alexpott’s picture

So without the additions in #95 running $ php ./core/scripts/run-tests.sh --list in a checkout of drupal which has not been installed - ie. no settings.php with db settings - generates the following error:

PHP Fatal error:  Uncaught exception 'Drupal\Core\Database\ConnectionNotDefinedException' with message 'The specified database connection is not defined: default' in /Volumes/devdisk/dev/drupal/core/lib/Drupal/Core/Database/Database.php:366
Stack trace:
#0 /Volumes/devdisk/dev/drupal/core/lib/Drupal/Core/Database/Database.php(171): Drupal\Core\Database\Database::openConnection('default', 'default')
#1 [internal function]: Drupal\Core\Database\Database::getConnection('default')
#2 /Volumes/devdisk/dev/drupal/core/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ContainerBuilder.php(960): call_user_func_array(Array, Array)
#3 /Volumes/devdisk/dev/drupal/core/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ContainerBuilder.php(490): Symfony\Component\DependencyInjection\ContainerBuilder->createService(Object(Symfony\Component\DependencyInjection\Definition), 'database')
#4 /Volumes/devdisk/dev/drupal/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php(36): Symfony\Compo in /Volumes/devdisk/dev/drupal/core/lib/Drupal/Core/Database/Database.php on line 366

This is happening because the session manager has the database injected and calling Database::openConnection(). So we're creating a connection to the db super early.

znerol’s picture

Instead of injecting the session directly into the session middleware, we could inject the container. This would allow us to retrieve the session service from the container and inject it into the request conditionally, i.e. only if not running in CLI mode.

Making the session service lazy might help also.

alexpott’s picture

re #101 but then we still have more page_cache_without_database problems, no?

znerol’s picture

No, the session middleware runs after (i.e. inside) the page cache middleware. Therefore if the session service is only initialized when its handle() method is invoked (instead of upon service construction), then the page cache is already done.

znerol’s picture

Status: Needs work » Needs review
FileSize
20.72 KB
3.76 KB

It seems to me that guarding against PHP_SAPI === 'cli' is the most effective way of fixing the run-tests.sh issue. Interdiff is against #83.

cpj’s picture

#104 works in our environment and the latest changes make sense to me, so I'm going to mark this as RTBC. OK ?

cpj’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Discussed with @catch on IRC - he said "so we initialize the database connection just to check a cookie, yes I don't like that either." I feel the same. We should only connect to the db if the cookie exists.

znerol’s picture

I do not think that this is any different than the current situation. We initialize the session manager on every request and we need to do that also in the future as long as we allow anyone to trigger a new session by simply putting stuff into $_SESSION.

cpj’s picture

@alexpott #107 - interesting comment, but I think @znerol is correct. What is there in this patch that changes this situation ?

neclimdul’s picture

Status: Needs work » Needs review
FileSize
21.1 KB
2.62 KB

Yeah I think cpj and znerol are right. The database behavior hasn't changed in this patch. The problem in #100 might have been miss-stated a bit though. The problem is actually run-test.sh calling DrupalKernel::prepareLegacyRequest() and prepareLegacyRequest changing slightly. Enough work has gone in that we don't have to pretend that we are working with a request in run-test so really run-test doesn't need to call prepareLegacyRequest anymore we can just call boot() and loadLegacyIncludes() directly.

Also, we can problem get rid of those PHP_SAPI checks. If something on the CLI is going through the http_kernel we've got problems.

Full disclosure for discussion, this does mean that cli tools have to assume a database connection or not use prepareLegacyRequest so that's sort of a regression. It doesn't affect drush though from what I can tell and run-test.sh was the only CLI tool we where using it in.

Status: Needs review » Needs work

The last submitted patch, 110: 2229145-symfony-session-facade.patch, failed testing.

neclimdul’s picture

Status: Needs work » Needs review
FileSize
21.3 KB
1.14 KB

My bad, I was poking at some other code and forgot to remove it before making the patch.

Also, I was mistaken and some tests actually require the prepareLegacyRequests. They fail in the runner which means they're probably unit tests. Unit tests relying on global state is pretty much a bug but we can follow up to fix that. For now the runner can boot legacy request stuff when it is executing a test.

Status: Needs review » Needs work

The last submitted patch, 112: 2229145-symfony-session-facade-112.patch, failed testing.

joelpittet’s picture

@neclimdul just a small nitpick:

+++ b/core/scripts/run-tests.sh
@@ -32,9 +32,11 @@
-$kernel->prepareLegacyRequest($request);
+$kernel->boot();
+$kernel->loadLegacyIncludes();;

Double ;;.

znerol’s picture

The PHP_SAPI === 'cli' checks are not only fixing the problem, but they actually make sense in those places. That way we clearly communicate the fact that there is no session when running from the command line. What else do we need?

tadityar’s picture

Status: Needs work » Needs review
FileSize
19.19 KB

Removed the extra ';'.

Status: Needs review » Needs work

The last submitted patch, 116: 2229145-symfony-session-facade-116.patch, failed testing.

neclimdul’s picture

Clearly I was wrong that we have done enough work in run-tests to get rid of those assumptions and my spot checks where not representative. The attached patch rolls back those CLI checks and run-tests changes and is the last RTBC'd patch#104 with a spelling fix.

To re-address alex and catch's concerns from when it was bumped NR, the database behavior is unchanged and if that is a problem it was an earlier regression not attached to this improvement. Since this basically gets us to the architecture we've been working for I would prefer if we can address that problem with in a separate issue with profiling and specific review. We might be able to just use a lazy proxy or something but we should better understand the problem.

neclimdul’s picture

that moment when you realize you where distracted by an email and forgot to attach the patches.

neclimdul’s picture

Status: Needs work » Needs review

and the status. please confirm things are working testbot.

cpj’s picture

@neclimdul #118 - I agree that this is not the right place to start optimizing / fixing database behavior concerns. This patch changes nothing in that regard so "no harm, no foul", right ? But I'd be totally up for helping on a separate issue in that direction, if someone was to open one...

Status: Needs review » Needs work

The last submitted patch, 119: 2229145-symfony-session-facade-118.patch, failed testing.

catch’s picture

Are we sure that the middleware won't be instantiated on page cache hits? That would be a regression compared to the status quo too (if using memcache database backends + database session storage for example).

I opened #2424451: Session manager (and others) should not initialize database connection if no query needs to be run for the more general problem.

cpj’s picture

@catch #123 - I haven't been testing with caching enabled, so to be honest I'm not sure if the behavior is different than before on page cache hits... Logically I'm not sure why this patch should change what happens on page cache hits but I will try to take a look at it...

catch’s picture

It depends whether all the middlewares get instantiated before any particular middleware gets executed or not.

znerol’s picture

Are we sure that the middleware won't be instantiated on page cache hits? That would be a regression compared to the status quo too (if using memcache database backends + database session storage for example).

Not so much of a regression, with #104 the database will not be initialized for page cache hits. The session middleware class will be loaded though, but nothing more.

It depends whether all the middlewares get instantiated before any particular middleware gets executed or not.

Thats true, all middlewares are instantiated the moment when the stacked kernel is instantiated. That is also the reason why we do not inject config into the page cache middleware, and why I moved out the session dependency in #104. Note that contrib can destroy your whole page cache performance when introducing middlewares with heavy dependencies.

That said, if we want to have the session on the request, then we need to put it onto the request somewhere. This needs to happen before authentication, i.e. before anything tries to log something (at the moment) or before routing (when #2286971: Remove dependency of current_user on request and authentication manager lands).

Status: Needs work » Needs review
neclimdul’s picture

There's a random failure in HEAD it seems. #2424743: Random testbot fail: "The page does not have double escaped HTML tags." in Drupal\field_ui\Tests\ManageFieldsTest.

Thanks catch, that's exactly the issue I was thinking and a great summary.

In case it wasn't clear because it wasn't in the comment, in #104 the patch has the middleware take the container instead of the session objects so it only creates services when its run and after it knows it needs them.

almaudoh’s picture

Status: Needs review » Reviewed & tested by the community

Core committer concerns raised in #107 have been addressed with a follow-up. The currrent patch does not introduce any regressions from HEAD and further optimizations can still be done in followups. Patch has been tested by @cpj in a dev environment and is ok.

Since all I did in this patch was a re-roll, I believe I can go ahead and RTBC this.

cpj’s picture

So I've not tested it again, but I've reviewed the latest version and there appear to be no substantial changes to the version I tested, so I agree this should be marked as RTBC as suggested by @almaudoh in #130.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Given that my contribution to this patch was only fixing run-tests.sh I think it is fine for me to commit.

Committed 7f6f61f and pushed to 8.0.x. Thanks!

Thanks for adding the beta evaluation to the issue summary.

diff --git a/core/lib/Drupal/Core/StackMiddleware/Session.php b/core/lib/Drupal/Core/StackMiddleware/Session.php
index 0fb786a..f92e778 100644
--- a/core/lib/Drupal/Core/StackMiddleware/Session.php
+++ b/core/lib/Drupal/Core/StackMiddleware/Session.php
@@ -16,8 +16,7 @@
  *
  * Note, the session service is not injected into this class in order to prevent
  * premature initialization of session storage (database). Instead the session
- * is retrieved from the container only immediately before passing an incomming
- * request on to the wrapped kernel.
+ * service is retrieved from the container only when handling the request.
  */
 class Session implements HttpKernelInterface {
 

In discussion with @catch we decided to change the doblock on commit.

  • alexpott committed 7f6f61f on 8.0.x
    Issue #2229145 by znerol, neclimdul, larowlan, alexpott, joelpittet,...
almaudoh’s picture

Wooot! :) Thanks @alexpott. We're getting there.

cosmicdreams’s picture

After this change, what uses SessionManager? I have attempted to find what code instantiates the SessionManager and have only found service container (generated) code that uses it.

Does that mean that we should remove the SessionManager in a follow up issue?

Edit: Found one reference to SessionManager's enable method in \Drupal\Core\Session\AccountSwitcher but that's the only one so far.

cosmicdreams’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Wim Leers’s picture

jibran’s picture

We have a @todo against this issue in core/lib/Drupal/Core/Session/SessionManager.php

    // @todo When not using the Symfony Session object, the list of bags in the
    //   NativeSessionStorage will remain uninitialized. This will lead to
    //   errors in NativeSessionHandler::loadSession. Remove this after
    //   https://www.drupal.org/node/2229145, when we will be using the Symfony
    //   session object (which registers an attribute bag with the
    //   manager upon instantiation).

Do we have an issue to remove this?

longwave’s picture