Description
The issue is 100% reproducible (latest develop branch) when using PULSE in embedded mode and the default configurations, the integrated security feature must not be enabled.
Steps to reproduce:
1. Start locator: gfsh start locator --name=locator1. 2. Open Pulse: gfsh start pulse. 3. Login into pulse application. 4. Click on the logout button.
At this stage, the following exception will be shown:
HTTP ERROR 500 Problem accessing /pulse/clusterLogout. Reason: Server Error Caused by: java.lang.ClassCastException: org.springframework.security.authentication.UsernamePasswordAuthenticationToken cannot be cast to org.apache.geode.tools.pulse.internal.security.GemFireAuthentication at org.apache.geode.tools.pulse.internal.security.LogoutHandler.onLogoutSuccess(LogoutHandler.java:43) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1668) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1180) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134) at org.eclipse.jetty.server.Server.handle(Server.java:524) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:319) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95) at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93) at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303) at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148) at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671) at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589) at java.lang.Thread.run(Thread.java:745)
The problem is within the LogoutHandler class, it's always trying to get an instance of GemFireAuthentication through downcasting, but the Authentication object is an instance of GemFireAuthentication only when the Integrated Security feature is used. This means that the LogoutHandler will only be successful when the profile pulse.authentication.gemfire is active and the GemFireAuthenticationProvider is in charge. In the default case scenario, on the other hand, the Authentication object is populated by the default classes from spring-security and, thus, the exception is thrown.
The fix should be quick and without major impact, anyway: the filter actually doesn't need to downcast to GemFireAuthentication since there's nothing extra on that object that needs to be used by the handler, it just needs to use the instance of Authentication as follows:
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.debug("Invoked #LogoutHandler ..."); if (authentication != null) { Repository.get().logoutUser(authentication.getName()); logger.info("#LogoutHandler : Closing GemFireAuthentication JMX Connection..."); } super.onLogoutSuccess(request, response, authentication); }
Attachments
Issue Links
- links to