Problem/Motivation
Follow-up to #2286971: Remove dependency of current_user on request and authentication manager. Drupal SessionHandler
's responsibility is to persist the session. However, the current implementation goes beyond Session handling and also does what should be cookie authentication.
Proposed resolution
- Store the
uid
as a session attribute upon user login. Let cookie authentication provider use that to load the logged in user. - Remove authentication code / responsibilities from
SessionHandler
toCookie
authentication provider. - Remove any dependencies of
SessionHandler
andSessionManager
from current user (since session does not have any means to determine whether a request was authenticated via cookie auth or any third-party provider, see also #79). - Move the cookie authentication provider to the
user
module, because that's also where the required login/logout functionality is implemented.
This issue fixes #2432911: Provide test coverage to prove that a third party authentication provider does not automatically start a session and #2468873: Test that the authentication provider doesn't leak authentication credentials from the previous request.
Remaining tasks
Reviews and commit
User interface changes
None
API changes
The current user's uid is introduced as a session attribute.
Beta phase evaluation
Issue category | Task because this is code refactoring |
---|---|
Issue priority | Major |
Prioritized changes | This issue reduces fragility of the Drupal 8 session management and authentication systems and improves code. This issue is also a blocker to several other issues. |
Disruption | Not disruptive |
Previous Summary
In #2205295: Objectify session handler we moved our session handler to a class, but its still the same old spaghetti code a la OOP.
- Split the cookie logic to a proxy class and then the storage (database logic) to another.
- Make the the proxy set the cookies through the Response object and not using directly setcookie(), with the help of SessionListener
That way we are close to the Proxy - Handler system of symfony session and will bring us one step closer to #1858196: [meta] Leverage Symfony Session components
Comment | File | Size | Author |
---|---|---|---|
#110 | interdiff.txt | 1.37 KB | pfrenssen |
#110 | 2228393-110.patch | 19.66 KB | pfrenssen |
#86 | 2228393-split_sessionhandler-86.patch | 17.26 KB | almaudoh |
Comments
Comment #1
ParisLiakos CreditAttribution: ParisLiakos commentedComment #2
ParisLiakos CreditAttribution: ParisLiakos commentedwrong status.
Comment #3
sunComment #4
ParisLiakos CreditAttribution: ParisLiakos commentedComment #5
cosmicdreams CreditAttribution: cosmicdreams commentedIs someone working on this in Szeged? I'm happy to pick up the work of others or attempt a first patch. Won't have time until Sunday.
Comment #6
znerol CreditAttribution: znerol commentedI will presumably have a first patch ready soon.
Comment #7
ParisLiakos CreditAttribution: ParisLiakos commentedThis is postponed to #2228341: Objectify session management functions + remove session.inc because we need a session service first to set to the request <- Requirement to use SessionListener.
Comment #8
znerol CreditAttribution: znerol commentedOk, this is just an experiment. The goal here is to discover ways of extracting storage from the session handler. The storage handler should be as simple as possible, but at the same time should account for mixed mode ssl sessions.
I did not bother to inject the storage into the service container yet. This definitely needs some more thought.
Comment #9
ParisLiakos CreditAttribution: ParisLiakos commentedyou are duplicating work that has already happened. but anyway. you can spend your time as you wish
Comment #10
znerol CreditAttribution: znerol commented@ParisLiakos: I am well aware of the work that has been done already, and I appreciate it very much. Nevertheless sometimes it can be helpful to rethink and explore a bit in different directions in order to deblock things.
Introducing
SslAwareSessionHandlerInterface
in this iteration. Session storage handlers implementing this interface automatically profit from SSL and SSL mixed-mode logic. Also removed the requirement to supply two session storage handlers for SSL sites.Comment #11
jibran10: 2228393-session-handler-extract-storage-10.diff queued for re-testing.
Comment #12
znerol CreditAttribution: znerol commentedNeeds work: I think we need to pass around the
uid
as part of a extendendMetadataBag
instead of as a session attribute.Comment #13
cosmicdreams CreditAttribution: cosmicdreams commented@znerol can you describe what that would buy us?
Comment #14
cosmicdreams CreditAttribution: cosmicdreams commentedHow does SslAwareSessionHandlerInterface impact #961508: Dual http/https session cookies interfere with drupal_valid_token()?
Comment #15
znerol CreditAttribution: znerol commentedRe #14: I think that using
session_id
anywhere outside session handling is a bad idea. I left comment in #961508-272: Dual http/https session cookies interfere with drupal_valid_token().Comment #16
sunI guess it makes sense to postpone this on #2238087: Rebase SessionManager onto Symfony NativeSessionStorage (?)
Comment #17
jibranBack to active again.
Comment #18
cosmicdreams CreditAttribution: cosmicdreams commentedstill green
Comment #19
BerdirRelated: #2345611: Load user entity in Cookie AuthenticationProvider instead of using manual queries
Comment #22
almaudoh CreditAttribution: almaudoh commentedUploading a patch that was derived from #8 and used in #2286971-73: Remove dependency of current_user on request and authentication manager but was afterwards separated again from that issue. The patch is rolled on top of #2286971: Remove dependency of current_user on request and authentication manager, while the do-not-test patch is the actual patch for this issue.
This is just a starting point.
Comment #24
almaudoh CreditAttribution: almaudoh commentedThought I was smart :) #2286971: Remove dependency of current_user on request and authentication manager is in now (yay!!), so the do-not-test.patch becomes the main patch.
Comment #26
almaudoh CreditAttribution: almaudoh commentedSo user last access time update should maybe reside at
AccountProxy::setAccount()
(next to the default timezone set call). Still mulling over this.This doesn't do exactly what the IS says. This patch takes the authentication parts of SessionHandler into the Cookie authentication provider where it should be actually residing.
If testbot is okay, then we can look at:
SessionHandler
Comment #27
star-szrThanks @almaudoh. Just for the record the two patches in #26 are identical, I checked :)
Comment #30
almaudoh CreditAttribution: almaudoh commented#27: Lol :) Fingers too quick. Test fails are suspicious. Retesting....
Comment #33
almaudoh CreditAttribution: almaudoh commentedLooks like the KernelTests that call
AccountProxy::setAccount()
blow up because of the extra dependency onUserEntityStorage
introduced there.So the question now is: where do we put the call to update last accessed time?
Comment #34
BerdirWhy not keep it in Cookie.php?
We've already loaded the session data, the join here shouldn't be necessary anymore?
I'm not sure where we want to do what, but @znerol opened an issue to store the uid directly in the session data, so we know it already here. Maybe we could already do a part of that here, then we can optimize the code here to only do a second query if we really have a user that we can load?
Any idea on how to achieve this? Who exactly destroys session right now?
I guess we need to wrap session_destroy() calls into something that can take care of this? A method on the authentication manager that forwards to the provider? It's not really provider-generic, you can't log out from a basic auth session, the client has to just stop sending the authentication.
Comment #35
almaudoh CreditAttribution: almaudoh commentedThanks for the review @Berdir. I had some time to think about this some more on how to decouple the
SessionHandler
completely fromCookie
andEntityManager
.Cookies are just a way of storing the session id (other implementations also possible), so deleting cookies should not be in
SessionHandler
because that meansSessionHandler
would need to know about all these other implementations as well. How about aSESSION_DESTROYED
event which the Cookie auth and AccountProxy can listen to and react accordingly. Is this a good way? The benefit is that other implementations of session ID storage can use this event to clean up. Would we need a similar SESSION_CREATED event?OTOH, the best place to implement user last access time update would be in a
DestructibleInterface::destruct()
method (implemented maybe inAccountProxy
).Will try this approach tomorrow if there are no better ideas.
Comment #36
almaudoh CreditAttribution: almaudoh commentedI haven't been able to find that issue...
Comment #37
znerol CreditAttribution: znerol commentedThe
hasSession
check is useless in this place. If$session->start()
returnsTRUE
, it means that there is a session cookie on the request. This check was already redundant before, so we should leave it out completely here.However, the original code has an additional check for an empty
$sid
(see the D7 _drupal_session_read(). This is due to SA-CORE-2014-006). That one must be left in place.This is fantastic. Except for the missing
empty($sid)
check mentioned above.This change should not be necessary.
\Drupal::currentUser()
is expected to always return a user. If it is necessary to make tests pass, then that indicates a problem with the patch.This is the logical thing to do and is inline with the way
BasicAuth
works today. In a follow-up we should changeauthenticate()
to only return theuid
and leave the actual loading code and the checks for blocked users to the code triggering the authentication, i.e. toAuthenticationSubscriber
or better a new helper class. This will further simplify the implementation of authentication providers.Let's revisit the use-cases the new code needs to accommodate in order to define responsibilities:
User: authenticated / Authentication provider: Cookie. During the log-out process,
session_destroy
is called. Expected outcome: Session record removed from the database (responsibility: SessionHandler), current user switched to anonymous (responsibility: the log-out code), session cookies removed from the browser (responsibility: SessionManager).User: not-authenticated / Authentication provider: not relevant. An anonymous session is destroyed because it became empty. Expected outcome: Session record removed from the database (responsibility: SessionHandler), current user not changed at all, session cookies removed from the browser (responsibility: SessionManager).
User: authentication status not relevant / Authentication provider: not Cookie. A session is destroyed because it became empty and user authentication is not performed by cookie auth. Expected outcome: Session record removed from the database (responsibility: SessionHandler), current user not changed at all, session cookies removed from the browser (responsibility: SessionManager).
To sum it up, I do not agree with neither one of those
@todo
s.There is a related issue in Symfony [Session] SessionInterface should have a destroy() method. Currently there is no way to simply destroy a session #12375.
We may introduce our own
SessionInterface
and add thedestroy()
method to it (and also toSessionManager
). But this seems to be a bit overkill. Another option is to extract thedeleteCookie
logic into a session handler proxy. That way people who genuinely have a reason to not use cookies might simply swap that out.#2432911: Provide test coverage to prove that a third party authentication provider does not automatically start a session
I just realized that I failed to write down my proposal in that issue. I remember mentioning the idea to @berdir in IRC though.
Comment #38
almaudoh CreditAttribution: almaudoh commentedThanks for the review and feedback @znerol. That clarifies a lot.
But looks like you agree
SessionHandler
should not be deleting cookies, ratherSessionManager
maybe.I also agree a Drupal
SessionInterface
would be too much, especially as we are moving towards using Symfony session components. So while we wait for Symfony to fix the SessionInterface::invalidate() / SessionInterface::destroy() issues, we'll use a work around in theSessionManager
.Thinking this is a great idea too.
Comment #39
almaudoh CreditAttribution: almaudoh commentedStep 1: Moved the user last access time update to
AccountProxy::destruct()
method implementingDestructableInterface
. This removes the Kernel test fails.Not addressed review comments yet.
Comment #40
almaudoh CreditAttribution: almaudoh commentedStep 2: Created a
SessionManagerInterface::destroy()
method wrapper forsession_destroy()
with cookie cleanup in there. This may end up being temporary since there is further refactoring needed inSessionManager
Also addressed review comments from #37:
1. Removed.
2. Great. Added
empty($sid)
check.3. Removed.
Also removed the current user
setAccount()
call since that should be in the calling code.#34.1 is a bit more complex since it requires
$uid
to be set in the session. Will look at this in the next patch.Over to testbot.
Comment #42
andypostNot sure that's right:
1) proxy should not carry this logic.
2) needs EM injected
Suppose once this is not happen in each request so better to move to some UserRequestSubscriber
Comment #43
znerol CreditAttribution: znerol commentedThis is impossible, see the comment elsewhere in the source file.
Comment #44
znerol CreditAttribution: znerol commentedVery much agree.
Comment #45
dawehnerIt would be cool to have a one line comment why we need this tag, ... this would stop people from breaking it in the future.
Honestly, it feels weird to have the database connect in here, directly, what about at least moving the DB queries to methods on this class, so they can be overridden easily?
Comment #46
cpj CreditAttribution: cpj commented#45
2. I've attempted to split out the database calls to class methods, as suggested.
Comment #49
cpj CreditAttribution: cpj commentedOops... Some silly typos in #46. Try again...
Comment #50
cpj CreditAttribution: cpj commentedComment #51
andypostNew patch, addresses #44 & #45
- Added
UserRequestSubscriber
, supposeKernelEvents::FINISH_REQUEST
is a right one- Moved user wrapper creation into protected
getUserFromSession()
Interdiff with #46, that was buggy:
missing $this
@cpj please provide interdiff next time between previous patch
Comment #52
andypostI found no tests for "update last access" for user, so it makes sense to file new issue
Comment #53
almaudoh CreditAttribution: almaudoh commented+1 to the
UserRequestSubscriber
. Nicely decouples things.Comment #54
andypostDiscussed at IRC with @Crell @neclimud this should not block request send.
Also added comment that user should be updated before other core subscribers start to write their caches
Comment #55
almaudoh CreditAttribution: almaudoh commentedTrying to remove the query join to the sessions table is proving difficult. Session variables set in
user_login_finalize()
are not getting carried over into the next request, so I can't set the just logged in$uid
in the session. Still looking at this though. Any ideas?Comment #56
dawehnerIs this about session variables before or after the ->migrate() call?
Comment #57
almaudoh CreditAttribution: almaudoh commentedAfter the ->migrate() call.
Here's the block of new code in
user_login_finalize()
In the Session stack middleware, the
uid
session attribute is still there, but somehow doesn't get serialized with the$request->getSession()->save
call.In digging deeper, I've observed that values in the Symfony session object are stored in the attribute bag which is serialized in the
$_SESSION['_sf2_attributes']
session value which would get serialized to the database.The problem is the
_sf2_attributes
value doesn't get updated in the$_SESSION
superglobal after$session->set('uid', ...)
is called, so when the session is saved, that value is lost.Comment #58
znerol CreditAttribution: znerol commentedOh, that's likely the same as #2437761: CSRF token seed and possibly other session data lost when set after a session regenerate.
Comment #59
almaudoh CreditAttribution: almaudoh commentedQuote from #2437761: CSRF token seed and possibly other session data lost when set after a session regenerate:
@znerol: I came to that same conclusion an hour ago after digging through Symfony's session implementation and how it uses references to sync $_SESSION superglobal with symfony session bags (I wish I had seen that issue a week ago).
NativeSessionStorage::loadSession()
seems to fix it.Comment #60
almaudoh CreditAttribution: almaudoh commentedAttached patch attempts to remove the join with sessions table. But it re-introduces the $_SESSION superglobal (which I'm not happy with) to maintain the expectations that
UserSession::session
should hold a copy of the serialized$_SESSION
contents. I don't know if that property is still necessary.Expect some test fails around SessionHttpsTest and SessionTest. Let's see what else fails.
Comment #62
almaudoh CreditAttribution: almaudoh commentedIn this patch I've removed the
'session'
values as I've seen it is not used anywhere else in core (usages forAccountInterface::getSessionData()
in core is zero).Additionally, a bug was exposed by this patch:
UserSession::getLastAccessedTime()
returns the session save timestamp, whileUser::getLastAccessedTime()
returns the user 'access' timestamp. This would result in inconsistent behavior since we use these two interchangeably to represent the $account variable in various places in core.The only reason why this doesn't show up is because they only go out of sync if the session is accessed twice (or more) within 180 seconds.
A logical follow up would be to clarify the relationship between the User entity and the UserSession value object. #2272189: Uncouple session handling from user module and #2347799: Remove bugged session-related methods from AccountInterface mentioned these already as well as #2345611: Load user entity in Cookie AuthenticationProvider instead of using manual queries.
Comment #63
almaudoh CreditAttribution: almaudoh commentedMarked #2437761: CSRF token seed and possibly other session data lost when set after a session regenerate as duplicate. Hopefully, we can get this reviewed, RTBC'd and committed soon.
Comment #64
znerol CreditAttribution: znerol commentedPlease also remove
AccountInterface::getSessionData()
in addition to removing thesession
attribute. I think that we even should go further and also killgetSessionId()
andgetSecureSessionId()
. Optionally that could be cleaned up later in #2347799: Remove bugged session-related methods from AccountInterface.As pointed out in #2345611-19: Load user entity in Cookie AuthenticationProvider instead of using manual queries,
getLastAccessedTime
is really only useful to order the list of users in the administrative interface. This is not something we need on the currently logged in user because that should obviously be equal toREQUEST_TIME
. Therefore removegetLastAccessedTime
from the interface and add it to the user entity.The whole point on having an
uid
attribute in the session is to be able to eliminate the current user from theSessionHandler
. Just inject the session and call$this->session->get('uid', 0);
here instead.What is the reason for this relocation?
Comment #65
almaudoh CreditAttribution: almaudoh commentedLet's do this in #2347799: Remove bugged session-related methods from AccountInterface
I think we can also do this in #2347799: Remove bugged session-related methods from AccountInterface, or maybe raise a new issue for it.
#64.1: Seems like this introduces a circular dependency:
session_handler.write_safe -> session_handler.write_check -> session_handler.storage -> session -> session_manager -> session_handler.write_safe
, much as I had thought. I will take a closer look at this.#64.2: Logically, I think the log entry should be made after all the necessary operations are completed successfully, not before.
This issue is at least major since it seems to block some other issues
#2432911: Provide test coverage to prove that a third party authentication provider does not automatically start a session
#2468873: Test that the authentication provider doesn't leak authentication credentials from the previous request
#2283637: Provide test coverage to prove that an AuthenticationProvider can initiate a session
Comment #66
znerol CreditAttribution: znerol commentedRight, we should use
$session = $this->requestStack->getCurrentRequest()->getSession()
and then retrieve theuid
from there.Ok. I think if this is not strictly required on this patch, it should probably be separated out.
Comment #67
almaudoh CreditAttribution: almaudoh commentedMy thoughts exactly, also removes one dependency.
Fine.
I will re-open the issue at #2347799: Remove bugged session-related methods from AccountInterface to follow up on comments #64 not already addressed in this patch.
Comment #69
znerol CreditAttribution: znerol commentedI guess the test failures are due to the missing session on the installer request and probably also because the lazy session start is broken in the installer. Let's make sure that a) the session is set on the request whenever a session is started and b) the session is saved (and thus started lazily if necessary) before each
exit
if there was one on the request.I guess something like in the attached interdiff should do it.
Comment #70
almaudoh CreditAttribution: almaudoh commentedThanks for the interdiff @znerol. Updated patch attached. Interdiff is #69.
Comment #72
znerol CreditAttribution: znerol commentedBrowserTestBaseTest
is based on the new mink stuff. I guess that runs in$install_state['interactive'] = FALSE
but still requires the session. Therefore let's drop the new$install_state['interactive']
condition ininstall_bootstrap_full()
.SimpleTestBrowserTest
depends onBrowserTestBaseTest
, that's the reason for its failure.No idea what is causing the test fail inEdit: seems like a random test failure #2417549: Drupal\migrate_drupal\Tests\d6\MigrateFileTest fail in MigrateTestBase]MigrateFileTest
.Comment #73
Dom. CreditAttribution: Dom. commentedSuggesting to remove this form the patch :
and solve it in it's dedicated issue : #2437761: CSRF token seed and possibly other session data lost when set after a session regenerate.
Patch is already quite complicated and we may want to split it in one-by-one issue fix.
Comment #74
znerol CreditAttribution: znerol commentedNo need to remove that line. The important thing is that the other issue brings in a proper test.
Comment #75
Dom. CreditAttribution: Dom. commentedNew patch with #72 applied, still not fulfilling tests though.
Comment #76
almaudoh CreditAttribution: almaudoh commentedRerolled #75 after #2437761: CSRF token seed and possibly other session data lost when set after a session regenerate went in.
Comment #77
xjmSince this resolves or unblocks several bugs, including a critical, let's bump this issue to critical.
Comment #78
xjmComment #79
znerol CreditAttribution: znerol commented@Berdir said in #2468873-16: Test that the authentication provider doesn't leak authentication credentials from the previous request:
Response in #2468873-21: Test that the authentication provider doesn't leak authentication credentials from the previous request:
I'd like to point out that storing the
uid
into session storage has two purposes:Before this patch, both of those use-cases were covered by storing the
uid
exclusively on the appropriate database column in the session table. As a result it was necessary to load the user as part ofSessionHandler::read
. Storing that information as a session attribute allows us to move that code into the cookie authentication provider. Note that theuid
session attribute is intended to be used by the cookie authentication provider exclusively, it is not meant to be used by any other code.It follows that we do not have any possibility to distinguish anonymous sessions from sessions which were used on requests where another authentication provider was in use. Note that in
HEAD
theuid
is populated regardless of whether or not the request was authenticated by the cookie provider and that is then reused on subsequent requests (this bug is addressed by the current patch).@Berdir basically proposes that we should store the
uid
in session storage regardless of the authentication provider in use and in addition also store the provider id in order to fix the bug pointed out above. The cookie provider would then only authenticate the request if both session attributes contain appropriate values. The benefit of this approach is that it would allow us to distinguish sessions which were authenticated by some third-party provider from anonymous ones and clear them out on a per-user basis (use-case 2).Let's take a closer look at use-case 2. The place where
SessionManager::delete()
is called is in User::postSave on two occasions: When the password was changed and when the user was blocked.Dropping sessions of blocked users is in fact not strictly necessary, because authentication providers are currently responsible to check that bit.
The impact of the patch on the removal of sessions when a password is changed is as follows:
HEAD
and @Berdir approach: Any session of a given user is removed regardless of the authentication provider in use.Note that the intention of this mechanism is to prevent that compromised sessions can be used to access a site after the password for an account was changed. This is still the case with the patch. Removing sessions of a user which do not allow an adversary to impersonate that user is not necessary.
Another reason why I do not like the authentication provider id to be stored in the session is that I consider that information private to the AuthenticationManager implementation (see #2456303-9: AuthenticationManager needs interface and ::getProviderKeys).
Instead of clearing the session on a per-user basis (which can be in fact difficult to implement in non-db session backends), it would also be possible to store a hash over relevant attributes (eg. password hash, blocked state) in the session. Upon authentication the provider compares the hash to one derived from the loaded user and fails if it does not match. This would make it possible to get rid of
SessionManager::delete()
and that again would again reduce the amount backend specific code inside that class. But that should be done in a follow-up.Comment #80
znerol CreditAttribution: znerol commentedWhile we wait for a decision on #79, let's fix some minor issues I've found while looking through the patch again:
Nitpick:
$install_state
is unused. The function signature does not need to be changed.I really like how simple this method is now. It could be even easier to read if there was only one return statement. E.g.
Also we do only need the
session
column, therefore it would be enough tofetchField()
instead offetchAssoc()
.This could profit if we'd populate
$request = $this->requestStack->getCurrentRequest()
beforehand and use$request->getSession()
and$request->getClientIP()
. That also would reduce theuid
row to less than 80 characters.Comment #81
pfrenssenI don't understand this part, why would we return a UserSession in which only the 'access' property is populated when the user is anonymous or blocked? This property is unused as far as I can see. It stores the 'last access time', but if the
UserSession::getLastAccessedTime()
does not even use this property, it uses$this->timestamp
instead.The
access
property is not even declared on theUserSession
class.Comment #82
znerol CreditAttribution: znerol commentedRe #81 I agree, see also #64. This is left over code but that is easy to remove at a later stage.
Comment #83
pfrenssenFrom #64:
This does not seem to be correct.
getLastAccessedTime()
is also used inSessionHandler
to prevent session data from being saved more than once per 3 minutes. In this case it is not certain that the user object is a fully populated entity, it might also be a UserSession object. This means that we still needgetLastAccessedTime
onAccountInterface
.Comment #84
BerdirLooks great, review below, looks like it overlaps a bit with what @znerol said, didn't read that before.
I've been wondering for a while now about moving cookie to the user module, not sure if we can still do tha, but it would kind of make sense and help to further decouple the user module from system/core.
As commented above, I hink we can just drop this code here, since it's dead here. Doesn't increase the complexity of the code here, it actually simplifies the patch.
Why are we selecting all fields when we only need the uid?
Can't just select the session column and then do a fetchField? Then we could even simplify it to:
return (string) $query->fetchField(); ?
We also have a DestructableInterface, but that's specificaly for services that if instantiated, want to be destructed, so not a replacement for this.
Anyway, since this is now in user.module, that means we should definitely move cookie (in a different issue) too?
are those comments still accurate? can we maybe move or remove them?
Comment #85
znerol CreditAttribution: znerol commentedRe #84.5: Yes the comments are still accurate. I tend to think that we should not access
session_manager
at all if possible. The canonical way to access and manipulate the session is via$request->getSession()
.Comment #86
almaudoh CreditAttribution: almaudoh commentedThis patch fixes #80.1, .2, .3, #81, and #84.2, .3.
Comment #87
dawehnerwhile reading this method name, I got confused, ... wait we load a user? Maybe name it getAccountFromSession?
Should we open a follow up to discuss that? At least we should certainly try to run before
\Drupal\Core\EventSubscriber\KernelDestructionSubscriber::onKernelTerminate
as we should not re-init the services.I'm curious how / where we document how the entire system works ...
Comment #88
Berdir1. There is no difference between account and user, that's all made up :) The interface is AccountInterface and the class that we load is actually UserSession. And I want to switch to using a user entity there anyway. I'd stick with getUserFromSession().
Comment #89
znerol CreditAttribution: znerol commentedIf $session->get('uid') returns
0
orNULL
, we should not bother querying the database at all.Authenticated users are currently represented by either a
UserSession
or aUser
entity. TheUserSession
uses thetimestamp
property internally while theUser
entity usesaccess
for the same thing. This is why we have to currently copy over theaccessed
loaded from the database to thetimestamp
value in theUserSession
. We can take care of this in #2345611: Load user entity in Cookie AuthenticationProvider instead of using manual queries.Regarding #81 ( user is anonymous or blocked. Only preserve access field from the {users} table.). I think I have an explanation for that weird bit.
In Drupal 7 we used
$user->timestamp
in order to prevent that unchanged sessions are written at every request. Obviously that timestamp needed to be in place even for anonymous users with an open session. However, we replaced that bit with theWriteCheckSessionHandler
proxy. For that reason it is justifiable to dropUserSession
for anonymous/blocked users here (additionally it is broken already as pointed out in #81).So the unnecessary query should be fixed, the rest is fine in my opinion.
Comment #90
znerol CreditAttribution: znerol commentedAlso while testing manually I discovered that we still open new sessions on every request with this patch. This is due to
SessionManager::save()
still checking the current user. Instead it should just rely onisSessionObsolete()
because that will returnFALSE
for authenticated sessions (i.e.uid
in session attributes). I guess something like the attached interdiff will solve that problem.Comment #91
andypost+1 to #84.1
Also I'm sure that we should not use
database::select()
because it needlessly executeshook_query*_alter()
hooks so loads all modules on this early stage.Comment #93
almaudoh CreditAttribution: almaudoh commentedGuess you were trying to say
[':uid' => $uid]
Comment #94
andypostFix typo and added #90, maybe code-comment could be improved
Comment #95
BerdirFirst comment should be improved a bit, maybe just say like something like "Do not execute additional database queries for anonymous users." ?
Similar for the second comment, I'm also not sure we still need the anyonmous there and I think the session is expired is also not relevant here, weren't not checking for that?
So just..
// The user was blocked or deleted.
?
Comment #96
znerol CreditAttribution: znerol commentedRe #95: Those comments would actually be superflous if we'd just stick to self documenting code:
Notes: Do not use multiple
returns
unless it really makes the code more readable. In conditionals, test first for the happy path.It would be great if we could use the identity operator here, not sure if it works though (is thestatus
int or bool?)Edit:@Berdir notes that database results are always string. Therefore typesafe comparison is not possible.
Comment #97
znerol CreditAttribution: znerol commentedOh, and btw.
getUserFromSession()
could simply returnNULL
instead ofAnonymousUserSession
. Additionally, because we do now access the session via Symfony session object, it is not necessary to start it explicitly, because that is taken care of by NativeSessionStorage::getBac() called by Session::get().So we could basically write:
Comment #98
znerol CreditAttribution: znerol commentedComment #99
znerol CreditAttribution: znerol commentedOpened follow-up #2472535: Remove SessionManager::delete in favor of a portable mechanism to invalid sessions of authenticated users.
Comment #100
almaudoh CreditAttribution: almaudoh commentedWhat is left to be done here?
Comment #101
znerol CreditAttribution: znerol commentedBasically #95 and following. I'll try to summarize:
AnonymousUserSession
at all in cookie provider becauseAuthenticationSubscriber
interpretsNULL
as anonymous session. This is in accordance toAuthenticationProviderInterface
authenticate
boils down to:getUserFromSession
can be simplified to:Comment #102
pfrenssenPicking this up. @andypost and @almoudoh are working in different time zones and I'm at the Drupal Dev Days sprint so I can get help from @znerol and @Berdir to get me up to speed on this issue.
Comment #103
almaudoh CreditAttribution: almaudoh commentedThanks @pfrenssen. #101 is a summary of what's left to be done.
Comment #104
pfrenssenI did a first pass through the patch to familiarize myself with the issue. Made a few small improvements and clarified documentation. Will start addressing the remarks starting from #95 now.
Comment #105
pfrenssenSessionHandler::read()
to avoid multiple return statements.Cookie::getUserFromSession()
to clarify that aUserSession
object is returned and not a fullUser
entity. Since they both implementAccountInterface
this might otherwise cause confusion.$values['uid']
since we are already checking that$uid
is not empty before executing the query. If the query returns a result then this value will always be there.Comment #106
znerol CreditAttribution: znerol commentedThank you! I've posted the first two patches in this issue, but they are something completely different than what @almaudoh started almost one year later. So I think it is okay for me to RTBC this.
Comment #107
alexpottNot used.
We should document that this method returns NULL if the user is not active or the session does not have a UID attribute. Plus given that
authenticate
specifically needs to returnNULL
to indicate that the authentication failed I think we should have an explicitreturn NULL;
here.Comment #108
alexpottBTW - this patch is looking great :) - should have mentioned that in #107
Comment #109
pfrenssenNew patch coming up!
Comment #110
pfrenssenComment #111
znerol CreditAttribution: znerol commentedThanks.
Comment #112
almaudoh CreditAttribution: almaudoh commentedRTBC+1
Comment #113
webchickBack to alexpott.
Comment #114
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 0fa529e and pushed to 8.0.x. Thanks!