Skip to content

Commit

Permalink
feat: get rid of KeycloakAuthenticationToken, due to migration to new…
Browse files Browse the repository at this point in the history
… spring boot
  • Loading branch information
tkuzynow committed Apr 3, 2024
1 parent f3300f1 commit badc79e
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@NoArgsConstructor
@Getter
@Setter
public class VideoUser {
public class AuthenticatedUser {

@NonNull private String userId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import static java.util.Collections.singletonList;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.exception.httpresponse.BadRequestException;
import de.caritas.cob.videoservice.api.model.CreateVideoCallDTO;
import de.caritas.cob.videoservice.api.model.VideoCallResponseDTO;
Expand All @@ -26,7 +26,6 @@
import de.caritas.cob.videoservice.liveservice.generated.web.model.VideoCallRequestDTO;
import de.caritas.cob.videoservice.statisticsservice.generated.web.model.UserRole;
import de.caritas.cob.videoservice.userservice.generated.web.model.ConsultantSessionDTO;
import java.util.stream.Collectors;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -43,7 +42,7 @@ public class VideoCallFacade {

private final @NonNull ChatService chatService;
private final @NonNull LiveEventNotificationService liveEventNotificationService;
private final @NonNull VideoUser authenticatedUser;
private final @NonNull AuthenticatedUser authenticatedUser;
private final @NonNull VideoCallUrlGeneratorService videoCallUrlGeneratorService;
private final @NonNull UuidRegistry uuidRegistry;
private final @NonNull StatisticsService statisticsService;
Expand Down Expand Up @@ -116,7 +115,7 @@ private VideoCallResponseDTO startGroupVideoCall(
chatMembers.getMembers().stream()
.filter(member -> !initiatorRcUserId.equals(member.getId()))
.map(member -> member.getUserId())
.collect(Collectors.toList());
.toList();
this.liveEventNotificationService.sendVideoCallRequestLiveEvent(
buildLiveEventMessage(
chatById.getGroupId(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.caritas.cob.videoservice.api.service.httpheader;

import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import java.util.UUID;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
Expand All @@ -13,7 +13,7 @@
@RequiredArgsConstructor
public class SecurityHeaderSupplier {

private final @NonNull VideoUser authenticatedUser;
private final @NonNull AuthenticatedUser authenticatedUser;

@Value("${csrf.header.property}")
private String csrfHeaderProperty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.exception.httpresponse.InternalServerErrorException;
import de.caritas.cob.videoservice.api.service.video.jwt.model.VideoCallToken;
import java.sql.Date;
import java.time.LocalDateTime;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
Expand All @@ -25,12 +24,11 @@
public class TokenGeneratorService {

@Autowired
public TokenGeneratorService(
@NonNull @Qualifier("AuthenticatedOrAnonymousUser") VideoUser authenticatedUser) {
public TokenGeneratorService(@NonNull AuthenticatedUser authenticatedUser) {
this.videoUser = authenticatedUser;
}

private final @NonNull VideoUser videoUser;
private final @NonNull AuthenticatedUser videoUser;

private static final String ROOM_CLAIM = "room";
private static final String MODERATOR_CLAIM = "moderator";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,117 +2,77 @@

import static java.util.Objects.isNull;

import de.caritas.cob.videoservice.api.authorization.VideoUser;
import com.google.common.collect.Lists;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.exception.KeycloakException;
import jakarta.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.beans.factory.annotation.Qualifier;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/** Configuration for the {@link VideoUser}. */
/** Configuration for the {@link AuthenticatedUser}. */
@Configuration
public class AuthenticatedUserConfig {

private static final String CLAIM_NAME_USER_ID = "userId";
private static final String CLAIM_NAME_USERNAME = "username";
private static final VideoUser ANONYMOUS_USER = new VideoUser();
private static final AuthenticatedUser ANONYMOUS_USER = new AuthenticatedUser();

/**
* Returns the currently authenticated user.
*
* @return {@link VideoUser}
* @return {@link AuthenticatedUser}
*/
@Bean
@Primary
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public VideoUser getAuthenticatedUser() {
var userPrincipal = getRequest().getUserPrincipal();
return createAuthenticatedUser(userPrincipal);
}

/**
* Returns the currently authenticated user, or an anonymous representation.
*
* @return {@link VideoUser}
*/
@Bean
@Qualifier("AuthenticatedOrAnonymousUser")
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public VideoUser getAuthenticatedOrAnonymousUser() {
public AuthenticatedUser getAuthenticatedUser() {
var userPrincipal = getRequest().getUserPrincipal();
if (isNull(userPrincipal)) {
return ANONYMOUS_USER;
}
return createAuthenticatedUser(userPrincipal);
}
JwtAuthenticationToken authenticationToken =
(JwtAuthenticationToken) getRequest().getUserPrincipal();

private VideoUser createAuthenticatedUser(Principal userPrincipal) {
var keycloakUser = (KeycloakAuthenticationToken) userPrincipal;
var keycloakSecContext = keycloakUser.getAccount().getKeycloakSecurityContext();
Map<String, Object> claimMap = keycloakSecContext.getToken().getOtherClaims();

var authenticatedUser = new VideoUser();
authenticatedUser.setAccessToken(getUserAccessToken(keycloakSecContext));
Map<String, Object> claimMap = authenticationToken.getToken().getClaims();
AuthenticatedUser authenticatedUser = new AuthenticatedUser();
authenticatedUser.setAccessToken(authenticationToken.getToken().getTokenValue());
authenticatedUser.setUserId(getUserAttribute(claimMap, CLAIM_NAME_USER_ID));
authenticatedUser.setUsername(getUserAttribute(claimMap, CLAIM_NAME_USERNAME));

var roles = keycloakSecContext.getToken().getRealmAccess().getRoles();
authenticatedUser.setRoles(roles);

authenticatedUser.setRoles(
extractRealmRoles(authenticationToken.getToken()).stream().collect(Collectors.toSet()));
return authenticatedUser;
}

private String getUserAccessToken(KeycloakSecurityContext keycloakSecContext) {
if (isNull(keycloakSecContext.getTokenString())) {
throw new KeycloakException("No valid Keycloak access token string found.");
public Collection<String> extractRealmRoles(Jwt jwt) {
Map<String, Object> realmAccess = (Map<String, Object>) jwt.getClaims().get("realm_access");
if (realmAccess != null) {
var roles = (List<String>) realmAccess.get("roles");
if (roles != null) {
return roles;
}
}

return keycloakSecContext.getTokenString();
return Lists.newArrayList();
}

private String getUserAttribute(Map<String, Object> claimMap, String claimValue) {
if (!claimMap.containsKey(claimValue)) {
throw new KeycloakException("Keycloak user attribute '" + claimValue + "' not found.");
}

return claimMap.get(claimValue).toString();
}

/**
* Returns the {@link KeycloakAuthenticationToken} which represents the token for a Keycloak
* authentication.
*
* @return {@link KeycloakAuthenticationToken}
*/
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakAuthenticationToken getAccessToken() {
return (KeycloakAuthenticationToken) getRequest().getUserPrincipal();
}

/**
* Returns the {@link KeycloakSecurityContext}.
*
* @return {@link KeycloakSecurityContext}
*/
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakSecurityContext getKeycloakSecurityContext() {
return ((KeycloakAuthenticationToken) getRequest().getUserPrincipal())
.getAccount()
.getKeycloakSecurityContext();
}

private HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.model.RejectVideoCallDTO;
import de.caritas.cob.videoservice.api.service.RejectVideoCallService;
import de.caritas.cob.videoservice.api.service.session.ChatService;
Expand Down Expand Up @@ -44,7 +44,7 @@ class VideoControllerE2eIT {

@MockBean
@SuppressWarnings("unused")
private VideoUser authenticatedUser;
private AuthenticatedUser authenticatedUser;

@MockBean ChatService chatService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import static org.mockito.Mockito.when;

import com.google.common.collect.Lists;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.exception.httpresponse.BadRequestException;
import de.caritas.cob.videoservice.api.model.CreateVideoCallDTO;
import de.caritas.cob.videoservice.api.model.VideoCallResponseDTO;
Expand Down Expand Up @@ -65,7 +65,7 @@ class VideoCallFacadeTest {
@Mock private SessionService sessionService;
@Mock private LiveEventNotificationService liveEventNotificationService;
@Mock private VideoCallUrlGeneratorService videoCallUrlGeneratorService;
@Mock private VideoUser authenticatedUser;
@Mock private AuthenticatedUser authenticatedUser;
@Mock private UuidRegistry uuidRegistry;
@Mock private StatisticsService statisticsService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import static org.mockito.Mockito.when;
import static org.springframework.test.util.ReflectionTestUtils.setField;

import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -24,7 +24,7 @@
public class SecurityHeaderSupplierTest {

@InjectMocks private SecurityHeaderSupplier securityHeaderSupplier;
@Mock private VideoUser videoUser;
@Mock private AuthenticatedUser videoUser;

@Before
public void setup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.impl.NullClaim;
import com.auth0.jwt.interfaces.DecodedJWT;
import de.caritas.cob.videoservice.api.authorization.VideoUser;
import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser;
import de.caritas.cob.videoservice.api.exception.httpresponse.InternalServerErrorException;
import de.caritas.cob.videoservice.api.service.video.jwt.model.VideoCallToken;
import org.junit.Before;
Expand All @@ -32,7 +32,7 @@ public class TokenGeneratorServiceTest {

@InjectMocks private TokenGeneratorService tokenGeneratorService;

@Mock private VideoUser authenticatedUser;
@Mock private AuthenticatedUser authenticatedUser;

@Before
public void setup() {
Expand Down

0 comments on commit badc79e

Please sign in to comment.