Skip to content

Commit

Permalink
[GEOS-11235] preauthentication filters - session reuse even after hav…
Browse files Browse the repository at this point in the history
…ing logout

.. or if no preauth headers are provided anymore.

See https://osgeo-org.atlassian.net/browse/GEOS-11235 for more details.

Without this change, we can end up with a persisting session across
logout.

Tests: adding utests specific to this change. Runtime tested into
a docker composition.
  • Loading branch information
pmauduit committed Jan 8, 2024
1 parent 56d8105 commit 6d4f7f3
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
package org.geoserver.security.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.geoserver.security.config.RequestHeaderAuthenticationFilterConfig;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

/**
* J2EE Authentication Filter
* Request header Authentication Filter
*
* @author mcr
*/
Expand All @@ -38,6 +45,22 @@ public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOExc
setPrincipalHeaderAttribute(authConfig.getPrincipalHeaderAttribute());
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String principalName = getPreAuthenticatedPrincipalName((HttpServletRequest) request);
Authentication preAuth = SecurityContextHolder.getContext().getAuthentication();
// If a pre-auth token exists but the request has no principal name anymore
// or differs from the one being sent in the headers, clear the
// security context, or else the user will remain authenticated.
if (preAuth instanceof PreAuthenticatedAuthenticationToken
&& ((null == principalName)
|| (!principalName.equals(preAuth.getPrincipal().toString())))) {
SecurityContextHolder.clearContext();
}
super.doFilter(request, response, chain);
}

@Override
protected String getPreAuthenticatedPrincipalName(HttpServletRequest request) {
return request.getHeader(getPrincipalHeaderAttribute());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* (c) 2023 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.security.filter;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.io.File;
import javax.servlet.http.HttpServletResponse;
import org.geoserver.config.GeoServerDataDirectory;
import org.geoserver.security.GeoServerSecurityManager;
import org.geoserver.security.config.PreAuthenticatedUserNameFilterConfig;
import org.junit.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

public class GeoServerRequestHeaderAuthenticationFilterTest {

@Test
public void testAuthenticationViaPreAuthChanging() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
HttpServletResponse response = new MockHttpServletResponse();
MockFilterChain filterChain = new MockFilterChain();
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(new PreAuthenticatedAuthenticationToken("testadmin", null));
SecurityContextHolder.setContext(sc);
GeoServerRequestHeaderAuthenticationFilter toTest =
new GeoServerRequestHeaderAuthenticationFilter();
toTest.setPrincipalHeaderAttribute("sec-username");
request.addHeader("sec-username", "testuser");
toTest.setSecurityManager(
new GeoServerSecurityManager(new GeoServerDataDirectory(new File("/tmp"))));
toTest.setRoleSource(
PreAuthenticatedUserNameFilterConfig.PreAuthenticatedUserNameRoleSource.Header);

toTest.doFilter(request, response, filterChain);

assertEquals(
"testuser",
SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
}

@Test
public void testAuthenticationViaPreAuthNoHeader() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
HttpServletResponse response = new MockHttpServletResponse();
MockFilterChain filterChain = new MockFilterChain();
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(new PreAuthenticatedAuthenticationToken("testadmin", null));
SecurityContextHolder.setContext(sc);
GeoServerRequestHeaderAuthenticationFilter toTest =
new GeoServerRequestHeaderAuthenticationFilter();
toTest.setPrincipalHeaderAttribute("sec-username");
toTest.setSecurityManager(
new GeoServerSecurityManager(new GeoServerDataDirectory(new File("/tmp"))));
toTest.setRoleSource(
PreAuthenticatedUserNameFilterConfig.PreAuthenticatedUserNameRoleSource.Header);

toTest.doFilter(request, response, filterChain);

// The security context should have been cleared
assertNull(SecurityContextHolder.getContext().getAuthentication());
}
}

0 comments on commit 6d4f7f3

Please sign in to comment.