diff --git a/src/main/java/de/caritas/cob/videoservice/api/authorization/VideoUser.java b/src/main/java/de/caritas/cob/videoservice/api/authorization/AuthenticatedUser.java similarity index 95% rename from src/main/java/de/caritas/cob/videoservice/api/authorization/VideoUser.java rename to src/main/java/de/caritas/cob/videoservice/api/authorization/AuthenticatedUser.java index cd7b772..52bd0e7 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/authorization/VideoUser.java +++ b/src/main/java/de/caritas/cob/videoservice/api/authorization/AuthenticatedUser.java @@ -15,7 +15,7 @@ @NoArgsConstructor @Getter @Setter -public class VideoUser { +public class AuthenticatedUser { @NonNull private String userId; diff --git a/src/main/java/de/caritas/cob/videoservice/api/facade/VideoCallFacade.java b/src/main/java/de/caritas/cob/videoservice/api/facade/VideoCallFacade.java index 780310c..152789c 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/facade/VideoCallFacade.java +++ b/src/main/java/de/caritas/cob/videoservice/api/facade/VideoCallFacade.java @@ -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; @@ -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; @@ -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; @@ -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(), diff --git a/src/main/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplier.java b/src/main/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplier.java index 0b522d3..08bc5d6 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplier.java +++ b/src/main/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplier.java @@ -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; @@ -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; diff --git a/src/main/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorService.java b/src/main/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorService.java index 7a6ef2e..fa8d131 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorService.java +++ b/src/main/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorService.java @@ -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; @@ -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"; diff --git a/src/main/java/de/caritas/cob/videoservice/config/AuthenticatedUserConfig.java b/src/main/java/de/caritas/cob/videoservice/config/AuthenticatedUserConfig.java index 262e84f..6d96bef 100644 --- a/src/main/java/de/caritas/cob/videoservice/config/AuthenticatedUserConfig.java +++ b/src/main/java/de/caritas/cob/videoservice/config/AuthenticatedUserConfig.java @@ -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 claimMap = keycloakSecContext.getToken().getOtherClaims(); - - var authenticatedUser = new VideoUser(); - authenticatedUser.setAccessToken(getUserAccessToken(keycloakSecContext)); + Map 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 extractRealmRoles(Jwt jwt) { + Map realmAccess = (Map) jwt.getClaims().get("realm_access"); + if (realmAccess != null) { + var roles = (List) realmAccess.get("roles"); + if (roles != null) { + return roles; + } } - - return keycloakSecContext.getTokenString(); + return Lists.newArrayList(); } private String getUserAttribute(Map 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(); diff --git a/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerE2eIT.java b/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerE2eIT.java index 3ea88c9..1d88810 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerE2eIT.java +++ b/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerE2eIT.java @@ -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; @@ -44,7 +44,7 @@ class VideoControllerE2eIT { @MockBean @SuppressWarnings("unused") - private VideoUser authenticatedUser; + private AuthenticatedUser authenticatedUser; @MockBean ChatService chatService; diff --git a/src/test/java/de/caritas/cob/videoservice/api/facade/VideoCallFacadeTest.java b/src/test/java/de/caritas/cob/videoservice/api/facade/VideoCallFacadeTest.java index 8e640d4..6e3b89e 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/facade/VideoCallFacadeTest.java +++ b/src/test/java/de/caritas/cob/videoservice/api/facade/VideoCallFacadeTest.java @@ -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; @@ -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; diff --git a/src/test/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplierTest.java b/src/test/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplierTest.java index a494056..5ef34ab 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplierTest.java +++ b/src/test/java/de/caritas/cob/videoservice/api/service/httpheader/SecurityHeaderSupplierTest.java @@ -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; @@ -24,7 +24,7 @@ public class SecurityHeaderSupplierTest { @InjectMocks private SecurityHeaderSupplier securityHeaderSupplier; - @Mock private VideoUser videoUser; + @Mock private AuthenticatedUser videoUser; @Before public void setup() { diff --git a/src/test/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorServiceTest.java b/src/test/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorServiceTest.java index 7e2c519..8765edd 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorServiceTest.java +++ b/src/test/java/de/caritas/cob/videoservice/api/service/video/jwt/TokenGeneratorServiceTest.java @@ -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; @@ -32,7 +32,7 @@ public class TokenGeneratorServiceTest { @InjectMocks private TokenGeneratorService tokenGeneratorService; - @Mock private VideoUser authenticatedUser; + @Mock private AuthenticatedUser authenticatedUser; @Before public void setup() {