Skip to content

Commit

Permalink
fix: technical resolver change to switch to new authentication objects
Browse files Browse the repository at this point in the history
  • Loading branch information
tkuzynow committed Apr 15, 2024
1 parent 12aa0fa commit 8382134
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 24 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<version>4.3.1</version>
</dependency>

<!-- OpenApi/Swagger dependencies -->
<!-- OpenApi dependencies -->
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
package de.caritas.cob.videoservice.api.tenant;

import com.google.common.collect.Lists;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
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
public class TechnicalUserTenantResolver implements TenantResolver {

@Override
public Optional<Long> 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<String> getRealmRoles(Jwt jwt) {

if (jwt != null) {
var claims = jwt.getClaims();
if (claims.containsKey("realm_access")) {
Map<String, Object> realmAccess = (Map<String, Object>) claims.get("realm_access");
if (realmAccess.containsKey("roles")) {
return (List<String>) realmAccess.get("roles");
}
}
}
return Lists.newArrayList();
}


@Override
public boolean canResolve(HttpServletRequest request) {
return resolve(request).isPresent();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package de.caritas.cob.videoservice.api.tenant;


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 jakarta.servlet.http.HttpServletRequest;
import org.assertj.core.util.Sets;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
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;
Expand All @@ -14,28 +20,45 @@
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;

@ExtendWith(MockitoExtension.class)
class TechnicalUserTenantResolverTest {
public static final long TECHNICAL_CONTEXT = 0L;
@Mock HttpServletRequest authenticatedRequest;
@Mock
HttpServletRequest authenticatedRequest;

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
KeycloakAuthenticationToken token;

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
AccessToken accessToken;

@Mock Access access;
@Mock
Access access;

@Mock
SecurityContext mockSecurityContext;

@Mock
Authentication mockAuthentication;

@InjectMocks TechnicalUserTenantResolver technicalUserTenantResolver;
@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);
Expand All @@ -44,11 +67,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<String, Object> 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<String, Object> givenClaimMapContainingRole(String realmRole) {
HashMap<String, Object> claimMap = Maps.newHashMap();
var realmAccess = Maps.newHashMap();
realmAccess.put("roles", Lists.newArrayList(realmRole));
claimMap.put("realm_access", realmAccess);
return claimMap;
}
}

0 comments on commit 8382134

Please sign in to comment.