View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.sra;
20  
21  import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
22  import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
23  import static com.github.tomakehurst.wiremock.client.WireMock.get;
24  import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
25  import static com.github.tomakehurst.wiremock.client.WireMock.post;
26  import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
27  import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
28  import static com.github.tomakehurst.wiremock.client.WireMock.verify;
29  import static org.junit.jupiter.api.Assertions.assertTrue;
30  import static org.junit.jupiter.api.Assertions.fail;
31  import static org.mockito.ArgumentMatchers.anyString;
32  import static org.mockito.ArgumentMatchers.eq;
33  import static org.mockito.Mockito.mock;
34  import static org.mockito.Mockito.when;
35  
36  import com.fasterxml.jackson.databind.JsonNode;
37  import java.io.IOException;
38  import java.net.URI;
39  import java.time.ZonedDateTime;
40  import org.apache.syncope.common.lib.to.SRARouteTO;
41  import org.apache.syncope.common.lib.types.SRARouteFilter;
42  import org.apache.syncope.common.lib.types.SRARouteFilterFactory;
43  import org.apache.syncope.common.lib.types.SRARoutePredicate;
44  import org.apache.syncope.common.lib.types.SRARoutePredicateCond;
45  import org.apache.syncope.common.lib.types.SRARoutePredicateFactory;
46  import org.apache.syncope.common.lib.types.SRARouteType;
47  import org.apache.syncope.sra.filters.BodyPropertyAddingGatewayFilterFactory;
48  import org.apache.syncope.sra.filters.PrincipalToRequestHeaderFilterFactory;
49  import org.apache.syncope.sra.predicates.BodyPropertyMatchingRoutePredicateFactory;
50  import org.junit.jupiter.api.BeforeEach;
51  import org.junit.jupiter.api.Test;
52  import org.springframework.beans.factory.annotation.Autowired;
53  import org.springframework.cache.Cache;
54  import org.springframework.cache.CacheManager;
55  import org.springframework.http.HttpHeaders;
56  import org.springframework.http.MediaType;
57  import org.springframework.security.core.Authentication;
58  import org.springframework.security.core.context.SecurityContextImpl;
59  import org.springframework.security.oauth2.core.oidc.OidcIdToken;
60  import org.springframework.security.oauth2.core.oidc.user.OidcUser;
61  import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
62  import org.springframework.session.MapSession;
63  import org.springframework.session.Session;
64  import org.springframework.test.util.ReflectionTestUtils;
65  import org.springframework.test.web.reactive.server.WebTestClient;
66  import org.springframework.web.reactive.function.BodyInserters;
67  
68  public class RouteProviderTest extends AbstractTest {
69  
70      @Autowired
71      private WebTestClient webClient;
72  
73      @BeforeEach
74      public void clearRoutes() {
75          SyncopeCoreTestingServer.ROUTES.clear();
76      }
77  
78      @Test
79      public void root() {
80          webClient.get().exchange().expectStatus().isNotFound();
81      }
82  
83      @Test
84      public void addResponseHeader() {
85          // 1. no mapping for URL
86          webClient.get().uri("/addResponseHeader").exchange().expectStatus().isNotFound();
87  
88          // 2. stub for proxied URL
89          stubFor(get(urlEqualTo("/addResponseHeader")).willReturn(aResponse()));
90  
91          // 3. create route configuration
92          SRARouteTO route = new SRARouteTO();
93          route.setKey("addResponseHeader");
94          route.setTarget(URI.create("http://localhost:" + wiremockPort));
95          route.getPredicates().add(new SRARoutePredicate.Builder().
96                  factory(SRARoutePredicateFactory.METHOD).args("GET").build());
97          route.getPredicates().add(new SRARoutePredicate.Builder().
98                  factory(SRARoutePredicateFactory.PATH).args("/addResponseHeader").cond(SRARoutePredicateCond.AND).
99                  build());
100         route.getFilters().add(new SRARouteFilter.Builder().
101                 factory(SRARouteFilterFactory.ADD_RESPONSE_HEADER).args("Hello,World").build());
102 
103         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
104         routeRefresher.refresh();
105 
106         // 4. now mapping works for URL
107         webClient.get().uri("/addResponseHeader").exchange().
108                 expectStatus().isOk().
109                 expectHeader().valueEquals("Hello", "World");
110 
111         // 5. update route configuration
112         route.getFilters().clear();
113         route.getFilters().add(new SRARouteFilter.Builder().
114                 factory(SRARouteFilterFactory.ADD_RESPONSE_HEADER).args("Hello,WorldZ").build());
115 
116         routeRefresher.refresh();
117 
118         // 6. mapping for URL is updated too
119         webClient.get().uri("/addResponseHeader").exchange().
120                 expectStatus().isOk().
121                 expectHeader().valueEquals("Hello", "WorldZ");
122 
123         // 7. update route configuration again
124         route.getFilters().clear();
125 
126         routeRefresher.refresh();
127 
128         // 8. mapping for URL is updated again
129         webClient.get().uri("/addResponseHeader").exchange().
130                 expectStatus().isOk().
131                 expectHeader().doesNotExist("Hello");
132     }
133 
134     @Test
135     public void addRequestHeader() {
136         webClient.get().uri("/requestHeader").exchange().expectStatus().isNotFound();
137 
138         stubFor(get(urlEqualTo("/requestHeader")).withHeader("Hello", equalTo("World")).willReturn(aResponse()));
139 
140         SRARouteTO route = new SRARouteTO();
141         route.setKey("requestHeader");
142         route.setTarget(URI.create("http://localhost:" + wiremockPort));
143         route.getPredicates().add(new SRARoutePredicate.Builder().
144                 factory(SRARoutePredicateFactory.REMOTE_ADDR).args("localhost").build());
145         route.getFilters().add(new SRARouteFilter.Builder().
146                 factory(SRARouteFilterFactory.ADD_REQUEST_HEADER).args("Hello,World").build());
147 
148         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
149         routeRefresher.refresh();
150 
151         webClient.get().uri("/requestHeader").exchange().expectStatus().isOk();
152 
153         route.getFilters().clear();
154         route.getFilters().add(new SRARouteFilter.Builder().
155                 factory(SRARouteFilterFactory.ADD_REQUEST_HEADER).args("Hello,Mondo").build());
156 
157         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
158         routeRefresher.refresh();
159 
160         webClient.get().uri("/requestHeader").exchange().expectStatus().isNotFound();
161 
162         route.getFilters().clear();
163         route.getFilters().add(new SRARouteFilter.Builder().
164                 factory(SRARouteFilterFactory.REMOVE_REQUEST_HEADER).args("Hello").build());
165 
166         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
167         routeRefresher.refresh();
168 
169         webClient.get().uri("/requestHeader").header("Hello", "World").exchange().expectStatus().isNotFound();
170 
171         route.getFilters().clear();
172         route.getFilters().add(new SRARouteFilter.Builder().
173                 factory(SRARouteFilterFactory.SET_REQUEST_HEADER).args("Hello, World").build());
174 
175         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
176         routeRefresher.refresh();
177 
178         webClient.get().uri("/requestHeader").header("Hello", "Mondo").exchange().expectStatus().isOk();
179     }
180 
181     @Test
182     public void requestHeaderToRequestUri() {
183         webClient.get().uri("/requestHeaderToRequestUri").exchange().expectStatus().isNotFound();
184 
185         stubFor(get(urlEqualTo("/requestHeaderToRequestUri")).willReturn(aResponse()));
186 
187         SRARouteTO route = new SRARouteTO();
188         route.setKey("requestHeaderToRequestUri");
189         route.setTarget(URI.create("http://localhost:" + wiremockPort));
190         route.getFilters().add(new SRARouteFilter.Builder().
191                 factory(SRARouteFilterFactory.REQUEST_HEADER_TO_REQUEST_URI).args("NewUri").build());
192 
193         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
194         routeRefresher.refresh();
195 
196         webClient.get().uri("/requestHeaderToRequestUri").
197                 header("NewUri", "http://localhost:" + wiremockPort + "/requestHeaderToRequestUri").
198                 exchange().expectStatus().isOk();
199     }
200 
201     @Test
202     public void responseHeader() {
203         webClient.get().uri("/responseHeader").exchange().expectStatus().isNotFound();
204 
205         stubFor(get(urlEqualTo("/responseHeader")).willReturn(aResponse().withHeader("Hello", "World")));
206 
207         SRARouteTO route = new SRARouteTO();
208         route.setKey("responseHeader");
209         route.setTarget(URI.create("http://localhost:" + wiremockPort));
210         route.getFilters().add(new SRARouteFilter.Builder().
211                 factory(SRARouteFilterFactory.REMOVE_RESPONSE_HEADER).args("Hello").build());
212 
213         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
214         routeRefresher.refresh();
215 
216         webClient.get().uri("/responseHeader").exchange().
217                 expectStatus().isOk().
218                 expectHeader().doesNotExist("Hello");
219 
220         route.getFilters().clear();
221 
222         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
223         routeRefresher.refresh();
224 
225         webClient.get().uri("/responseHeader").exchange().
226                 expectStatus().isOk().
227                 expectHeader().valueEquals("Hello", "World");
228 
229         route.getFilters().clear();
230         route.getFilters().add(new SRARouteFilter.Builder().
231                 factory(SRARouteFilterFactory.REWRITE_RESPONSE_HEADER).args("Hello,World,Mondo").build());
232         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
233         routeRefresher.refresh();
234 
235         webClient.get().uri("/responseHeader").exchange().
236                 expectStatus().isOk().
237                 expectHeader().valueEquals("Hello", "Mondo");
238 
239         route.getFilters().clear();
240         route.getFilters().add(new SRARouteFilter.Builder().
241                 factory(SRARouteFilterFactory.SET_RESPONSE_HEADER).args("Hello,Mondo").build());
242         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
243         routeRefresher.refresh();
244 
245         webClient.get().uri("/responseHeader").exchange().
246                 expectStatus().isOk().
247                 expectHeader().valueEquals("Hello", "Mondo");
248     }
249 
250     @Test
251     public void addRequestParameter() {
252         webClient.get().uri("/addRequestParameter?Hello=World").exchange().expectStatus().isNotFound();
253 
254         stubFor(get(urlEqualTo("/addRequestParameter?Hello=World")).withQueryParam("Hello", equalTo("World")).
255                 willReturn(aResponse()));
256 
257         SRARouteTO route = new SRARouteTO();
258         route.setKey("addRequestParameter");
259         route.setTarget(URI.create("http://localhost:" + wiremockPort));
260         route.getFilters().add(new SRARouteFilter.Builder().
261                 factory(SRARouteFilterFactory.ADD_REQUEST_PARAMETER).args("Hello,World").build());
262 
263         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
264         routeRefresher.refresh();
265 
266         webClient.get().uri("/addRequestParameter").exchange().expectStatus().isOk();
267 
268         route.getFilters().clear();
269         route.getFilters().add(new SRARouteFilter.Builder().
270                 factory(SRARouteFilterFactory.ADD_REQUEST_PARAMETER).args("Hello,Mondo").build());
271 
272         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
273         routeRefresher.refresh();
274 
275         webClient.get().uri("/addRequestParameter").exchange().expectStatus().isNotFound();
276     }
277 
278     @Test
279     public void rewritePath() {
280         webClient.get().uri("/rewrite").exchange().expectStatus().isNotFound();
281 
282         stubFor(get(urlEqualTo("/rewrite")).willReturn(aResponse()));
283 
284         SRARouteTO route = new SRARouteTO();
285         route.setKey("rewrite");
286         route.setTarget(URI.create("http://localhost:" + wiremockPort));
287         route.getFilters().add(new SRARouteFilter.Builder().
288                 factory(SRARouteFilterFactory.REWRITE_PATH).args("/remove/(?<segment>.*), /${segment}").build());
289         route.getFilters().add(new SRARouteFilter.Builder().
290                 factory(SRARouteFilterFactory.SECURE_HEADERS).build());
291 
292         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
293         routeRefresher.refresh();
294 
295         webClient.get().uri("/remove/rewrite").exchange().
296                 expectStatus().isOk().
297                 expectHeader().valueEquals("X-XSS-Protection", "1 ; mode=block");
298 
299         route.getFilters().clear();
300 
301         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
302         routeRefresher.refresh();
303 
304         webClient.get().uri("/remove/rewrite").exchange().
305                 expectStatus().isNotFound();
306 
307         route.getFilters().clear();
308 
309         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
310         routeRefresher.refresh();
311 
312         webClient.get().uri("/rewrite").exchange().
313                 expectStatus().isOk().
314                 expectHeader().doesNotExist("X-XSS-Protection");
315 
316         route.getFilters().clear();
317         route.getPredicates().add(new SRARoutePredicate.Builder().
318                 factory(SRARoutePredicateFactory.PATH).args("/remove/{segment}").build());
319         route.getFilters().add(new SRARouteFilter.Builder().
320                 factory(SRARouteFilterFactory.SET_PATH).args("/{segment}").build());
321 
322         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
323         routeRefresher.refresh();
324 
325         webClient.get().uri("/remove/rewrite").exchange().
326                 expectStatus().isOk();
327 
328         route.getFilters().clear();
329         route.getFilters().add(new SRARouteFilter.Builder().
330                 factory(SRARouteFilterFactory.STRIP_PREFIX).args("1").build());
331 
332         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
333         routeRefresher.refresh();
334 
335         webClient.get().uri("/remove/rewrite").exchange().expectStatus().isOk();
336     }
337 
338     @Test
339     public void redirect() {
340         webClient.get().uri("/redirect").exchange().expectStatus().isNotFound();
341 
342         stubFor(get(urlEqualTo("/redirect")).willReturn(aResponse()));
343 
344         SRARouteTO route = new SRARouteTO();
345         route.setKey("redirect");
346         route.setTarget(URI.create("http://localhost:" + wiremockPort));
347         route.getFilters().add(new SRARouteFilter.Builder().
348                 factory(SRARouteFilterFactory.REDIRECT_TO).args("307,http://127.0.0.1:" + wiremockPort).build());
349 
350         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
351         routeRefresher.refresh();
352 
353         webClient.get().uri("/redirect").exchange().expectStatus().isTemporaryRedirect();
354 
355         route.getFilters().clear();
356 
357         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
358         routeRefresher.refresh();
359 
360         webClient.get().uri("/redirect").exchange().expectStatus().isOk();
361 
362         route.getFilters().clear();
363         route.getFilters().add(new SRARouteFilter.Builder().factory(SRARouteFilterFactory.SET_STATUS).args("404").
364                 build());
365 
366         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
367         routeRefresher.refresh();
368 
369         webClient.get().uri("/redirect").exchange().expectStatus().isNotFound();
370     }
371 
372     @Test
373     public void datetime() {
374         webClient.get().uri("/prefix/datetime").exchange().expectStatus().isNotFound();
375 
376         stubFor(get(urlEqualTo("/prefix/datetime")).willReturn(aResponse()));
377 
378         SRARouteTO route = new SRARouteTO();
379         route.setKey("datetime");
380         route.setTarget(URI.create("http://localhost:" + wiremockPort));
381         route.getPredicates().add(new SRARoutePredicate.Builder().
382                 factory(SRARoutePredicateFactory.AFTER).args(ZonedDateTime.now().minusYears(1).toString()).build());
383         route.getPredicates().add(new SRARoutePredicate.Builder().
384                 factory(SRARoutePredicateFactory.BEFORE).args(ZonedDateTime.now().plusYears(1).toString()).
385                 cond(SRARoutePredicateCond.AND).build());
386         route.getPredicates().add(new SRARoutePredicate.Builder().
387                 factory(SRARoutePredicateFactory.BETWEEN).args(ZonedDateTime.now().minusYears(1).toString() + ","
388                 + ZonedDateTime.now().plusYears(1).toString()).
389                 cond(SRARoutePredicateCond.AND).build());
390         route.getFilters().add(new SRARouteFilter.Builder().
391                 factory(SRARouteFilterFactory.PREFIX_PATH).args("/prefix").build());
392 
393         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
394         routeRefresher.refresh();
395 
396         webClient.get().uri("/datetime").exchange().
397                 expectStatus().isOk();
398 
399         route.getPredicates().clear();
400         route.getPredicates().add(new SRARoutePredicate.Builder().
401                 factory(SRARoutePredicateFactory.AFTER).args(ZonedDateTime.now().plusYears(1).toString()).build());
402         route.getPredicates().add(new SRARoutePredicate.Builder().
403                 factory(SRARoutePredicateFactory.BEFORE).args(ZonedDateTime.now().minusYears(1).toString()).
404                 cond(SRARoutePredicateCond.OR).build());
405         route.getPredicates().add(new SRARoutePredicate.Builder().
406                 factory(SRARoutePredicateFactory.BETWEEN).args(ZonedDateTime.now().plusYears(1).toString() + ","
407                 + ZonedDateTime.now().minusYears(1).toString()).cond(SRARoutePredicateCond.OR).build());
408 
409         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
410         routeRefresher.refresh();
411 
412         webClient.get().uri("/datetime").exchange().expectStatus().isNotFound();
413 
414         route.getPredicates().clear();
415         route.getPredicates().add(new SRARoutePredicate.Builder().
416                 factory(SRARoutePredicateFactory.BEFORE).negate().args(ZonedDateTime.now().minusYears(1).toString()).
417                 build());
418 
419         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
420         routeRefresher.refresh();
421 
422         webClient.get().uri("/datetime").exchange().expectStatus().isOk();
423     }
424 
425     @Test
426     public void header() {
427         webClient.get().uri("/header").exchange().expectStatus().isNotFound();
428 
429         stubFor(get(urlEqualTo("/header")).willReturn(aResponse()));
430 
431         SRARouteTO route = new SRARouteTO();
432         route.setKey("header");
433         route.setTarget(URI.create("http://localhost:" + wiremockPort));
434         route.getPredicates().add(new SRARoutePredicate.Builder().
435                 factory(SRARoutePredicateFactory.COOKIE).args("Hello,World").build());
436         route.getPredicates().add(new SRARoutePredicate.Builder().
437                 factory(SRARoutePredicateFactory.HOST).args("host").cond(SRARoutePredicateCond.AND).build());
438         route.getPredicates().add(new SRARoutePredicate.Builder().
439                 factory(SRARoutePredicateFactory.HEADER).args("Hello,World").cond(SRARoutePredicateCond.AND).build());
440         route.getFilters().add(new SRARouteFilter.Builder().
441                 factory(SRARouteFilterFactory.PRESERVE_HOST_HEADER).build());
442 
443         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
444         routeRefresher.refresh();
445 
446         webClient.get().uri("/header").cookie("Hello", "World").header("Host", "host").header("Hello", "World").
447                 exchange().expectStatus().isOk();
448 
449         webClient.get().uri("/header").cookie("Hello", "Mondo").header("Host", "host").header("Hello", "World").
450                 exchange().expectStatus().isNotFound();
451 
452         webClient.get().uri("/header").cookie("Hello", "World").header("Host", "anotherHost").header("Hello", "World").
453                 exchange().expectStatus().isNotFound();
454 
455         webClient.get().uri("/header").cookie("Hello", "World").header("Host", "host").header("Hello", "Mondo").
456                 exchange().expectStatus().isNotFound();
457     }
458 
459     @Test
460     public void query() {
461         stubFor(get(urlEqualTo("/query?name=value")).willReturn(aResponse()));
462 
463         SRARouteTO route = new SRARouteTO();
464         route.setKey("query");
465         route.setTarget(URI.create("http://localhost:" + wiremockPort));
466         route.getPredicates().add(new SRARoutePredicate.Builder().
467                 factory(SRARoutePredicateFactory.QUERY).args("name,value").build());
468         route.getFilters().add(new SRARouteFilter.Builder().
469                 factory(SRARouteFilterFactory.SAVE_SESSION).build());
470         route.getFilters().add(new SRARouteFilter.Builder().
471                 factory(SRARouteFilterFactory.SET_REQUEST_SIZE).args("5000").build());
472         route.getFilters().add(new SRARouteFilter.Builder().
473                 factory(SRARouteFilterFactory.RETRY).args("3").build());
474 
475         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
476         routeRefresher.refresh();
477 
478         webClient.get().uri("/query?name=value").exchange().expectStatus().isOk();
479 
480         route.getPredicates().clear();
481         route.getPredicates().add(new SRARoutePredicate.Builder().
482                 factory(SRARoutePredicateFactory.QUERY).args("name,anotherValue").build());
483 
484         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
485         routeRefresher.refresh();
486 
487         webClient.get().uri("/query?name=value").exchange().expectStatus().isNotFound();
488     }
489 
490     @Test
491     public void path() {
492         stubFor(get(urlEqualTo("/pathMatcher/1")).willReturn(aResponse()));
493         stubFor(get(urlEqualTo("/pathMatcher/2")).willReturn(aResponse()));
494         stubFor(get(urlEqualTo("/pathMatcher/2/3")).willReturn(aResponse()));
495 
496         SRARouteTO route = new SRARouteTO();
497         route.setKey("pathMatcher");
498         route.setTarget(URI.create("http://localhost:" + wiremockPort));
499         route.getPredicates().add(new SRARoutePredicate.Builder().
500                 factory(SRARoutePredicateFactory.PATH).args("/pathMatcher/**").build());
501 
502         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
503         routeRefresher.refresh();
504 
505         webClient.get().uri("/pathMatcher/1").exchange().expectStatus().isOk();
506         webClient.get().uri("/pathMatcher/2").exchange().expectStatus().isOk();
507         webClient.get().uri("/pathMatcher/2/3").exchange().expectStatus().isOk();
508         webClient.get().uri("/pathMatcher/4").exchange().expectStatus().isNotFound();
509     }
510 
511     @Test
512     public void linkRewrite() {
513         stubFor(get(urlEqualTo("/linkRewrite")).willReturn(aResponse().
514                 withHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_VALUE).
515                 withBody("<html><head></head><body><a href=\"/absolute\">absolute link</a></body></html>")));
516 
517         SRARouteTO route = new SRARouteTO();
518         route.setKey("linkRewrite");
519         route.setTarget(URI.create("http://localhost:" + wiremockPort));
520         route.getFilters().add(new SRARouteFilter.Builder().factory(SRARouteFilterFactory.LINK_REWRITE).
521                 args("http://localhost:" + sraPort).build());
522 
523         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
524         routeRefresher.refresh();
525 
526         webClient.get().uri("/linkRewrite").exchange().
527                 expectStatus().isOk().
528                 expectBody().consumeWith(exchange -> {
529                     assertTrue(new String(exchange.getResponseBody()).
530                             contains("<a href=\"http://localhost:" + sraPort + "/absolute\">"));
531                 });
532 
533         route.getFilters().clear();
534         route.getFilters().add(new SRARouteFilter.Builder().factory(SRARouteFilterFactory.LINK_REWRITE).
535                 args("http://localhost:" + sraPort + ",true").build());
536 
537         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
538         routeRefresher.refresh();
539 
540         webClient.get().uri("/linkRewrite").exchange().
541                 expectStatus().isOk().
542                 expectBody().consumeWith(exchange -> {
543                     assertTrue(new String(exchange.getResponseBody()).
544                             contains("<a href=\"http://localhost:" + sraPort + "/absolute\">"));
545                 });
546     }
547 
548     @Test
549     public void clientCertToRequestHeader() {
550         stubFor(get(urlEqualTo("/clientCert")).willReturn(aResponse().
551                 withHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_VALUE)));
552 
553         SRARouteTO route = new SRARouteTO();
554         route.setKey("clientCert");
555         route.setTarget(URI.create("http://localhost:" + wiremockPort));
556         route.getFilters().add(new SRARouteFilter.Builder().
557                 factory(SRARouteFilterFactory.CLIENT_CERTS_TO_REQUEST_HEADER).build());
558 
559         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
560         routeRefresher.refresh();
561 
562         webClient.get().uri("/clientCert").exchange().
563                 expectStatus().isOk().
564                 expectHeader().doesNotExist("X-Client-Certificate");
565     }
566 
567     @Test
568     public void queryParamToRequestHeader() {
569         stubFor(get(urlEqualTo("/queryParamToRequestHeader")).
570                 withHeader("Hello", equalTo("World")).willReturn(aResponse()));
571 
572         stubFor(get(urlEqualTo("/queryParamToRequestHeader?Header=Test&Header=Test1")).
573                 withHeader("Hello", equalTo("World")).willReturn(aResponse()));
574 
575         SRARouteTO route = new SRARouteTO();
576         route.setKey("queryParamToRequestHeader");
577         route.setTarget(URI.create("http://localhost:" + wiremockPort));
578         route.getFilters().add(new SRARouteFilter.Builder().
579                 factory(SRARouteFilterFactory.QUERY_PARAM_TO_REQUEST_HEADER).args("Hello").build());
580 
581         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
582         routeRefresher.refresh();
583 
584         webClient.get().uri("/queryParamToRequestHeader").exchange().
585                 expectStatus().isNotFound();
586 
587         webClient.get().uri("/queryParamToRequestHeader?Hello=World").exchange().
588                 expectStatus().isOk();
589 
590         webClient.get().uri("/queryParamToRequestHeader?Header=Test&Hello=World&Header=Test1").exchange().
591                 expectStatus().isOk();
592     }
593 
594     @Test
595     public void principalToRequestHeader() throws IllegalArgumentException, IllegalAccessException {
596         // first mock...
597         OidcIdToken oidcIdToken = mock(OidcIdToken.class);
598         when(oidcIdToken.getTokenValue()).thenReturn("john.doe");
599 
600         OidcUser user = mock(OidcUser.class);
601         when(user.getIdToken()).thenReturn(oidcIdToken);
602 
603         Authentication authentication = mock(Authentication.class);
604         when(authentication.getPrincipal()).thenReturn(user);
605 
606         MapSession session = new MapSession();
607         session.setAttribute(
608                 WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME,
609                 new SecurityContextImpl(authentication));
610 
611         Cache cache = mock(Cache.class);
612         when(cache.get(anyString(), eq(Session.class))).thenReturn(session);
613 
614         CacheManager cacheManager = mock(CacheManager.class);
615         when(cacheManager.getCache(eq(SessionConfig.DEFAULT_CACHE))).thenReturn(cache);
616 
617         PrincipalToRequestHeaderFilterFactory factory = new PrincipalToRequestHeaderFilterFactory();
618         ReflectionTestUtils.setField(factory, "cacheManager", cacheManager);
619         ctx.getBeanFactory().registerSingleton(PrincipalToRequestHeaderFilterFactory.class.getName(), factory);
620 
621         // ...then test
622         stubFor(get(urlEqualTo("/principalToRequestHeader")).willReturn(aResponse()));
623 
624         SRARouteTO route = new SRARouteTO();
625         route.setKey("principalToRequestHeader");
626         route.setTarget(URI.create("http://localhost:" + wiremockPort));
627         route.setType(SRARouteType.PROTECTED);
628         route.getFilters().add(new SRARouteFilter.Builder().
629                 factory(SRARouteFilterFactory.PRINCIPAL_TO_REQUEST_HEADER).args("HTTP_REMOTE_USER").build());
630 
631         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
632         routeRefresher.refresh();
633 
634         webClient.get().uri("/principalToRequestHeader").exchange().
635                 expectStatus().isOk();
636 
637         verify(getRequestedFor(urlEqualTo("/principalToRequestHeader")).
638                 withHeader("HTTP_REMOTE_USER", equalTo("john.doe")));
639     }
640 
641     @Test
642     public void custom() {
643         stubFor(post(urlEqualTo("/custom")).
644                 willReturn(aResponse().
645                         withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).
646                         withBody("{\"data\": \"data\"}")));
647 
648         SRARouteTO route = new SRARouteTO();
649         route.setKey("custom");
650         route.setTarget(URI.create("http://localhost:" + wiremockPort));
651         route.getPredicates().add(new SRARoutePredicate.Builder().
652                 factory(SRARoutePredicateFactory.CUSTOM).
653                 args(BodyPropertyMatchingRoutePredicateFactory.class.getName() + ";cool").build());
654         route.getFilters().add(new SRARouteFilter.Builder().
655                 factory(SRARouteFilterFactory.ADD_RESPONSE_HEADER).args("Custom,matched").build());
656         route.getFilters().add(new SRARouteFilter.Builder().
657                 factory(SRARouteFilterFactory.CUSTOM).
658                 args(BodyPropertyAddingGatewayFilterFactory.class.getName() + ";customized=true").build());
659 
660         SyncopeCoreTestingServer.ROUTES.put(route.getKey(), route);
661         routeRefresher.refresh();
662 
663         webClient.post().uri("/custom").
664                 body(BodyInserters.fromValue(MAPPER.createObjectNode().put("other", true))).
665                 exchange().
666                 expectStatus().isNotFound();
667 
668         webClient.post().uri("/custom").
669                 body(BodyInserters.fromValue(MAPPER.createObjectNode().put("cool", true))).
670                 exchange().
671                 expectStatus().isOk().
672                 expectHeader().valueEquals("Custom", "matched").
673                 expectBody().
674                 consumeWith(response -> {
675                     try {
676                         JsonNode body = MAPPER.readTree(response.getResponseBody());
677                         assertTrue(body.has("customized"));
678                         assertTrue(body.get("customized").asBoolean());
679                     } catch (IOException e) {
680                         fail(e.getMessage(), e);
681                     }
682                 });
683     }
684 }