1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.fit.sra;
20
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.fail;
24 import static org.junit.jupiter.api.Assumptions.assumeTrue;
25
26 import com.fasterxml.jackson.databind.node.ObjectNode;
27 import java.io.IOException;
28 import java.lang.invoke.MethodHandles;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.concurrent.TimeoutException;
32 import javax.ws.rs.core.HttpHeaders;
33 import javax.ws.rs.core.MediaType;
34 import javax.ws.rs.core.Response;
35 import org.apache.commons.lang3.tuple.Triple;
36 import org.apache.http.Consts;
37 import org.apache.http.HttpStatus;
38 import org.apache.http.NameValuePair;
39 import org.apache.http.client.entity.UrlEncodedFormEntity;
40 import org.apache.http.client.methods.CloseableHttpResponse;
41 import org.apache.http.client.methods.HttpGet;
42 import org.apache.http.client.methods.HttpPost;
43 import org.apache.http.client.protocol.HttpClientContext;
44 import org.apache.http.impl.client.BasicCookieStore;
45 import org.apache.http.impl.client.CloseableHttpClient;
46 import org.apache.http.impl.client.HttpClients;
47 import org.apache.http.message.BasicNameValuePair;
48 import org.apache.http.util.EntityUtils;
49 import org.apache.syncope.common.lib.SyncopeConstants;
50 import org.apache.syncope.common.lib.to.SAML2SPClientAppTO;
51 import org.apache.syncope.common.lib.types.ClientAppType;
52 import org.apache.syncope.common.lib.types.SAML2SPNameId;
53 import org.apache.syncope.common.rest.api.RESTHeaders;
54 import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
55 import org.junit.jupiter.api.BeforeAll;
56 import org.junit.jupiter.api.Test;
57
58 public class SAML2SRAITCase extends AbstractSRAITCase {
59
60 @BeforeAll
61 public static void startSRA() throws IOException, InterruptedException, TimeoutException {
62 assumeTrue(SAML2SRAITCase.class.equals(MethodHandles.lookup().lookupClass()));
63
64 doStartSRA("saml2");
65 }
66
67 @BeforeAll
68 public static void clientAppSetup() {
69 String appName = SAML2SRAITCase.class.getName();
70 SAML2SPClientAppTO clientApp = CLIENT_APP_SERVICE.list(ClientAppType.SAML2SP).stream().
71 filter(app -> appName.equals(app.getName())).
72 map(SAML2SPClientAppTO.class::cast).
73 findFirst().
74 orElseGet(() -> {
75 SAML2SPClientAppTO app = new SAML2SPClientAppTO();
76 app.setName(appName);
77 app.setRealm(SyncopeConstants.ROOT_REALM);
78 app.setClientAppId(3L);
79 app.setEntityId(SRA_ADDRESS);
80 app.setMetadataLocation(SRA_ADDRESS + "/saml2/metadata");
81
82 Response response = CLIENT_APP_SERVICE.create(ClientAppType.SAML2SP, app);
83 if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
84 fail("Could not create SAML2 Client App");
85 }
86
87 return CLIENT_APP_SERVICE.read(
88 ClientAppType.SAML2SP, response.getHeaderString(RESTHeaders.RESOURCE_KEY));
89 });
90
91 clientApp.setSignAssertions(true);
92 clientApp.setSignResponses(true);
93 clientApp.setRequiredNameIdFormat(SAML2SPNameId.PERSISTENT);
94 clientApp.setAuthPolicy(getAuthPolicy().getKey());
95
96 CLIENT_APP_SERVICE.update(ClientAppType.SAML2SP, clientApp);
97 WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.clientApps, List.of());
98 }
99
100 @Test
101 public void web() throws IOException {
102 CloseableHttpClient httpclient = HttpClients.createDefault();
103 HttpClientContext context = HttpClientContext.create();
104 context.setCookieStore(new BasicCookieStore());
105
106
107 HttpGet get = new HttpGet(SRA_ADDRESS + "/public/get?" + QUERY_STRING);
108 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
109 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
110 try (CloseableHttpResponse response = httpclient.execute(get, context)) {
111 ObjectNode headers = checkGetResponse(response, get.getURI().toASCIIString().replace("/public", ""));
112 assertFalse(headers.has(HttpHeaders.COOKIE));
113 }
114
115
116 get = new HttpGet(SRA_ADDRESS + "/protected/get?" + QUERY_STRING);
117 String originalRequestURI = get.getURI().toASCIIString();
118 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
119 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
120 String responseBody;
121 try (CloseableHttpResponse response = httpclient.execute(get, context)) {
122 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
123 responseBody = EntityUtils.toString(response.getEntity());
124 }
125
126
127 Triple<String, String, String> parsed = parseSAMLRequestForm(responseBody);
128
129 HttpPost post = new HttpPost(parsed.getLeft());
130 post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
131 post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
132 post.setEntity(new UrlEncodedFormEntity(
133 List.of(new BasicNameValuePair("RelayState", parsed.getMiddle()),
134 new BasicNameValuePair("SAMLRequest", parsed.getRight())), Consts.UTF_8));
135 String location;
136 try (CloseableHttpResponse response = httpclient.execute(post, context)) {
137 assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
138 location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
139 }
140
141
142 post = new HttpPost(location);
143 post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
144 post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
145 try (CloseableHttpResponse response = httpclient.execute(post, context)) {
146 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
147 responseBody = EntityUtils.toString(response.getEntity());
148 }
149
150 boolean isOk = false;
151 try (CloseableHttpResponse response =
152 authenticateToWA("bellini", "password", responseBody, httpclient, context)) {
153
154 switch (response.getStatusLine().getStatusCode()) {
155 case HttpStatus.SC_OK:
156 isOk = true;
157 responseBody = EntityUtils.toString(response.getEntity());
158 break;
159
160 case HttpStatus.SC_MOVED_TEMPORARILY:
161 location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
162 break;
163
164 default:
165 fail();
166 }
167 }
168
169
170 if (isOk) {
171 String execution = extractWAExecution(responseBody);
172
173 List<NameValuePair> form = new ArrayList<>();
174 form.add(new BasicNameValuePair("_eventId", "confirm"));
175 form.add(new BasicNameValuePair("execution", execution));
176 form.add(new BasicNameValuePair("option", "1"));
177 form.add(new BasicNameValuePair("reminder", "30"));
178 form.add(new BasicNameValuePair("reminderTimeUnit", "days"));
179
180 post = new HttpPost(WA_ADDRESS + "/login");
181 post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
182 post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
183 post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8));
184 try (CloseableHttpResponse response = httpclient.execute(post, context)) {
185 assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
186 location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
187 }
188 }
189
190 get = new HttpGet(location);
191 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
192 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
193 try (CloseableHttpResponse response = httpclient.execute(get, context)) {
194 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
195 responseBody = EntityUtils.toString(response.getEntity());
196 }
197
198
199 parsed = parseSAMLResponseForm(responseBody);
200
201 post = new HttpPost(parsed.getLeft());
202 post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
203 post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
204 post.setEntity(new UrlEncodedFormEntity(
205 List.of(new BasicNameValuePair("RelayState", parsed.getMiddle()),
206 new BasicNameValuePair("SAMLResponse", parsed.getRight())), Consts.UTF_8));
207 try (CloseableHttpResponse response = httpclient.execute(post, context)) {
208 assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
209 location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
210 }
211
212
213 get = new HttpGet(location);
214 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
215 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
216 try (CloseableHttpResponse response = httpclient.execute(get, context)) {
217 ObjectNode headers = checkGetResponse(response, originalRequestURI.replace("/protected", ""));
218 assertFalse(headers.get(HttpHeaders.COOKIE).asText().isBlank());
219 }
220
221
222 get = new HttpGet(SRA_ADDRESS + "/protected/logout");
223 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
224 try (CloseableHttpResponse response = httpclient.execute(get, context)) {
225 responseBody = EntityUtils.toString(response.getEntity());
226 }
227
228
229 parsed = parseSAMLRequestForm(responseBody);
230
231 post = new HttpPost(parsed.getLeft());
232 post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
233 post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
234 post.setEntity(new UrlEncodedFormEntity(
235 List.of(new BasicNameValuePair("RelayState", parsed.getMiddle()),
236 new BasicNameValuePair("SAMLRequest", parsed.getRight())), Consts.UTF_8));
237 try (CloseableHttpResponse response = httpclient.execute(post, context)) {
238 assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
239 location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
240 }
241
242 get = new HttpGet(location);
243 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
244 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
245
246 try (CloseableHttpResponse response = httpclient.execute(get, context)) {
247 checkLogout(response);
248 }
249 }
250 }