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.filters;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.charset.Charset;
24  import java.nio.charset.StandardCharsets;
25  import org.apache.commons.lang3.BooleanUtils;
26  import org.apache.commons.lang3.StringUtils;
27  import org.jsoup.Jsoup;
28  import org.jsoup.nodes.Document;
29  import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
30  import org.springframework.web.server.ServerWebExchange;
31  
32  public class LinkRewriteGatewayFilterFactory extends ModifyResponseGatewayFilterFactory {
33  
34      @Override
35      protected boolean skipCond(final ServerHttpResponseDecorator decorator) {
36          return decorator.getHeaders().getContentType() == null
37                  || !StringUtils.containsIgnoreCase(decorator.getHeaders().getContentType().toString(), "html");
38      }
39  
40      private Charset getCharset(final ServerHttpResponseDecorator decorator) {
41          return decorator.getHeaders().getContentType() != null
42                  && decorator.getHeaders().getContentType().getCharset() != null
43                  ? decorator.getHeaders().getContentType().getCharset()
44                  : StandardCharsets.UTF_8;
45      }
46  
47      private void replace(final Document doc, final String element, final String attr, final String prefix) {
48          doc.select(element).forEach(link -> {
49              String attrValue = link.attributes().get(attr);
50              if (attrValue.startsWith("/") && !attrValue.startsWith("//")) {
51                  link.attr(attr, attrValue.replace(attrValue, prefix + attrValue));
52              }
53          });
54      }
55  
56      @Override
57      protected byte[] modifyResponse(
58              final InputStream responseBody,
59              final Config config,
60              final ServerHttpResponseDecorator decorator,
61              final ServerWebExchange exchange)
62              throws IOException {
63  
64          String[] keyValue = config.getData().split(",");
65  
66          String oldBase = StringUtils.appendIfMissing(keyValue[0], "/");
67          String newBase = StringUtils.appendIfMissing(keyValue[1], "/");
68          String newBaseAsPrefix = StringUtils.removeEnd(keyValue[1], "/");
69  
70          boolean rewriterRootAttrs = true;
71          if (keyValue.length == 3) {
72              rewriterRootAttrs = BooleanUtils.toBoolean(keyValue[2]);
73          }
74  
75          Document doc = Jsoup.parse(
76                  responseBody, getCharset(decorator).name(), exchange.getRequest().getURI().toASCIIString());
77  
78          if (rewriterRootAttrs) {
79              replace(doc, "a", "href", newBaseAsPrefix);
80              replace(doc, "link", "href", newBaseAsPrefix);
81              replace(doc, "img", "src", newBaseAsPrefix);
82              replace(doc, "script", "src", newBaseAsPrefix);
83              replace(doc, "object", "data", newBaseAsPrefix);
84          }
85  
86          return doc.toString().replace(oldBase, newBase).getBytes();
87      }
88  }