Problem/Motivation
The request listener of the maintenance mode subscriber of the user module currently runs before routing and therefore relies on the _system_path
request attribute. However, the listener is not only acting on maintenance mode, but is additionally responsible for redirecting authenticated users hitting user/login
and user/register
to user/[uid]
and user/[uid]/edit
respectively. Currently the redirection needs to happen before routing/access-check, otherwise users will end up on a 403 page.
It is possible to solve this dilemma by splitting out the redirection to an exception subscriber which acts on AccessDeniedHttpException
. As a result the priority of the maintenance mode subscriber can be lowered such that it fires after routing.
Proposed resolution
- Change priority of
\Drupal\user\EventSubscriber\MaintenanceModeSubscriber::onKernelRequestMaintenance()
- Perform redirection for authenticated users on the
user.login
anduser.register
route in a newly introducedKernelEvents::EXCEPTION
- Modify the
\Drupal\user\Access\LoginStatusCheck
such that it actually respects the boolean argument to the_user_is_logged_in
route requirement. - Deny access to authenticated users on
user.login
such that it runs after routing
Remaining tasks
User interface changes
API changes
Comment | File | Size | Author |
---|---|---|---|
#63 | 2288911-use-route-instead-of-system-path-in-user-63.diff | 15.48 KB | znerol |
#63 | interdiff.txt | 503 bytes | znerol |
#62 | 2288911-use-route-instead-of-system-path-in-user-62.diff | 15.2 KB | znerol |
#62 | interdiff.txt | 4.42 KB | znerol |
#58 | interdiff.txt | 3.06 KB | effulgentsia |
Comments
Comment #1
znerol CreditAttribution: znerol commentedComment #2
znerol CreditAttribution: znerol commentedComment #4
znerol CreditAttribution: znerol commentedInteresting test failures. Apparently this change results in the authentication being triggered after routing now. Therefore we need to cherry-pick one of the changes from #2286971: Remove dependency of current_user on request and authentication manager and ensure that it behaves like the
AuthenticationEnhancer
(only allow default authentication provider if_auth
route option was set).Comment #5
sunShouldn't this redirect to the full /user/123 entity URI/path instead of the /user alias?
Let's replace these early-returns with
elseif
Don't we have a better naming pattern for such services? E.g.,
user.routing.exception_subscriber
?
Also, the aspect of "redirect" is rather meaningless in the service ID + class name... Should reference the context of "account" instead.
Comment #6
znerol CreditAttribution: znerol commentedThanks for the review, will address 1...3 on my way home. Two questions:
UrlGenerator::generate()
(Symfony) andUrlGenerator::generateFromRoute()
(Drupal) which one is preferred?RedirectSubscriber
). We do not really handle exceptions here, but sort of gracefully handle a 403. I feel that the fact that it is necessary to catch anAccessDeniedException
in order to handle the 403 gracefully is an implementation detail. How aboutRedirectOn403Subscriber
orAccessDenialSubscriber
?Comment #7
znerol CreditAttribution: znerol commentedAddresses #5 1...3, also uses the new
RouteMatch
class.Comment #8
znerol CreditAttribution: znerol commentedRename
ExceptionRedirectSubscriber
toAccessDeniedSubscriber
.Comment #9
effulgentsia CreditAttribution: effulgentsia commentedThis patch looks great. Just some nits:
Even after reading this comment, I don't get why this is necessary. What's the flow that this patch changes that results in AuthenticationEnhancer not running or not running at the right time?
Missing @param.
The doc is an improvement, but still doesn't capture everything the function does.
Could use RouteMatch here?
Comment #10
znerol CreditAttribution: znerol commentedThanks for the review @effulgentsia.
AuthenticationManager::handleException()
also).Perhaps we could merge this patch into #2288665: Remove _maintenance request attribute and replace it with a maintenance mode service, they are overlapping quite a bit in the user maintenance mode subscriber. Opinions?
Comment #11
znerol CreditAttribution: znerol commentedComment #13
znerol CreditAttribution: znerol commentedComment #15
znerol CreditAttribution: znerol commentedSeveral tests expect that the login-form is at
/user
instead of/user/login
. We should fix this in another issue.Comment #16
tim.plunkettThis change doesn't seem like it does anything, it just means an extra call to ->id(), why?
This was a very specific behavior in D7 that we were replicating
Whaaaat. Why are you using url() with a path here?
Comment #17
znerol CreditAttribution: znerol commented@tim.plunkett: The interdiff in #15 does not show any code which is newly introduced by this patch. It is merely a partial rollback of #10 because fixing the
user
touser/login
redirect involves changing unrelated tests which is definitely not in the scope of this issue.Comment #18
znerol CreditAttribution: znerol commentedFiled #2294571: Redirect anonymous users to login page from an exception listener instead of in MaintenanceModeSubscriber and restrict access to the my-account link to authenticated users.
Comment #19
tim.plunkettLOL. I have flashbacks to writing that user login code, and I tried about 5 ways before coming up with whatever is in there, so I was nervous we were changing it. I didn't realize it was a revert :D
Comment #20
xjmComment #21
znerol CreditAttribution: znerol commentedReroll after #2239009: Remove public direct usage of the '_system_path' request attribute.
Comment #22
dawehnerCan we make this typehint absolute? 1354 will tell you about that.
Interesting but let's be honest == 'TRUE' is way easier to read, isn't it?
Let's fix the docs.
Let's use the generator as written in the todo
It is great that we can remove that
Comment #25
znerol CreditAttribution: znerol commentedReroll of #21, review in #22 not yet addressed.
Comment #27
znerol CreditAttribution: znerol commentedTest failures from #25 reveal a rather interesting problem. The following hunk is responsible that users are not authenticated unconditionally on every request:
Removal of the call to
AccountProxy::isAuthenticated()
leads to some requests (e.g. Ajax) not being authenticated at all. Because of the tight coupling of the authentication manager and session manager, this means that the session does not get initialized. However the CSRF token stores the token seed in the session and therefore it needs to be initialized beforehand.Regrettably it is not possible to inject the session manager service into the form builder, because that would break the installer.
Note to self: review in #22 still not addressed.
Comment #29
znerol CreditAttribution: znerol commentedWe need to mock an introspectable container for the form builder test now.
Comment #31
znerol CreditAttribution: znerol commentedRestore use statement in form builder test...
Comment #32
Crell CreditAttribution: Crell commentedI don't get why this is needed. If we have to pollute the FormBuilder with a Drupal:: call it should be documented with why.
I like this class.
Nitpick: If we're already making a utility method for url() here, the only thing that varies at all between these two is the route name. May as well move the entire RedirectResponse creation to the method, too. (Not a commit blocker at all, just some tidying that can be done.)
Comment #35
znerol CreditAttribution: znerol commentedThe introduction of #2328777: Refactor FAPI getCache()/setCache() into a standalone class made it possible to remove the
\Drupal::getContainer()->get('session_manager')->startLazy();
in the form builder. Instead we just can swap out the form-cache instance in the installer.Comment #37
znerol CreditAttribution: znerol commentedRerolled after the stackphp patch, let's see how this fails today.
Comment #39
znerol CreditAttribution: znerol commentedReroll after #2330363: Enhance the controller resolver to get a route match class.
Comment #41
znerol CreditAttribution: znerol commentedReroll.
Comment #42
znerol CreditAttribution: znerol commentedTest failures are still due to the reasons explained in #27. That will be resolved by #2229145: Register symfony session components in the DIC and inject the session service into the request object, which is currently blocked on #2230121: Remove exit() from FormBuilder.
If we do not want to wait for the other issues, it would be possible to fix this by triggering authentication just before access is checked:
Comment #44
Crell CreditAttribution: Crell commentedI hate the idea of such a call, but I hate blocking this on 2 other non-trivial issues more. :-) If we add such a force-login then we would need it documented with a @todo.
I would normally insist on injecting the current_user service, but for a temp hack I may be persuaded that it's better to leave it. IFF we are certain the hack will be removed before 8.0.0.
Comment #45
znerol CreditAttribution: znerol commentedReroll, added the hack proposed in #42.
Comment #47
znerol CreditAttribution: znerol commentedReroll.
Comment #49
znerol CreditAttribution: znerol commentedReroll after #2294571: Redirect anonymous users to login page from an exception listener instead of in MaintenanceModeSubscriber and restrict access to the my-account link to authenticated users.
Comment #51
znerol CreditAttribution: znerol commentedFix reroll.
Comment #52
znerol CreditAttribution: znerol commentedFix route names.
Comment #54
znerol CreditAttribution: znerol commentedRollback changes to
FormCache
, I figure those might not be necessary anymore with the workaround introduced in #45.Comment #57
znerol CreditAttribution: znerol commentedFiled #2364643: Add a minimal default frontpage.
Comment #58
effulgentsia CreditAttribution: effulgentsia commentedThis should fix the remaining test failures without #57.
Comment #59
effulgentsia CreditAttribution: effulgentsia commentedFYI: this is needed because of #2371863: SiteInformationForm validates access to paths that aren't changed, potentially making the form unsubmittable.
Comment #60
catchWould help resolve #2372507: Remove _system_path from $request->attributes and I think at least the blocking session refactoring issues should be critical.
Comment #61
dawehnerDid you meant === TRUE? Otherwise you can just skip == TRUE entirely.
Just curious, do we really need an absolut redirect?
+1 to not longer run before routing!
+1 to not hardcode that into the form Note: At least RedirectResponse can now be removed from the use statements, Url is also not used anymore
Comment #62
znerol CreditAttribution: znerol commentedThanks.
Also fixed the documentation in user maintenance mode subscriber and removed a stray
return
from the core maintenance mode subscriber.Comment #63
znerol CreditAttribution: znerol commentedAlso remove spurious use statements.
Comment #64
dawehnerAlright!
Ha, I was shy to mention this :)
Comment #65
alexpottThis issue is a critical task and is allowed per https://www.drupal.org/core/beta-changes. Committed 5ddf7c2 and pushed to 8.0.x. Thanks!
Fixed on commit.
$route_name
is not used incore/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
.