Skip to content

Commit 6686bb0

Browse files
committed
JAVA-46416: Changes made for restoring soap-keycloak code
1 parent 148c660 commit 6686bb0

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package com.baeldung.keycloaksoap;
2+
3+
import com.baeldung.keycloak.keycloaksoap.KeycloakSoapServicesApplication;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.beans.factory.annotation.Value;
11+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
12+
import org.springframework.boot.test.context.SpringBootTest;
13+
import org.springframework.boot.test.web.client.TestRestTemplate;
14+
import org.springframework.boot.test.web.server.LocalServerPort;
15+
import org.springframework.http.HttpEntity;
16+
import org.springframework.http.HttpHeaders;
17+
import org.springframework.http.HttpMethod;
18+
import org.springframework.http.HttpStatus;
19+
import org.springframework.http.MediaType;
20+
import org.springframework.http.ResponseEntity;
21+
import org.springframework.test.context.ActiveProfiles;
22+
import org.springframework.util.LinkedMultiValueMap;
23+
import org.springframework.util.MultiValueMap;
24+
25+
import java.util.Objects;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* The class contains Live tests.
31+
* These tests expect that the Keycloak server is up and running on port 8080.
32+
*/
33+
@DisplayName("Keycloak SOAP Webservice Live Tests")
34+
@SpringBootTest(classes = KeycloakSoapServicesApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
35+
@ActiveProfiles("test")
36+
@AutoConfigureMockMvc
37+
class KeycloakSoapLiveTest {
38+
39+
private static final Logger logger = LoggerFactory.getLogger(KeycloakSoapLiveTest.class);
40+
41+
@LocalServerPort
42+
private int port;
43+
44+
@Autowired
45+
private TestRestTemplate restTemplate;
46+
47+
@Autowired
48+
private ObjectMapper objectMapper;
49+
50+
@Value("${grant.type}")
51+
private String grantType;
52+
53+
@Value("${client.id}")
54+
private String clientId;
55+
56+
@Value("${client.secret}")
57+
private String clientSecret;
58+
59+
@Value("${url}")
60+
private String keycloakUrl;
61+
62+
/**
63+
* Test a happy flow. Test the <i>janedoe</i> user.
64+
* This user should be configured in Keycloak server with a role <i>user</i>
65+
*/
66+
@Test
67+
@DisplayName("Get Products With Access Token")
68+
void givenAccessToken_whenGetProducts_thenReturnProduct() {
69+
70+
HttpHeaders headers = new HttpHeaders();
71+
headers.set("content-type", "text/xml");
72+
headers.set("Authorization", "Bearer " + generateToken("janedoe", "password"));
73+
HttpEntity<String> request = new HttpEntity<>(Utility.getGetProductDetailsRequest(), headers);
74+
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class);
75+
76+
assertThat(responseEntity).isNotNull();
77+
assertThat(responseEntity.getStatusCode().value()).isEqualTo(HttpStatus.OK.value());
78+
assertThat(responseEntity.getBody()).isNotBlank();
79+
assertThat(responseEntity.getBody()).containsIgnoringCase(":id>1</");
80+
}
81+
82+
/**
83+
* A negative test. Deliberately pass wrong credentials to Keycloak. Test the invalid <i>janeadoe</i> user.
84+
* Keycloak returns Unauthorized. Assert 401 status and empty body.
85+
*/
86+
@Test
87+
@DisplayName("Get Products With Wrong Access Token")
88+
void givenWrongAccessToken_whenGetProducts_thenReturnError() {
89+
90+
HttpHeaders headers = new HttpHeaders();
91+
headers.set("content-type", "text/xml");
92+
headers.set("Authorization", "Bearer " + generateToken("janeadoe", "password"));
93+
HttpEntity<String> request = new HttpEntity<>(Utility.getGetProductDetailsRequest(), headers);
94+
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class);
95+
assertThat(responseEntity).isNotNull();
96+
assertThat(responseEntity.getStatusCode().value()).isEqualTo(HttpStatus.UNAUTHORIZED.value());
97+
assertThat(responseEntity.getBody()).isBlank();
98+
}
99+
100+
/**
101+
* Happy flow to test <i>deleteProduct</i> operation. Test the <i>jhondoe</i> user.
102+
* This user should be configured in Keycloak server with a role <i>user</i>
103+
*/
104+
@Test
105+
@DisplayName("Delete Product With Access Token")
106+
void givenAccessToken_whenDeleteProduct_thenReturnSuccess() {
107+
HttpHeaders headers = new HttpHeaders();
108+
headers.set("content-type", "text/xml");
109+
headers.set("Authorization", "Bearer " + generateToken("jhondoe", "password"));
110+
HttpEntity<String> request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers);
111+
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class);
112+
113+
assertThat(responseEntity).isNotNull();
114+
assertThat(responseEntity.getStatusCode().value()).isEqualTo(HttpStatus.OK.value());
115+
assertThat(responseEntity.getBody()).isNotBlank();
116+
assertThat(responseEntity.getBody()).containsIgnoringCase("Deleted the product with the id");
117+
}
118+
119+
/**
120+
* Negative flow to test <i></i>. Test the <i>janedoe</i> user.
121+
* Obtain the access token of <i>janedoe</i> and access the admin operation <i>deleteProduct</i>
122+
* Assume <i>janedoe</i> has restricted access to <i>deleteProduct</i> operation
123+
*/
124+
@Test
125+
@DisplayName("Delete Products With Unauthorized Access Token")
126+
void givenUnauthorizedAccessToken_whenDeleteProduct_thenReturnUnauthorized() {
127+
HttpHeaders headers = new HttpHeaders();
128+
headers.set("content-type", "text/xml");
129+
headers.set("Authorization", "Bearer " + generateToken("johndoe", "password"));
130+
HttpEntity<String> request = new HttpEntity<>(Utility.getDeleteProductsRequest(), headers);
131+
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://localhost:" + port + "/ws/api/v1/", request, String.class);
132+
133+
assertThat(responseEntity).isNotNull();
134+
assertThat(responseEntity.getStatusCode().value()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.value());
135+
assertThat(responseEntity.getBody()).isNotBlank();
136+
assertThat(responseEntity.getBody()).containsIgnoringCase("Access Denied");
137+
}
138+
139+
private String generateToken(String username, String password) {
140+
141+
try {
142+
HttpHeaders headers = new HttpHeaders();
143+
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
144+
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
145+
map.add("grant_type", grantType);
146+
map.add("client_id", clientId);
147+
map.add("client_secret", clientSecret);
148+
map.add("username", username);
149+
map.add("password", password);
150+
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
151+
ResponseEntity<String> response = restTemplate.exchange(keycloakUrl, HttpMethod.POST, entity, String.class);
152+
return Objects.requireNonNull(response.getBody()).contains("access_token") ? objectMapper.readTree(response.getBody()).get("access_token").asText() : "";
153+
} catch (Exception ex) {
154+
logger.error("There is an internal server error. Returning an empty access token", ex);
155+
return "";
156+
}
157+
158+
}
159+
160+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.baeldung.keycloaksoap;
2+
3+
public class Utility {
4+
public static String getGetProductDetailsRequest() {
5+
return "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:key=\"http://www.baeldung.com/springbootsoap/keycloak\">\n" + " <soapenv:Header/>\n" + " <soapenv:Body>\n" + " <key:getProductDetailsRequest>\n"
6+
+ " <key:id>1</key:id>\n" + " </key:getProductDetailsRequest>\n" + " </soapenv:Body>\n" + "</soapenv:Envelope>";
7+
}
8+
public static String getDeleteProductsRequest() {
9+
return "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:key=\"http://www.baeldung.com/springbootsoap/keycloak\">\n" + " <soapenv:Header/>\n" + " <soapenv:Body>\n" + " <key:deleteProductRequest>\n"
10+
+ " <key:id>1</key:id>\n" + " </key:deleteProductRequest>\n" + " </soapenv:Body>\n" + "</soapenv:Envelope>";
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
grant.type=password
2+
client.id=baeldung-soap-services
3+
client.secret=d2ba7af8-f7d2-4c97-b4a5-3c88b59920ae
4+
logging.level.org.springframework.security: DEBUG
5+
6+
url=http://localhost:8080/realms/baeldung-soap-services/protocol/openid-connect/token
7+
8+
keycloak.enabled=true
9+
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/baeldung-soap-services

0 commit comments

Comments
 (0)