diff --git a/google_checks_light.xml b/google_checks_light.xml index 339138d6..eff0b29d 100644 --- a/google_checks_light.xml +++ b/google_checks_light.xml @@ -37,14 +37,6 @@ - - @@ -130,7 +122,6 @@ - - - - - - diff --git a/pom.xml b/pom.xml index 7a583d66..644a9803 100644 --- a/pom.xml +++ b/pom.xml @@ -541,14 +541,13 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.1 + 3.3.0 validate validate google_checks_light.xml - UTF-8 true true true diff --git a/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java b/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java index 95753b25..fab2eca4 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/admin/service/AgencyAdminService.java @@ -26,7 +26,6 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; - import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolver.java b/src/main/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolver.java index 51fa3f70..80f4493a 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolver.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolver.java @@ -6,10 +6,10 @@ import java.util.Optional; import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.keycloak.KeycloakSecurityContext; -import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Component; @@ -22,31 +22,39 @@ public class AccessTokenTenantResolver implements TenantResolver { @Override public Optional resolve(HttpServletRequest request) { - return resolveTenantIdFromTokenClaims(request); + return resolveTenantIdFromTokenClaims(); } - private Optional resolveTenantIdFromTokenClaims(HttpServletRequest request) { - Map claimMap = getClaimMap(request); + private Optional resolveTenantIdFromTokenClaims() { + Map claimMap = getClaimMap(); log.debug("Found tenantId in claim : " + claimMap.toString()); return getUserTenantIdAttribute(claimMap); } private Optional getUserTenantIdAttribute(Map claimMap) { if (claimMap.containsKey(TENANT_ID)) { - Integer tenantId = (Integer) claimMap.get(TENANT_ID); - return Optional.of(Long.valueOf(tenantId)); - } else { - return Optional.empty(); + Object tenantIdObject = claimMap.get(TENANT_ID); + if (tenantIdObject instanceof Long tenantId) { + return Optional.of(tenantId); + } + if (tenantIdObject instanceof Integer tenantId) { + return Optional.of(Long.valueOf(tenantId)); + } } + return Optional.empty(); } - private Map getClaimMap(HttpServletRequest request) { - KeycloakSecurityContext keycloakSecContext = - ((KeycloakAuthenticationToken) request.getUserPrincipal()).getAccount() - .getKeycloakSecurityContext(); - return keycloakSecContext.getToken().getOtherClaims(); + private Map getClaimMap() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + var jwt = (Jwt) authentication.getPrincipal(); + return jwt.getClaims(); + } else { + return Map.of(); + } } + @Override public boolean canResolve(HttpServletRequest request) { return resolve(request).isPresent(); diff --git a/src/main/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolver.java b/src/main/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolver.java index 715e6b1e..3074c3fe 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolver.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolver.java @@ -1,9 +1,14 @@ package de.caritas.cob.agencyservice.api.tenant; +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.Optional; import jakarta.servlet.http.HttpServletRequest; -import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; -import org.keycloak.representations.AccessToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Component; @Component @@ -11,19 +16,34 @@ public class TechnicalUserTenantResolver implements TenantResolver { @Override public Optional resolve(HttpServletRequest request) { - return isTechnicalUserRole(request) ? Optional.of(0L) : Optional.empty(); + return isTechnicalUserRole() ? Optional.of(0L) : Optional.empty(); } - private boolean isTechnicalUserRole(HttpServletRequest request) { - AccessToken token = ((KeycloakAuthenticationToken) request.getUserPrincipal()).getAccount() - .getKeycloakSecurityContext().getToken(); - return hasRoles(token) && token.getRealmAccess().getRoles().contains("technical"); + private boolean isTechnicalUserRole() { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + Jwt jwt = (Jwt) authentication.getPrincipal(); + return getRealmRoles(jwt).contains("technical"); + } + return false; } - private boolean hasRoles(AccessToken accessToken) { - return accessToken.getRealmAccess() != null && accessToken.getRealmAccess().getRoles() != null; + private Collection getRealmRoles(Jwt jwt) { + + if (jwt != null) { + var claims = jwt.getClaims(); + if (claims.containsKey("realm_access")) { + Map realmAccess = (Map) claims.get("realm_access"); + if (realmAccess.containsKey("roles")) { + return (List) realmAccess.get("roles"); + } + } + } + return Lists.newArrayList(); } + @Override public boolean canResolve(HttpServletRequest request) { return resolve(request).isPresent(); diff --git a/src/main/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverService.java b/src/main/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverService.java index a869bb60..8280c450 100644 --- a/src/main/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverService.java +++ b/src/main/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverService.java @@ -12,6 +12,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @Slf4j @@ -39,7 +41,8 @@ public class TenantResolverService { private boolean multitenancyWithSingleDomain; private List nonAuthenticatedTenantResolvers() { - return newArrayList(multitenancyWithSingleDomainTenantResolver, customHeaderTenantResolver, subdomainTenantResolver); + return newArrayList(multitenancyWithSingleDomainTenantResolver, customHeaderTenantResolver, + subdomainTenantResolver); } private List tenantIdCrossValidationResolvers() { @@ -51,7 +54,7 @@ private ArrayList authenticatedTenantResolvers() { } public Long resolve(HttpServletRequest request) { - if (userIsAuthenticated(request)) { + if (userIsAuthenticated()) { return resolveForAuthenticatedUser(request); } else { return resolveForNonAuthenticatedUser(request); @@ -88,7 +91,7 @@ private Long resolveForNonAuthenticatedUser(HttpServletRequest request) { return tenantId.get(); } - private void validateResolvedTenantMatch(Optional tenantId, + private void validateResolvedTenantMatch(Optional tenantId, Optional tenantIdFromHeaderOrSubdomain) { if (tenantId.isPresent() && tenantIdFromHeaderOrSubdomain.isPresent()) { if (!tenantId.get().equals(tenantIdFromHeaderOrSubdomain.get())) { @@ -109,7 +112,12 @@ private Optional getFirstResolvedTenant(HttpServletRequest request, return Optional.empty(); } - private boolean userIsAuthenticated(HttpServletRequest request) { - return request.getUserPrincipal() != null; + private boolean userIsAuthenticated() { + /* after upgrade to oauth2ResourceServer security configuration (spring 6.x upgrade) + for authenticated users request.getUserPrincipal() might be still null at the time of HttpTenantFilter is executed + but BearerTokenAuthenticationFilter has already set the principal in the SecurityContext */ + SecurityContext context = SecurityContextHolder.getContext(); + return context.getAuthentication() != null + && context.getAuthentication().isAuthenticated(); } } diff --git a/src/main/java/de/caritas/cob/agencyservice/config/SecurityConfig.java b/src/main/java/de/caritas/cob/agencyservice/config/SecurityConfig.java index 25ac0fcb..18f83ea8 100644 --- a/src/main/java/de/caritas/cob/agencyservice/config/SecurityConfig.java +++ b/src/main/java/de/caritas/cob/agencyservice/config/SecurityConfig.java @@ -19,6 +19,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; @@ -74,7 +75,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { if (multitenancy) { httpSecurity = httpSecurity - .addFilterAfter(httpTenantFilter, CsrfFilter.class); + .addFilterAfter(httpTenantFilter, BearerTokenAuthenticationFilter.class); } httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) diff --git a/src/main/java/de/caritas/cob/agencyservice/config/security/AuthorisationService.java b/src/main/java/de/caritas/cob/agencyservice/config/security/AuthorisationService.java index 660c2f4a..3c402a96 100644 --- a/src/main/java/de/caritas/cob/agencyservice/config/security/AuthorisationService.java +++ b/src/main/java/de/caritas/cob/agencyservice/config/security/AuthorisationService.java @@ -21,26 +21,6 @@ public class AuthorisationService { private final RoleAuthorizationAuthorityMapper roleAuthorizationAuthorityMapper = new RoleAuthorizationAuthorityMapper(); - public boolean hasAuthority(String authorityName) { - return getAuthentication().getAuthorities().stream() - .anyMatch(role -> authorityName.equals(role.getAuthority())); - } - - public boolean hasRole(String roleName) { - var roles = extractRealmRoles(getPrincipal()); - return roles != null && roles.contains(roleName); - } - - public Optional findTenantIdInAccessToken() { - Jwt principal = getPrincipal(); - Long tenantId = (Long) principal.getClaims().get("tenantId"); - - if (tenantId == null) { - throw new AccessDeniedException("tenantId attribute not found in the access token"); - } - return Optional.of(tenantId); - } - public Object getUsername() { return getPrincipal().getClaims().get("username"); } diff --git a/src/test/java/de/caritas/cob/agencyservice/api/controller/AuthenticationMockBuilder.java b/src/test/java/de/caritas/cob/agencyservice/api/controller/AuthenticationMockBuilder.java deleted file mode 100644 index b416a874..00000000 --- a/src/test/java/de/caritas/cob/agencyservice/api/controller/AuthenticationMockBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -package de.caritas.cob.agencyservice.api.controller; - -import com.google.common.collect.Lists; -import org.keycloak.KeycloakPrincipal; -import org.keycloak.KeycloakSecurityContext; -import org.keycloak.representations.AccessToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; - -public class AuthenticationMockBuilder { - - private String authority; - private String tenantId; - - AuthenticationMockBuilder withAuthority(String authority) { - this.authority = authority; - return this; - } - - AuthenticationMockBuilder withTenantIdAttribute(String tenantId) { - this.tenantId = tenantId; - return this; - } - - public Authentication build() { - return new Authentication() { - @Override - public Collection getAuthorities() { - return Lists.newArrayList((GrantedAuthority) () -> authority); - } - - @Override - public Object getCredentials() { - return null; - } - - @Override - public Object getDetails() { - return null; - } - - @Override - public Object getPrincipal() { - AccessToken token = new AccessToken(); - token.setOtherClaims("tenantId", tenantId); - KeycloakSecurityContext keycloakSecurityContext = new KeycloakSecurityContext("", token, - null, null); - return new KeycloakPrincipal<>("name", keycloakSecurityContext); - } - - @Override - public boolean isAuthenticated() { - return true; - } - - @Override - public void setAuthenticated(boolean b) throws IllegalArgumentException { - - } - - @Override - public String getName() { - return null; - } - }; - } -} diff --git a/src/test/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolverTest.java b/src/test/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolverTest.java index 49c7b619..90e2c6ad 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolverTest.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/tenant/AccessTokenTenantResolverTest.java @@ -5,10 +5,13 @@ import static org.mockito.Mockito.when; import com.google.common.collect.Maps; +import java.time.Instant; import java.util.HashMap; +import java.util.Map; import java.util.Optional; import jakarta.servlet.http.HttpServletRequest; import org.assertj.core.util.Sets; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @@ -18,26 +21,42 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; @ExtendWith(MockitoExtension.class) class AccessTokenTenantResolverTest { + + @InjectMocks + AccessTokenTenantResolver accessTokenTenantResolver; + + @Mock + SecurityContext mockSecurityContext; + + @Mock + Authentication mockAuthentication; + @Mock HttpServletRequest authenticatedRequest; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - KeycloakAuthenticationToken token; + @AfterEach + public void tearDown() { + SecurityContextHolder.clearContext(); + } - @InjectMocks - AccessTokenTenantResolver accessTokenTenantResolver; + private void givenUserIsAuthenticated() { + SecurityContextHolder.setContext(mockSecurityContext); + when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); + when(mockAuthentication.getPrincipal()).thenReturn(buildJwt()); + } @Test void resolve_Should_ResolveTenantId_When_TenantIdInAccessTokenClaim() { // given - when(authenticatedRequest.getUserPrincipal()).thenReturn(token); - - HashMap claimMap = givenClaimMapContainingTenantId(1); - when(token.getAccount().getKeycloakSecurityContext().getToken().getOtherClaims()) - .thenReturn(claimMap); + givenUserIsAuthenticated(); // when Optional resolvedTenantId = accessTokenTenantResolver.resolve(authenticatedRequest); @@ -46,6 +65,14 @@ void resolve_Should_ResolveTenantId_When_TenantIdInAccessTokenClaim() { assertThat(resolvedTenantId).isEqualTo(Optional.of(1L)); } + private Jwt buildJwt() { + Map headers = new HashMap<>(); + headers.put("alg", "HS256"); // Signature algorithm + headers.put("typ", "JWT"); // Token type + return new Jwt( + "token", Instant.now(), Instant.now(), headers, givenClaimMapContainingTenantId(1)); + } + private HashMap givenClaimMapContainingTenantId(Integer tenantId) { HashMap claimMap = Maps.newHashMap(); claimMap.put("tenantId", tenantId); diff --git a/src/test/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolverTest.java b/src/test/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolverTest.java index 218908de..f5973751 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolverTest.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/tenant/TechnicalUserTenantResolverTest.java @@ -4,10 +4,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import jakarta.servlet.http.HttpServletRequest; import org.assertj.core.api.AssertionsForClassTypes; import org.assertj.core.util.Sets; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @@ -17,6 +23,11 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; @ExtendWith(MockitoExtension.class) class TechnicalUserTenantResolverTest { @@ -33,16 +44,25 @@ class TechnicalUserTenantResolverTest { @Mock Access access; + @Mock + SecurityContext mockSecurityContext; + + @Mock + Authentication mockAuthentication; + @InjectMocks TechnicalUserTenantResolver technicalUserTenantResolver; + @AfterEach + public void tearDown() { + SecurityContextHolder.clearContext(); + } + @Test void resolve_should_ResolveTechnicalTenantId_ForTechnicalUserRole() { // given - when(authenticatedRequest.getUserPrincipal()).thenReturn(token); - when(token.getAccount() - .getKeycloakSecurityContext().getToken()).thenReturn(accessToken); - when(accessToken.getRealmAccess().getRoles()).thenReturn(Sets.newLinkedHashSet("technical")); + givenUserIsAuthenticated(); + when(mockAuthentication.getPrincipal()).thenReturn(buildJwtWithRealmRole("technical")); var resolved = technicalUserTenantResolver.resolve(authenticatedRequest); // then assertThat(resolved).contains(TECHNICAL_CONTEXT); @@ -51,12 +71,31 @@ void resolve_should_ResolveTechnicalTenantId_ForTechnicalUserRole() { @Test void resolve_should_NotResolveTenantId_When_NonTechnicalUserRole() { // given - when(authenticatedRequest.getUserPrincipal()).thenReturn(token); - when(token.getAccount() - .getKeycloakSecurityContext().getToken()).thenReturn(accessToken); - when(accessToken.getRealmAccess().getRoles()).thenReturn(Sets.newLinkedHashSet("another-role")); + givenUserIsAuthenticated(); + when(mockAuthentication.getPrincipal()).thenReturn(buildJwtWithRealmRole("another-role")); var resolved = technicalUserTenantResolver.resolve(authenticatedRequest); // then assertThat(resolved).isEmpty(); } + + private void givenUserIsAuthenticated() { + SecurityContextHolder.setContext(mockSecurityContext); + when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); + } + + private Jwt buildJwtWithRealmRole(String realmRole) { + Map headers = new HashMap<>(); + headers.put("alg", "HS256"); // Signature algorithm + headers.put("typ", "JWT"); // Token type + return new Jwt( + "token", Instant.now(), Instant.now(), headers, givenClaimMapContainingRole(realmRole)); + } + + private HashMap givenClaimMapContainingRole(String realmRole) { + HashMap claimMap = Maps.newHashMap(); + var realmAccess = Maps.newHashMap(); + realmAccess.put("roles", Lists.newArrayList(realmRole)); + claimMap.put("realm_access", realmAccess); + return claimMap; + } } \ No newline at end of file diff --git a/src/test/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverServiceTest.java b/src/test/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverServiceTest.java index 1586302b..b773c63b 100644 --- a/src/test/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverServiceTest.java +++ b/src/test/java/de/caritas/cob/agencyservice/api/tenant/TenantResolverServiceTest.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @@ -12,6 +13,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -46,9 +50,20 @@ class TenantResolverServiceTest { @Mock private TechnicalUserTenantResolver technicalUserTenantResolver; + @Mock + private SecurityContext mockSecurityContext; + + @Mock + private Authentication mockAuthentication; + @Mock private MultitenancyWithSingleDomainTenantResolver multitenancyWithSingleDomainTenantResolver; + @AfterEach + public void tearDown() { + SecurityContextHolder.clearContext(); + } + @Test void resolve_Should_ResolveFromAccessTokenForAuthenticatedUser_And_PassValidation() { // given @@ -66,7 +81,9 @@ void resolve_Should_ResolveFromAccessTokenForAuthenticatedUser_And_PassValidatio } private void givenUserIsAuthenticated() { - when(authenticatedRequest.getUserPrincipal()).thenReturn(token); + SecurityContextHolder.setContext(mockSecurityContext); + when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); + when(mockAuthentication.isAuthenticated()).thenReturn(true); } @Test @@ -80,7 +97,8 @@ void resolve_Should_ThrowAccessDeniedException_ForAuthenticatedUser_When_Subdoma when(subdomainTenantResolver.resolve(authenticatedRequest)).thenReturn(Optional.of(2L)); // when, then - assertThrows(AccessDeniedException.class, () -> tenantResolverService.resolve(authenticatedRequest)); + assertThrows(AccessDeniedException.class, + () -> tenantResolverService.resolve(authenticatedRequest)); } @Test @@ -90,7 +108,8 @@ void resolve_Should_ThrowAccessDeniedExceptionForAuthenticatedUser_IfAccessToken when(accessTokenTenantResolver.canResolve(authenticatedRequest)).thenReturn(false); // when, then - assertThrows(AccessDeniedException.class, () -> tenantResolverService.resolve(authenticatedRequest)); + assertThrows(AccessDeniedException.class, + () -> tenantResolverService.resolve(authenticatedRequest)); } @Test @@ -118,7 +137,8 @@ void resolve_Should_ResolveTenantId_ForTechnicalUserRole() { // given givenUserIsAuthenticated(); when(technicalUserTenantResolver.canResolve(authenticatedRequest)).thenReturn(true); - when(technicalUserTenantResolver.resolve(authenticatedRequest)).thenReturn(Optional.of(TECHNICAL_CONTEXT)); + when(technicalUserTenantResolver.resolve(authenticatedRequest)).thenReturn( + Optional.of(TECHNICAL_CONTEXT)); Long resolved = tenantResolverService.resolve(authenticatedRequest); // then