Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ protected static void setUp() throws Exception {
refresh = true;
}
RequestContext reqCtx = BaseTestCase.createRequestContext(applicationContext, servletContext);
request = BaseTestCase.createRequest();
request = BaseTestCase.createRequest(servletContext);
reqCtx.setRequest(request);
request.setAttribute(RequestContext.REQCTX, reqCtx);
response = new MockHttpServletResponse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static Optional<String> extractCurrentTenantCode(HttpServletRequest reque
} else {
logger.debug("the custom header:'{}' is empty or blank, skip it", UrlUtils.ENTANDO_TENANT_CODE_CUSTOM_HEADER);
String domain = getDomainFromRequest(request);
ITenantManager tenantManager = ApsWebApplicationUtils.getBean(ITenantManager.class, request);
ITenantManager tenantManager = ApsWebApplicationUtils.getBean(ITenantManager.class, request.getServletContext());
String tenantCodeFromDomain = tenantManager.getTenantCodeByDomain(domain);
logger.debug("the tenantCodeFromDomain is:'{}', return it", tenantCodeFromDomain);
return Optional.ofNullable(tenantCodeFromDomain);
Expand Down
8 changes: 6 additions & 2 deletions engine/src/test/java/com/agiletec/aps/BaseTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static void setUp(ConfigTestUtils configTestUtils) throws Exception {
refresh = true;
}
reqCtx = createRequestContext(applicationContext, servletContext);
request = createRequest();
request = createRequest(servletContext);
request.setAttribute(RequestContext.REQCTX, reqCtx);
request.setSession(new MockHttpSession(servletContext));
reqCtx.setRequest(request);
Expand All @@ -96,7 +96,11 @@ public static RequestContext createRequestContext(ApplicationContext application
}

public static MockHttpServletRequest createRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
return createRequest(null);
}

