vaadin / collaboration-engine

The simplest way to build real-time collaboration into web apps
https://vaadin.com/collaboration
Other
3 stars 1 forks source link

CollaborationKit interferes with login preservation in development mode #75

Open Legioth opened 1 year ago

Legioth commented 1 year ago

Describe the bug Flow has a feature that by default disables serialization of some parts of VaadinSession in development mode. This is to allow serializing the rest of the HTTP session so that authentication data managed by e.g. Spring Security can preserved over redeploys even if the Flow session cannot be serialized.

The problem is that this Collaboration Kit stores an unserializable ServiceDestroyDelegate instance as an attribute in VaadinSession and the functionality to disable session serialization does not consider session attributes. This leads to a situation where serialization fails which in turn also prevents the authentication data from being preserved.

To Reproduce Steps to reproduce the behavior:

  1. Create an application at start.vaadin.com that includes the Collaborative Master-Detail view and has access control enabled
  2. Run the application, log in, navigate to the collaborative view, and select an item in the grid
  3. Make a code change that triggers a redeploy
  4. Observe that you're no longer logged in after the redeploy.

Expected behavior Expected to remain logged in after the redeploy. This can be observed in the same example application if you only visit views that don't use Collaboration Engine before doing the code change since there will then not be any ServiceDestroyDelegate in the session.

Versions

Additional context This problem should be resolved by the ongoing work to make all of Collaboration Kit serializable (https://github.com/vaadin/collaboration-engine/issues/51). I'm reporting this ticket mostly to have a place to document a workaround in case someone else encounters this problem and they're not using a version with fixed serialization.

My workaround adds a session passivation listener that removes the ServiceDestroyDelegate instance from the session's attributes. This might lead to leaking memory so it's only active in development mode when session serialization is otherwise disabled.

private static final class SessionCleanupListener
        implements HttpSessionActivationListener, Serializable {
    private final VaadinSession session;
    private final Class<?> clz;

    public SessionCleanupListener(VaadinSession session) {
        this.session = session;

        try {
            // Cannot directly access package-private class
            clz = Class.forName(
                    "com.vaadin.collaborationengine.ServiceDestroyDelegate");
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException(e1);
        }

    }

    public void sessionWillPassivate(HttpSessionEvent se) {
        session.accessSynchronously(() -> session.setAttribute(clz, null));
    };
}

@Bean
VaadinServiceInitListener serviceInitListener() {
    return serviceEvent -> {
        VaadinService service = serviceEvent.getSource();
        ApplicationConfiguration config = ApplicationConfiguration
                .get(service.getContext());
        if (config.isProductionMode()
                || config.isDevModeSessionSerializationEnabled()) {
            return;
        }
        service.addSessionInitListener(sessionEvent -> {
            VaadinSession session = sessionEvent.getSession();
            session.getSession().setAttribute("hack",
                    new SessionCleanupListener(session));
        });
    };
}