Files
spring-boot-rest/spring-boot-modules/spring-boot-keycloak/src/test/java/com/baeldung/keycloaksoap/KeycloakSoapLiveTest.java

160 lines
7.0 KiB
Java

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 <i>janedoe</i> user.
* This user should be configured in Keycloak server with a role <i>user</i>
*/
@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<String> request = new HttpEntity<>(Utility.getGetProductDetailsRequest(), headers);
ResponseEntity<String> 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>1</");
}
/**
* A negative test. Deliberately pass wrong credentials to Keycloak. Test the invalid <i>janeadoe</i> 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<String> request = new HttpEntity<>(Utility.getGetProductDetailsRequest(), headers);
ResponseEntity<String> 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 <i>deleteProduct</i> operation. Test the <i>jhondoe</i> user.
* This user should be configured in Keycloak server with a role <i>user</i>
*/
@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<String> request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers);
ResponseEntity<String> 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 <i></i>. Test the <i>janedoe</i> user.
* Obtain the access token of <i>janedoe</i> and access the admin operation <i>deleteProduct</i>
* Assume <i>janedoe</i> has restricted access to <i>deleteProduct</i> 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<String> request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers);
ResponseEntity<String> 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<String, String> 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<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
ResponseEntity<String> 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 "";
}
}
}