Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XWIKI-22430: Logging out does not unlock pages that were being edited #3380

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

surli
Copy link
Member

@surli surli commented Aug 29, 2024

Jira URL

https://jira.xwiki.org/browse/XWIKI-22430

Changes

Description

  • Provide a new UserUnauthenticatedEvent to detect whenever a user logged out, the same way that we have UserAuthenticatedEvent
  • Trigger that new event when processing a logout in MyFormAuthenticator
  • Refactor the code in XWikiHibernateStore to listen for UserUnauthenticatedEvent for cleaning the locks of a user, instead of listening to an action event
  • Rename UserAuthenticatedEventNotifier and use it for both authenticated and unauthenticated events
  • Fix test

Clarifications

Screenshots & Video

N/A

Executed Tests

mvn clean install -Pquality on:

  • xwiki-platform-security-authentication-api
  • xwiki-platform-oldcore
  • xwiki-platform-legacy-oldcore

Expected merging strategy

  • Prefers squash: Yes
  • Backport on branches:
    • None: the changes might impact perf and provides new API, better fix it only in stable

@surli surli requested a review from tmortagne August 29, 2024 16:36
for (String wikiId : this.wikiDescriptorManager.getAllIds()) {
if (!currentWiki.equals(wikiId)) {
context.setWikiReference(new WikiReference(wikiId));
this.releaseAllLocksForUser(userDoc, context);
Copy link
Member

@tmortagne tmortagne Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this fail, the context wiki will be broken. Would be better to restore the context wiki only once in a try/finally around the whole for (there is no need to restore it after each releaseAllLocksForUser anyway).

this.releaseAllLocksForCurrentUser(ctx);
} finally {
ctx.setWikiId(cdb);
String currentWiki = context.getWikiId();
Copy link
Member

@tmortagne tmortagne Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably make more sense to use getWikiReference here, it avoids creating a new WikiReference just to restore it.

String currentWiki = context.getWikiId();
for (String wikiId : this.wikiDescriptorManager.getAllIds()) {
if (!currentWiki.equals(wikiId)) {
context.setWikiReference(new WikiReference(wikiId));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels simpler to use setWikiId here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought setWikiId was deprecated. Will check.

@@ -292,7 +292,8 @@ public boolean processLogout(SecurityRequestWrapper securityRequestWrapper,
HttpServletResponse httpServletResponse, URLPatternMatcher urlPatternMatcher) throws Exception
{
boolean result = super.processLogout(securityRequestWrapper, httpServletResponse, urlPatternMatcher);
if (result == true) {
if (result) {
this.getUserAuthenticatedEventNotifier().notifyUserUnauthenticated(securityRequestWrapper.getRemoteUser());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this should probably be called after the forgetLogin.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment about that one: I thought the same at first but then what I'm doing doesn't work anymore because forgetLogin reset the wrapped request user to null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgetLogin reset the wrapped request user to null.

Yes, that's why the event should be sent after. You just need to remember the user reference before it's reseted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

} finally {
ctx.setWikiId(cdb);
String currentWiki = context.getWikiId();
for (String wikiId : this.wikiDescriptorManager.getAllIds()) {
Copy link
Member

@tmortagne tmortagne Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels too big a change for a LTS (I'm afraid logout on myxwiki.org is going to be quite long now, for example).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I wasn't really sure about this loop: it feels a bit overkill but on the other side that's the only way to guarantee that a user properly has release all locks to my knowledge.

Copy link
Member

@tmortagne tmortagne Aug 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not overkill, the user could have lock anywhere in the farm (there are many use cases where you have only main wiki users and various subwikis, xwiki.org for example). It's just very expensive in a farm like myxwiki.org (I guess we might want to move the locks to Solr eventually, or just stop with the locks since that's another thing we talked about).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so it will impact perf, you think it's enough to prevent backporting?

I guess we might want to move the locks to Solr eventually

Interesting idea.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so it will impact perf, you think it's enough to prevent backporting?

I updated the description to not backport.

* @since 15.10.13
*/
@Unstable
public class UserUnauthenticatedEvent implements Event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe UserUnauthenticatedEvent and UserAuthenticatedEvent should share a common abstract class since their content is mostly the same.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I hesitated about doing that but was lazy :)

@Override
public boolean matches(Object other)
{
return other instanceof UserUnauthenticatedEvent;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would worth improving this to take into account this.userReference as otherwise it's quite missleading to have two different constructors from the point of view of a listener.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I'm surprised that the code for UserAuthenticatedEvent doesn't use it, since the javadoc says otherwise, see: https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-api/src/main/java/org/xwiki/security/authentication/UserAuthenticatedEvent.java#L39

I'd preferably have same behaviour for both events, but changing this implem in UserAuthenticatedEvent is a breaking change behaviour...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd preferably have same behaviour for both events, but changing this implem in UserAuthenticatedEvent is a breaking change behaviour...

IMO it's fixing a bug and that's typically something that should go in the abstract.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it's fixing a bug

Would need to check usage first but I tend to agree that we should use it.

that's typically something that should go in the abstract.

+1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't find any usage in xwiki-contrib, except to trigger the event: https://github.com/search?q=org%3Axwiki-contrib%20UserAuthenticatedEvent&type=code

if (userUnauthenticatedEvent.getUserReference() != null) {
DocumentReference userDoc = XWikiHibernateStore.this.userReferenceSerializer.serialize(
userUnauthenticatedEvent.getUserReference());
releaseAllLocksForUser(userDoc, (XWikiContext) data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this release all locks? Shouldn't this only release locks of the current session of the user? I mean when I log out on my phone, this shouldn't release locks that I'm holding on my desktop browser, should it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really have the concept of session right now in the locks, but yes ideally they should.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be an improvment to add in the future I think, the expectation of current mechanism is that it does release all locks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be done at the same time as moving the locks to solr for better perf as Thomas suggested.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing a reference to all active locks of the user in the session would solve both the session and the performance problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing a reference to all active locks of the user in the session would solve both the session and the performance problem.

It would probably be enough for the performance problem, yes (there is very little chance that the user has locks on many wikis at the same time in practice).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd do that as part of another improvment ticket: IMO it's out of the scope of current PR.

Copy link
Member

@tmortagne tmortagne Aug 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's kind of the same scope as the for you added, the session references was another way to fix this problem (but yes, it's more complex than adding a for :)). It would also make the new event useless for this need, as the trigger would be more the session invalidation (which is also triggered by the logout) and not the logout (as a bonus this would work with all authenticators right away).

@surli surli marked this pull request as draft August 30, 2024 08:43
  * Provide a new UserUnauthenticatedEvent to detect whenever a user
    logged out, the same way that we have UserAuthenticatedEvent
  * Trigger that new event when processing a logout in
    MyFormAuthenticator
  * Refactor the code in XWikiHibernateStore to listen for
    UserUnauthenticatedEvent for cleaning the locks of a user, instead
of listening to an action event
  * Rename UserAuthenticatedEventNotifier and use it for both
    authenticated and unauthenticated events
  * Fix test
  * Provide an abstract event for authentication
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants