From cb2eca3a26a063fd58e2bd0a02b873d46ee113b4 Mon Sep 17 00:00:00 2001 From: hill-daniel <20245376+hill-daniel@users.noreply.github.com> Date: Tue, 5 Apr 2022 12:45:01 +0200 Subject: [PATCH] feat: enable web token creation for anonymous users --- .../api/authorization/Authority.java | 14 +- .../api/authorization/UserRole.java | 15 ++ ...{AuthenticatedUser.java => VideoUser.java} | 4 +- .../api/facade/StartVideoCallFacade.java | 4 +- .../SecurityHeaderSupplier.java | 4 +- .../video/jwt/TokenGeneratorService.java | 20 ++- .../config/AuthenticatedUserConfig.java | 39 ++++- .../config/security/WebSecurityConfig.java | 2 +- .../resources/application-testing.properties | 7 + .../VideoControllerAuthorizationIT.java | 136 +++++++++++++----- .../api/facade/StartVideoCallFacadeTest.java | 4 +- .../SecurityHeaderSupplierTest.java | 6 +- .../video/jwt/TokenGeneratorServiceTest.java | 28 +++- .../api/testhelper/PathConstants.java | 4 + .../api/testhelper/TestConstants.java | 5 +- 15 files changed, 223 insertions(+), 69 deletions(-) create mode 100644 src/main/java/de/caritas/cob/videoservice/api/authorization/UserRole.java rename src/main/java/de/caritas/cob/videoservice/api/authorization/{AuthenticatedUser.java => VideoUser.java} (85%) diff --git a/src/main/java/de/caritas/cob/videoservice/api/authorization/Authority.java b/src/main/java/de/caritas/cob/videoservice/api/authorization/Authority.java index ab01735..b39ca7e 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/authorization/Authority.java +++ b/src/main/java/de/caritas/cob/videoservice/api/authorization/Authority.java @@ -7,14 +7,14 @@ */ public enum Authority { - CONSULTANT("consultant", "AUTHORIZATION_CONSULTANT_DEFAULT"), - USER("user", "AUTHORIZATION_USER_DEFAULT"); + CONSULTANT(UserRole.CONSULTANT, "AUTHORIZATION_CONSULTANT_DEFAULT"), + USER(UserRole.USER, "AUTHORIZATION_USER_DEFAULT"); - private final String roleName; + private final UserRole role; private final String authorityName; - Authority(final String roleName, final String authorityName) { - this.roleName = roleName; + Authority(final UserRole role, final String authorityName) { + this.role = role; this.authorityName = authorityName; } @@ -26,14 +26,14 @@ public enum Authority { */ public static Authority fromRoleName(String roleName) { return Stream.of(values()) - .filter(authority -> authority.roleName.equals(roleName)) + .filter(authority -> authority.role.getValue().equals(roleName)) .findFirst() .orElse(null); } /** * Returns the authority name for the given {@link Authority}. - * + * * @return authority name for the given {@link Authority} **/ public String getAuthority() { diff --git a/src/main/java/de/caritas/cob/videoservice/api/authorization/UserRole.java b/src/main/java/de/caritas/cob/videoservice/api/authorization/UserRole.java new file mode 100644 index 0000000..efd7594 --- /dev/null +++ b/src/main/java/de/caritas/cob/videoservice/api/authorization/UserRole.java @@ -0,0 +1,15 @@ +package de.caritas.cob.videoservice.api.authorization; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum UserRole { + + USER("user"), + CONSULTANT("consultant"); + + private final String value; + +} diff --git a/src/main/java/de/caritas/cob/videoservice/api/authorization/AuthenticatedUser.java b/src/main/java/de/caritas/cob/videoservice/api/authorization/VideoUser.java similarity index 85% rename from src/main/java/de/caritas/cob/videoservice/api/authorization/AuthenticatedUser.java rename to src/main/java/de/caritas/cob/videoservice/api/authorization/VideoUser.java index acb7aa9..91a0504 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/authorization/AuthenticatedUser.java +++ b/src/main/java/de/caritas/cob/videoservice/api/authorization/VideoUser.java @@ -17,7 +17,7 @@ @NoArgsConstructor @Getter @Setter -public class AuthenticatedUser { +public class VideoUser { @NonNull private String userId; @@ -32,6 +32,6 @@ public class AuthenticatedUser { @JsonIgnore public boolean isConsultant() { - return nonNull(roles) && roles.contains(Authority.CONSULTANT.name()); + return nonNull(roles) && roles.contains(UserRole.CONSULTANT.getValue()); } } diff --git a/src/main/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacade.java b/src/main/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacade.java index 8c2ea52..9a99043 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacade.java +++ b/src/main/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacade.java @@ -3,7 +3,7 @@ import static de.caritas.cob.videoservice.api.service.session.SessionStatus.IN_PROGRESS; import static java.util.Collections.singletonList; -import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import de.caritas.cob.videoservice.api.exception.httpresponse.BadRequestException; import de.caritas.cob.videoservice.api.model.CreateVideoCallResponseDTO; import de.caritas.cob.videoservice.api.service.LogService; @@ -31,7 +31,7 @@ public class StartVideoCallFacade { private final @NonNull SessionService sessionService; private final @NonNull LiveEventNotificationService liveEventNotificationService; - private final @NonNull AuthenticatedUser authenticatedUser; + private final @NonNull VideoUser authenticatedUser; private final @NonNull VideoCallUrlGeneratorService videoCallUrlGeneratorService; private final @NonNull UuidRegistry uuidRegistry; private final @NonNull StatisticsService statisticsService; diff --git a/src/main/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplier.java b/src/main/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplier.java index 181c302..7d50b7b 100644 --- a/src/main/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplier.java +++ b/src/main/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplier.java @@ -1,6 +1,6 @@ package de.caritas.cob.videoservice.api.service.securityheader; -import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import java.util.UUID; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -13,7 +13,7 @@ @RequiredArgsConstructor public class SecurityHeaderSupplier { - private final @NonNull AuthenticatedUser authenticatedUser; + private final @NonNull VideoUser 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 7238001..280f1ca 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,7 +7,7 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator.Builder; import com.auth0.jwt.algorithms.Algorithm; -import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import de.caritas.cob.videoservice.api.exception.httpresponse.InternalServerErrorException; import de.caritas.cob.videoservice.api.service.decoder.UsernameDecoder; import de.caritas.cob.videoservice.api.service.video.jwt.model.VideoCallToken; @@ -16,7 +16,8 @@ import java.util.HashMap; import java.util.Map; import lombok.NonNull; -import lombok.RequiredArgsConstructor; +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,11 +26,16 @@ /** * JWT token generator service. */ -@RequiredArgsConstructor @Service public class TokenGeneratorService { - private final @NonNull AuthenticatedUser authenticatedUser; + @Autowired + public TokenGeneratorService( + @NonNull @Qualifier("AuthenticatedOrAnonymousUser") VideoUser authenticatedUser) { + this.videoUser = authenticatedUser; + } + + private final @NonNull VideoUser videoUser; private static final String CONTEXT_CLAIM = "context"; private static final String ROOM_CLAIM = "room"; @@ -67,7 +73,7 @@ public void initAlgorithm() { * @return token */ public String generateToken(String roomId) { - return authenticatedUser.isConsultant() + return videoUser.isConsultant() ? generateModeratorToken(roomId) : generateNonModeratorToken(roomId); } @@ -147,7 +153,7 @@ public String generateModeratorToken(String roomId, String guestVideoCallUrl) { * @return token */ public String generateModeratorToken(String roomId) { - var userContext = createUserContext(authenticatedUser.getUsername()); + var userContext = createUserContext(videoUser.getUsername()); return buildBasicJwt(roomId) .withClaim(MODERATOR_CLAIM, true) @@ -158,7 +164,7 @@ public String generateModeratorToken(String roomId) { private String buildModeratorJwt(String roomId, String guestVideoCallUrl) { return buildBasicJwt(roomId) .withClaim(MODERATOR_CLAIM, true) - .withClaim(CONTEXT_CLAIM, createUserContext(authenticatedUser.getUsername())) + .withClaim(CONTEXT_CLAIM, createUserContext(videoUser.getUsername())) .withClaim(GUEST_URL_CLAIM, guestVideoCallUrl) .sign(algorithm); } 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 04102e8..3e491b6 100644 --- a/src/main/java/de/caritas/cob/videoservice/config/AuthenticatedUserConfig.java +++ b/src/main/java/de/caritas/cob/videoservice/config/AuthenticatedUserConfig.java @@ -2,14 +2,17 @@ import static java.util.Objects.isNull; -import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import de.caritas.cob.videoservice.api.exception.KeycloakException; +import java.security.Principal; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.springframework.beans.factory.annotation.Qualifier; 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.web.context.WebApplicationContext; @@ -17,28 +20,50 @@ import org.springframework.web.context.request.ServletRequestAttributes; /** - * Configuration for the {@link AuthenticatedUser}. + * Configuration for the {@link VideoUser}. */ @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(); /** * Returns the currently authenticated user. * - * @return {@link AuthenticatedUser} + * @return {@link VideoUser} */ @Bean + @Primary @Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) - public AuthenticatedUser getAuthenticatedUser() { + public VideoUser getAuthenticatedUser() { + var userPrincipal = getRequest().getUserPrincipal(); + return createAuthenticatedUser(userPrincipal); + } - var keycloakSecContext = ((KeycloakAuthenticationToken) getRequest().getUserPrincipal()) - .getAccount().getKeycloakSecurityContext(); + /** + * 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() { + var userPrincipal = getRequest().getUserPrincipal(); + if (isNull(userPrincipal)) { + return ANONYMOUS_USER; + } + return createAuthenticatedUser(userPrincipal); + } + + private VideoUser createAuthenticatedUser(Principal userPrincipal) { + var keycloakUser = (KeycloakAuthenticationToken) userPrincipal; + var keycloakSecContext = keycloakUser.getAccount().getKeycloakSecurityContext(); Map claimMap = keycloakSecContext.getToken().getOtherClaims(); - var authenticatedUser = new AuthenticatedUser(); + var authenticatedUser = new VideoUser(); authenticatedUser.setAccessToken(getUserAccessToken(keycloakSecContext)); authenticatedUser.setUserId(getUserAttribute(claimMap, CLAIM_NAME_USER_ID)); authenticatedUser.setUsername(getUserAttribute(claimMap, CLAIM_NAME_USERNAME)); diff --git a/src/main/java/de/caritas/cob/videoservice/config/security/WebSecurityConfig.java b/src/main/java/de/caritas/cob/videoservice/config/security/WebSecurityConfig.java index bd4c7a5..3849497 100644 --- a/src/main/java/de/caritas/cob/videoservice/config/security/WebSecurityConfig.java +++ b/src/main/java/de/caritas/cob/videoservice/config/security/WebSecurityConfig.java @@ -65,7 +65,7 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers("/videocalls/reject") .hasAnyAuthority(USER.getAuthority()) .antMatchers("/videocalls/*/jwt") - .hasAnyAuthority(CONSULTANT.getAuthority()) //TODO: add anonymous + .permitAll() .anyRequest().denyAll() .and() .exceptionHandling() diff --git a/src/main/resources/application-testing.properties b/src/main/resources/application-testing.properties index 427036d..e30421a 100644 --- a/src/main/resources/application-testing.properties +++ b/src/main/resources/application-testing.properties @@ -13,3 +13,10 @@ csrf.cookie.property=csrfCookie # Statistics statistics.enabled=true + +# JWT +video.call.security.jwt.audience=test_server +video.call.security.jwt.issuer=test_app_client +video.call.security.jwt.subject=meet.jitsi +video.call.security.jwt.secret=95148E36-19AA-4A4B-8F5A-FC6245A36912 +video.call.security.jwt.validity.hours=3 \ No newline at end of file diff --git a/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerAuthorizationIT.java b/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerAuthorizationIT.java index 448519d..42344ff 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerAuthorizationIT.java +++ b/src/test/java/de/caritas/cob/videoservice/api/controller/VideoControllerAuthorizationIT.java @@ -1,5 +1,6 @@ package de.caritas.cob.videoservice.api.controller; +import static de.caritas.cob.videoservice.api.testhelper.PathConstants.PATH_GET_WEB_TOKEN; import static de.caritas.cob.videoservice.api.testhelper.PathConstants.PATH_REJECT_VIDEO_CALL; import static de.caritas.cob.videoservice.api.testhelper.PathConstants.PATH_START_VIDEO_CALL; import static de.caritas.cob.videoservice.api.testhelper.RequestBodyConstants.VALID_START_VIDEO_CALL_BODY; @@ -9,6 +10,7 @@ import static de.caritas.cob.videoservice.api.testhelper.TestConstants.CSRF_COOKIE; import static de.caritas.cob.videoservice.api.testhelper.TestConstants.CSRF_HEADER; import static de.caritas.cob.videoservice.api.testhelper.TestConstants.CSRF_VALUE; +import static de.caritas.cob.videoservice.api.testhelper.TestConstants.RC_CHAT_ROOM_ID; import static de.caritas.cob.videoservice.api.testhelper.TestConstants.RC_USER_ID_HEADER; import static de.caritas.cob.videoservice.api.testhelper.TestConstants.RC_USER_ID_VALUE; import static de.caritas.cob.videoservice.api.testhelper.TestConstants.SESSION_ID; @@ -19,6 +21,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -26,6 +29,7 @@ import de.caritas.cob.videoservice.api.facade.StartVideoCallFacade; import de.caritas.cob.videoservice.api.model.RejectVideoCallDTO; import de.caritas.cob.videoservice.api.service.RejectVideoCallService; +import de.caritas.cob.videoservice.api.service.video.jwt.TokenGeneratorService; import javax.servlet.http.Cookie; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +38,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -54,8 +59,12 @@ public class VideoControllerAuthorizationIT { @MockBean private RejectVideoCallService rejectVideoCallService; + @MockBean + private TokenGeneratorService tokenGeneratorService; + private final Cookie csrfCookie = new Cookie(CSRF_COOKIE, CSRF_VALUE); + @Test @WithMockUser(authorities = AUTHORITY_CONSULTANT) public void createVideoCall_Should_ReturnCreated_When_EverythingSucceeded() throws Exception { @@ -64,12 +73,12 @@ public void createVideoCall_Should_ReturnCreated_When_EverythingSucceeded() thro CREATE_VIDEO_CALL_RESPONSE_DTO); mvc.perform(post(PATH_START_VIDEO_CALL) - .cookie(csrfCookie) - .header(CSRF_HEADER, CSRF_VALUE) - .header(RC_USER_ID_HEADER, RC_USER_ID_VALUE) - .contentType(MediaType.APPLICATION_JSON) - .content(VALID_START_VIDEO_CALL_BODY) - .accept(MediaType.APPLICATION_JSON)) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .header(RC_USER_ID_HEADER, RC_USER_ID_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(VALID_START_VIDEO_CALL_BODY) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()); } @@ -81,11 +90,11 @@ public void createVideoCall_Should_ReturnUnauthorized_When_AuthorizationIsMissin CREATE_VIDEO_CALL_RESPONSE_DTO); mvc.perform(post(PATH_START_VIDEO_CALL) - .cookie(csrfCookie) - .header(CSRF_HEADER, CSRF_VALUE) - .contentType(MediaType.APPLICATION_JSON) - .content(VALID_START_VIDEO_CALL_BODY) - .accept(MediaType.APPLICATION_JSON)) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(VALID_START_VIDEO_CALL_BODY) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isUnauthorized()); } @@ -95,11 +104,11 @@ public void createVideoCall_Should_ReturnForbiddenAndCallNoMethods_WhenNoConsult throws Exception { mvc.perform(post(PATH_START_VIDEO_CALL) - .cookie(csrfCookie) - .header(CSRF_HEADER, CSRF_VALUE) - .contentType(MediaType.APPLICATION_JSON) - .content(VALID_START_VIDEO_CALL_BODY) - .accept(MediaType.APPLICATION_JSON)) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(VALID_START_VIDEO_CALL_BODY) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()); verifyNoMoreInteractions(startVideoCallFacade); @@ -111,9 +120,9 @@ public void createVideoCall_Should_ReturnForbiddenAndCallNoMethods_WhenNoCsrfTok throws Exception { mvc.perform(post(PATH_START_VIDEO_CALL) - .contentType(MediaType.APPLICATION_JSON) - .content(VALID_START_VIDEO_CALL_BODY) - .accept(MediaType.APPLICATION_JSON)) + .contentType(MediaType.APPLICATION_JSON) + .content(VALID_START_VIDEO_CALL_BODY) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()); verifyNoMoreInteractions(startVideoCallFacade); @@ -129,9 +138,9 @@ public void rejectVideoCall_Should_ReturnForbiddenAndCallNoMethods_WhenNoCsrfTok .initiatorRcUserId("rcUserId")); mvc.perform(post(PATH_REJECT_VIDEO_CALL) - .contentType(MediaType.APPLICATION_JSON) - .content(content) - .accept(MediaType.APPLICATION_JSON)) + .contentType(MediaType.APPLICATION_JSON) + .content(content) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()); verifyNoMoreInteractions(rejectVideoCallService); @@ -147,11 +156,11 @@ public void rejectVideoCall_Should_ReturnForbiddenAndCallNoMethods_WhenNoAuthori .initiatorRcUserId("rcUserId")); mvc.perform(post(PATH_REJECT_VIDEO_CALL) - .cookie(csrfCookie) - .header(CSRF_HEADER, CSRF_VALUE) - .contentType(MediaType.APPLICATION_JSON) - .content(content) - .accept(MediaType.APPLICATION_JSON)) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(content) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()); verifyNoMoreInteractions(rejectVideoCallService); @@ -167,14 +176,77 @@ public void rejectVideoCall_Should_ReturnOkAndCallService_WhenUserRole() .initiatorRcUserId("rcUserId")); mvc.perform(post(PATH_REJECT_VIDEO_CALL) - .cookie(csrfCookie) - .header(CSRF_HEADER, CSRF_VALUE) - .contentType(MediaType.APPLICATION_JSON) - .content(content) - .accept(MediaType.APPLICATION_JSON)) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(content) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); verify(rejectVideoCallService, times(1)).rejectVideoCall(any()); } + @Test + @WithAnonymousUser + public void getWebToken_should_generate_token_for_anonymous_user() throws Exception { + mvc.perform(get(PATH_GET_WEB_TOKEN) + .header(RC_USER_ID_HEADER, RC_USER_ID_VALUE) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + verify(tokenGeneratorService).generateToken(RC_CHAT_ROOM_ID); + } + + @Test + @WithAnonymousUser + public void getWebToken_should_return_forbidden_for_request_without_csrf() throws Exception { + mvc.perform(get(PATH_GET_WEB_TOKEN) + .header(RC_USER_ID_HEADER, RC_USER_ID_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + } + + @Test + @WithAnonymousUser + public void getWebToken_should_return_bad_request_for_request_without_rocket_chat_user_id() + throws Exception { + mvc.perform(get(PATH_GET_WEB_TOKEN) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); + } + + @Test + @WithMockUser(authorities = {AUTHORITY_USER}) + public void getWebToken_should_generate_token_for_user() throws Exception { + mvc.perform(get(PATH_GET_WEB_TOKEN) + .header(RC_USER_ID_HEADER, RC_USER_ID_VALUE) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + verify(tokenGeneratorService).generateToken(RC_CHAT_ROOM_ID); + } + + @Test + @WithMockUser(authorities = {AUTHORITY_CONSULTANT}) + public void getWebToken_should_generate_token_for_consultant() throws Exception { + mvc.perform(get(PATH_GET_WEB_TOKEN) + .header(RC_USER_ID_HEADER, RC_USER_ID_VALUE) + .cookie(csrfCookie) + .header(CSRF_HEADER, CSRF_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + verify(tokenGeneratorService).generateToken(RC_CHAT_ROOM_ID); + } } diff --git a/src/test/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacadeTest.java b/src/test/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacadeTest.java index 6cee49c..26fc01e 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacadeTest.java +++ b/src/test/java/de/caritas/cob/videoservice/api/facade/StartVideoCallFacadeTest.java @@ -16,7 +16,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import de.caritas.cob.videoservice.api.exception.httpresponse.BadRequestException; import de.caritas.cob.videoservice.api.model.CreateVideoCallResponseDTO; import de.caritas.cob.videoservice.api.service.UuidRegistry; @@ -52,7 +52,7 @@ public class StartVideoCallFacadeTest { @Mock private VideoCallUrlGeneratorService videoCallUrlGeneratorService; @Mock - private AuthenticatedUser authenticatedUser; + private VideoUser authenticatedUser; @Mock private UuidRegistry uuidRegistry; @Mock diff --git a/src/test/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplierTest.java b/src/test/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplierTest.java index c7002fb..563c715 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplierTest.java +++ b/src/test/java/de/caritas/cob/videoservice/api/service/securityheader/SecurityHeaderSupplierTest.java @@ -11,7 +11,7 @@ import static org.mockito.Mockito.when; import static org.springframework.test.util.ReflectionTestUtils.setField; -import de.caritas.cob.videoservice.api.authorization.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,7 +27,7 @@ public class SecurityHeaderSupplierTest { @InjectMocks private SecurityHeaderSupplier securityHeaderSupplier; @Mock - private AuthenticatedUser authenticatedUser; + private VideoUser videoUser; @Before public void setup() { @@ -65,7 +65,7 @@ public void getKeycloakAndCsrfHttpHeaders_Should_Return_CorrectHeaderAndCookieVa @Test public void getRocketChatAndCsrfHttpHeaders_Should_ReturnHeaderWithKeycloakAuthToken() { - when(authenticatedUser.getAccessToken()).thenReturn(BEARER_TOKEN); + when(videoUser.getAccessToken()).thenReturn(BEARER_TOKEN); HttpHeaders result = securityHeaderSupplier.getKeycloakAndCsrfHttpHeaders(); 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 a99a64e..a0ca77d 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 @@ -14,7 +14,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.AuthenticatedUser; +import de.caritas.cob.videoservice.api.authorization.VideoUser; import de.caritas.cob.videoservice.api.exception.httpresponse.InternalServerErrorException; import de.caritas.cob.videoservice.api.service.video.jwt.model.VideoCallToken; import org.junit.Before; @@ -35,7 +35,7 @@ public class TokenGeneratorServiceTest { private TokenGeneratorService tokenGeneratorService; @Mock - private AuthenticatedUser authenticatedUser; + private VideoUser authenticatedUser; @Before public void setup() { @@ -122,4 +122,28 @@ public void generateModeratorToken_Should_returnExpectedToken_When_ParamsAreGive assertThat(JWT.decode(moderatorToken).getClaim("guestVideoCallUrl").asString(), is(GUEST_VIDEO_CALL_URL)); } + + @Test + public void generateToken_should_generate_moderator_token_if_user_is_consultant() { + when(authenticatedUser.getUsername()).thenReturn(USERNAME); + when(authenticatedUser.isConsultant()).thenReturn(true); + + var moderatorToken = tokenGeneratorService.generateToken("privateRoom4711"); + + verifyBasicTokenFields(moderatorToken, "privateRoom4711"); + assertThat(JWT.decode(moderatorToken).getClaim("moderator").asBoolean(), is(true)); + assertThat(JWT.decode(moderatorToken).getClaim("context").asMap().get("user").toString(), + is("{name=" + USERNAME + "}")); + } + + @Test + public void generateToken_should_generate_non_moderator_token_if_user_is_no_consultant() { + when(authenticatedUser.isConsultant()).thenReturn(false); + + var moderatorToken = tokenGeneratorService.generateToken("privateRoom4711"); + + assertThat(JWT.decode(moderatorToken).getClaim("moderator").isNull(), is(true)); + assertThat(JWT.decode(moderatorToken).getClaim("context").isNull(), is(true)); + verifyBasicTokenFields(moderatorToken, "privateRoom4711"); + } } diff --git a/src/test/java/de/caritas/cob/videoservice/api/testhelper/PathConstants.java b/src/test/java/de/caritas/cob/videoservice/api/testhelper/PathConstants.java index 7aa1aa3..3eea877 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/testhelper/PathConstants.java +++ b/src/test/java/de/caritas/cob/videoservice/api/testhelper/PathConstants.java @@ -1,7 +1,11 @@ package de.caritas.cob.videoservice.api.testhelper; +import static de.caritas.cob.videoservice.api.testhelper.TestConstants.RC_CHAT_ROOM_ID; + public class PathConstants { public static final String PATH_START_VIDEO_CALL = "/videocalls/new"; public static final String PATH_REJECT_VIDEO_CALL = "/videocalls/reject"; + public static final String PATH_GET_WEB_TOKEN = "/videocalls/" + RC_CHAT_ROOM_ID + "/jwt"; + } diff --git a/src/test/java/de/caritas/cob/videoservice/api/testhelper/TestConstants.java b/src/test/java/de/caritas/cob/videoservice/api/testhelper/TestConstants.java index 4e5a3c3..3b7aefe 100644 --- a/src/test/java/de/caritas/cob/videoservice/api/testhelper/TestConstants.java +++ b/src/test/java/de/caritas/cob/videoservice/api/testhelper/TestConstants.java @@ -16,10 +16,11 @@ public class TestConstants { public static final String BEARER_TOKEN = "w948hisidfgjaoidg839huishdfkjsdkfjhsdf34"; /* - * RC Header + * RC */ public static final String RC_USER_ID_HEADER = "RCUserId"; public static final String RC_USER_ID_VALUE = "rcUser123"; + public static final String RC_CHAT_ROOM_ID = "R_ID_76543210"; /* * CSRF token @@ -52,7 +53,7 @@ public class TestConstants { public static final String CONSULTANT_ID = "fb3cbee2-c5f3-4582-a5e4-d853572e9860"; /* - * Common + * Common */ public static final String ERROR_MESSAGE = "Error message"; }