package com.baeldung.keycloaksoap; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; /** * The class contains Live tests. * These tests expect that the Keycloak server is up and running on port 8080. */ @DisplayName("Keycloak SOAP Webservice Live Tests") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") @AutoConfigureMockMvc class KeycloakSoapLiveTest { private static final Logger logger = LoggerFactory.getLogger(KeycloakSoapLiveTest.class); @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; @Value("${grant.type}") private String grantType; @Value("${client.id}") private String clientId; @Value("${client.secret}") private String clientSecret; @Value("${url}") private String keycloakUrl; /** * Test a happy flow. Test the janedoe user. * This user should be configured in Keycloak server with a role user */ @Test @DisplayName("Get Products With Access Token") void givenAccessToken_whenGetProducts_thenReturnProduct() { HttpHeaders headers = new HttpHeaders(); headers.set("content-type", "text/xml"); headers.set("Authorization", "Bearer " + generateToken("janedoe", "password")); HttpEntity request = new HttpEntity<>(Utility.getGetProductDetailsRequest(), headers); ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class); assertThat(responseEntity).isNotNull(); assertThat(responseEntity.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value()); assertThat(responseEntity.getBody()).isNotBlank(); assertThat(responseEntity.getBody()).containsIgnoringCase(":id>1janeadoe user. * Keycloak returns Unauthorized. Assert 401 status and empty body. */ @Test @DisplayName("Get Products With Wrong Access Token") void givenWrongAccessToken_whenGetProducts_thenReturnError() { HttpHeaders headers = new HttpHeaders(); headers.set("content-type", "text/xml"); headers.set("Authorization", "Bearer " + generateToken("janeadoe", "password")); HttpEntity request = new HttpEntity<>(Utility.getGetProductDetailsRequest(), headers); ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class); assertThat(responseEntity).isNotNull(); assertThat(responseEntity.getStatusCodeValue()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); assertThat(responseEntity.getBody()).isBlank(); } /** * Happy flow to test deleteProduct operation. Test the jhondoe user. * This user should be configured in Keycloak server with a role user */ @Test @DisplayName("Delete Product With Access Token") void givenAccessToken_whenDeleteProduct_thenReturnSuccess() { HttpHeaders headers = new HttpHeaders(); headers.set("content-type", "text/xml"); headers.set("Authorization", "Bearer " + generateToken("johndoe", "password")); HttpEntity request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers); ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class); assertThat(responseEntity).isNotNull(); assertThat(responseEntity.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value()); assertThat(responseEntity.getBody()).isNotBlank(); assertThat(responseEntity.getBody()).containsIgnoringCase("Deleted the product with the id"); } /** * Negative flow to test . Test the janedoe user. * Obtain the access token of janedoe and access the admin operation deleteProduct * Assume janedoe has restricted access to deleteProduct operation */ @Test @DisplayName("Delete Products With Unauthorized Access Token") void givenUnauthorizedAccessToken_whenDeleteProduct_thenReturnUnauthorized() { HttpHeaders headers = new HttpHeaders(); headers.set("content-type", "text/xml"); headers.set("Authorization", "Bearer " + generateToken("janedoe", "password")); HttpEntity request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers); ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class); assertThat(responseEntity).isNotNull(); assertThat(responseEntity.getStatusCodeValue()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.value()); assertThat(responseEntity.getBody()).isNotBlank(); assertThat(responseEntity.getBody()).containsIgnoringCase("Access is denied"); } private String generateToken(String username, String password) { try { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); map.add("grant_type", grantType); map.add("client_id", clientId); map.add("client_secret", clientSecret); map.add("username", username); map.add("password", password); HttpEntity> entity = new HttpEntity<>(map, headers); ResponseEntity response = restTemplate.exchange(keycloakUrl, HttpMethod.POST, entity, String.class); return Objects.requireNonNull(response.getBody()).contains("access_token") ? objectMapper.readTree(response.getBody()).get("access_token").asText() : ""; } catch (Exception ex) { logger.error("There is an internal server error. Returning an empty access token", ex); return ""; } } }