public static MockHttpServletRequest createRequest(ServletContext servletContext) {
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
request.setScheme("http");
request.setServerName("www.entando.com");
request.addHeader("Host", "www.entando.com");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package com.agiletec.aps.util;

import java.util.Optional;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;

import org.assertj.core.api.Assertions;
Expand All @@ -24,6 +25,9 @@
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.WebApplicationContext;

@ExtendWith(MockitoExtension.class)
class ApsTenantApplicationUtilsTest {
Expand All @@ -37,7 +41,7 @@ class ApsTenantApplicationUtilsTest {
void shouldExtractCorrectTenantDomainWithEveryInput(){

try(MockedStatic<ApsWebApplicationUtils> apsWebApplicationUtils = Mockito.mockStatic(ApsWebApplicationUtils.class)){
apsWebApplicationUtils.when(() -> ApsWebApplicationUtils.getBean(ITenantManager.class,httpServletRequest)).thenReturn(tenantManager);
apsWebApplicationUtils.when(() -> ApsWebApplicationUtils.getBean(Mockito.eq(ITenantManager.class), Mockito.<ServletContext>any())).thenReturn(tenantManager);

Mockito.when(httpServletRequest.getHeader("X-ENTANDO-TENANTCODE")).thenReturn("tenant2");
Mockito.when(httpServletRequest.getServerName()).thenReturn("www.test.com");
Expand Down Expand Up @@ -72,6 +76,22 @@ void shouldExtractCorrectTenantDomainWithEveryInput(){
}
}

@Test
void shouldNotCreateSessionWhenExtractingTenantFromDomain() {
MockServletContext servletContext = new MockServletContext();
WebApplicationContext wac = Mockito.mock(WebApplicationContext.class);
Mockito.when(wac.getBean(ITenantManager.class)).thenReturn(tenantManager);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
request.setServerName("www.test.com");
Mockito.when(tenantManager.getTenantCodeByDomain("www.test.com")).thenReturn("wwwCode");

Optional<String> res = ApsTenantApplicationUtils.extractCurrentTenantCode(request);

Assertions.assertThat(res).contains("wwwCode");
Assertions.assertThat(request.getSession(false)).isNull();
}

@Test
void shouldGetSetRemoveWorkCorrectTenantCodeWithEveryInput() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.entando.entando.aps.system.services.tenants.ITenantManager;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.MDC;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.WebApplicationContext;

@ExtendWith(MockitoExtension.class)
class MDCTenantFilterTest {
Expand Down Expand Up @@ -54,6 +59,28 @@ void shouldSetPrimaryTenantToMDC() throws Exception {
}
}

@Test
void shouldNotCreateSessionWhenResolvingTenantFromDomain() throws Exception {
ITenantManager tenantManager = Mockito.mock(ITenantManager.class);
Mockito.when(tenantManager.getTenantCodeByDomain("tenant1.test.com")).thenReturn("tenant1");
WebApplicationContext wac = Mockito.mock(WebApplicationContext.class);
Mockito.when(wac.getBean(ITenantManager.class)).thenReturn(tenantManager);
MockServletContext servletContext = new MockServletContext();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
MockHttpServletRequest mockRequest = new MockHttpServletRequest(servletContext);
mockRequest.setServerName("tenant1.test.com");
String[] tenantInChain = new String[1];
Mockito.doAnswer(invocation -> {
tenantInChain[0] = MDC.get("tenant");
return null;
}).when(chain).doFilter(Mockito.any(), Mockito.any());

filter.doFilter(mockRequest, new MockHttpServletResponse(), chain);

Assertions.assertEquals("tenant1", tenantInChain[0]);
Assertions.assertNull(mockRequest.getSession(false));
}

@Test
void shouldRemoveMDCKeyInCaseOfException() throws Exception {
try (MockedStatic<MDC> mdc = Mockito.mockStatic(MDC.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
Expand Down Expand Up @@ -47,6 +48,21 @@
this.authenticationProviderManager = authenticationProviderManager;
}

@Bean
@Order(60)
public SecurityFilterChain healthSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(new AntPathRequestMatcher("/api/health"))

Check failure on line 54 in keycloak-plugin/src/main/java/org/entando/entando/aps/servlet/security/KeycloakSecurityConfig.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "/api/health" 3 times.

See more on https://sonarcloud.io/project/issues?id=entando_app-engine&issues=AaFFEXdw_7yBdDVDuExL&open=AaFFEXdw_7yBdDVDuExL&pullRequest=379
.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.requestCache(RequestCacheConfigurer::disable)
.headers(headers -> headers.frameOptions(iframeDisabled
? HeadersConfigurer.FrameOptionsConfig::disable
: HeadersConfigurer.FrameOptionsConfig::sameOrigin))
.anonymous(AbstractHttpConfigurer::disable)
.cors(cors -> cors.configurationSource(corsConfigurationSource()));
return http.build();
}

@Bean
@Order(70)
public SecurityFilterChain keycloakSecurityFilterChain(HttpSecurity http) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ void setUp() {
Mockito.lenient().when(request.getHeader(HttpHeaders.HOST)).thenReturn("localhost");
Mockito.lenient().when(request.getServerName()).thenReturn("localhost");
Mockito.lenient().when(session.getServletContext()).thenReturn(servletContext);
Mockito.lenient().when(request.getServletContext()).thenReturn(servletContext);
Mockito.lenient().when(webApplicationContext.getBean(ITenantManager.class)).thenReturn(tenantManager);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -88,6 +89,7 @@ public void setUp() {
Mockito.lenient().when(request.getHeader(HttpHeaders.HOST)).thenReturn("dev.entando.org");
Mockito.lenient().when(request.getServerName()).thenReturn("dev.entando.org");
Mockito.lenient().when(session.getServletContext()).thenReturn(svCtx);
Mockito.lenient().when(request.getServletContext()).thenReturn(svCtx);
Mockito.lenient().when(wac.getBean(ITenantManager.class)).thenReturn(tenantManager);
}

Expand Down Expand Up @@ -151,7 +153,7 @@ void apiAuthenticationShouldSetAttributeRequest() {
wacUtil.when(() -> WebApplicationContextUtils.getWebApplicationContext(svCtx)).thenReturn(wac);
keycloakAuthenticationFilter.attemptAuthentication(request, response);
verify(request).setAttribute(eq("user"), any());
verify(request, times(1)).getSession();
verify(request, never()).getSession();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,28 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.entando.entando.aps.system.services.tenants.ITenantManager;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -214,4 +228,93 @@ void keycloakEnabled_shouldConfigureHttpSecurityCorrectly() throws Exception {
verify(httpSecurity).build();
}

@Test
void healthProbeShouldNotCreateSession() throws Exception {
try (AnnotationConfigWebApplicationContext context = securityChainsContext()) {
MockHttpServletRequest request = apiRequest(context, "/health");
MockFilterChain chain = new MockFilterChain();

context.getBean("springSecurityFilterChain", Filter.class).doFilter(request, new MockHttpServletResponse(), chain);

assertNotNull(chain.getRequest());
assertNull(request.getSession(false));
}
}

@Test
void healthPostWithoutCsrfTokenShouldBeRejected() throws Exception {
try (AnnotationConfigWebApplicationContext context = securityChainsContext()) {
MockHttpServletRequest request = apiRequest(context, "/health");
request.setMethod("POST");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain();

context.getBean("springSecurityFilterChain", Filter.class).doFilter(request, response, chain);

assertNull(chain.getRequest());
assertEquals(403, response.getStatus());
}
}

@Test
void otherApiPathShouldStillCreateSession() throws Exception {
try (AnnotationConfigWebApplicationContext context = securityChainsContext()) {
MockHttpServletRequest request = apiRequest(context, "/other");
MockFilterChain chain = new MockFilterChain();

context.getBean("springSecurityFilterChain", Filter.class).doFilter(request, new MockHttpServletResponse(), chain);

assertNotNull(chain.getRequest());
assertNotNull(request.getSession(false));
}
}

private static AnnotationConfigWebApplicationContext securityChainsContext() {
MockServletContext servletContext = new MockServletContext();
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(servletContext);
context.register(SecurityChainsConfig.class);
context.refresh();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
return context;
}

private static MockHttpServletRequest apiRequest(WebApplicationContext context, String pathInfo) {
MockHttpServletRequest request = new MockHttpServletRequest(context.getServletContext(), "GET", "/api" + pathInfo);
request.setServletPath("/api");
request.setPathInfo(pathInfo);
return request;
}

@EnableWebSecurity
@EnableWebMvc
static class SecurityChainsConfig {

private final KeycloakSecurityConfig securityConfig = keycloakDisabledSecurityConfig();

@Bean
ITenantManager tenantManager() {
return mock(ITenantManager.class);
}

@Bean
@Order(60)
SecurityFilterChain healthChain(HttpSecurity http) throws Exception {
return securityConfig.healthSecurityFilterChain(http);
}

@Bean
@Order(70)
SecurityFilterChain defaultChain(HttpSecurity http) throws Exception {
return securityConfig.keycloakSecurityFilterChain(http);
}

private static KeycloakSecurityConfig keycloakDisabledSecurityConfig() {
KeycloakSecurityConfig config = new KeycloakSecurityConfig(mock(KeycloakAuthenticationFilter.class),
mock(KeycloakConfiguration.class), mock(IUserManager.class), mock(IAuthenticationProviderManager.class));
config.corsConfiguration = new CorsConfiguration();
return config;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public void setUp() {
keycloakFilter = new KeycloakFilter(configuration, oidcService, providerManager, keycloakGroupManager, userManager);
Mockito.lenient().when(request.getSession()).thenReturn(session);
Mockito.lenient().when(session.getServletContext()).thenReturn(svCtx);
Mockito.lenient().when(request.getServletContext()).thenReturn(svCtx);
Mockito.lenient().when(wac.getBean(ITenantManager.class)).thenReturn(tenantManager);
Mockito.lenient().when(request.getServerName()).thenReturn("dev.entando.org");
Mockito.lenient().when(request.getScheme()).thenReturn("https");
Expand Down Expand Up @@ -421,7 +422,7 @@ void apiCallShouldNotSaveUserOnSession() throws Exception {
}

verify(filterChain, times(1)).doFilter(any(), any());
verify(request, times(1)).getSession();
verify(request, never()).getSession();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ void testService_2() throws EntException {

public RequestContext createExtRequestContext(String serverName, ApplicationContext applicationContext) {
RequestContext reqCtx = this.getRequestContext();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletRequest request = new MockHttpServletRequest(reqCtx.getRequest().getServletContext());
request.setScheme("http");
request.setServerName(serverName);
request.addHeader("Host", serverName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ protected static void setUp() throws Exception {
refresh = true;
}
RequestContext reqCtx = BaseTestCase.createRequestContext(applicationContext, servletContext);
request = new MockHttpServletRequest();
request = new MockHttpServletRequest(servletContext);
request.setAttribute(RequestContext.REQCTX, reqCtx);
response = new MockHttpServletResponse();
request.setSession(new MockHttpSession(servletContext));
Expand Down
Loading