diff --git a/src/main/java/org/rundeck/client/Rundeck.java b/src/main/java/org/rundeck/client/Rundeck.java index b1f6878a..de67c7ef 100644 --- a/src/main/java/org/rundeck/client/Rundeck.java +++ b/src/main/java/org/rundeck/client/Rundeck.java @@ -1,12 +1,11 @@ package org.rundeck.client; -import okhttp3.*; +import okhttp3.HttpUrl; +import okhttp3.JavaNetCookieJar; +import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import org.rundeck.client.api.RundeckApi; -import org.rundeck.client.util.FormAuthInterceptor; -import org.rundeck.client.util.Client; -import org.rundeck.client.util.QualifiedTypeConverterFactory; -import org.rundeck.client.util.StaticHeaderInterceptor; +import org.rundeck.client.util.*; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; import retrofit2.converter.simplexml.SimpleXmlConverterFactory; @@ -78,11 +77,21 @@ public static Client client( final int httpLogging ) { + String appBaseUrl = buildBaseAppUrlForVersion(baseUrl); String base = buildApiUrlForVersion(baseUrl, apiVers); OkHttpClient.Builder callFactory = new OkHttpClient.Builder() .addInterceptor(new StaticHeaderInterceptor("X-Rundeck-Auth-Token", authToken)); + String bypassUrl = System.getProperty("rundeck.client.bypass.url", System.getenv("RUNDECK_BYPASS_URL")); + + if (null != bypassUrl) { + //fix redirects to external Rundeck URL by rewriting as to the baseurl + callFactory.addNetworkInterceptor(new RedirectBypassInterceptor( + appBaseUrl, + bypassUrl + )); + } if (httpLogging > 0) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); @@ -158,7 +167,15 @@ public static Client client( ) )); - + String bypassUrl = System.getProperty("rundeck.client.bypass.url", System.getenv("RUNDECK_BYPASS_URL")); + + if (null != bypassUrl) { + //fix redirects to external Rundeck URL by rewriting as to the baseurl + callFactory.addNetworkInterceptor(new RedirectBypassInterceptor( + appBaseUrl, + normalizeUrlPath(bypassUrl) + )); + } if (httpLogging > 0) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); @@ -190,8 +207,13 @@ public static Client client( */ private static String buildApiUrlForVersion(String baseUrl, final int apiVers) { if (!baseUrl.matches("^.*/api/\\d+/?$")) { - return baseUrl + "/api/" + (apiVers) + "/"; - } else if (!baseUrl.matches(".*/$")) { + return normalizeUrlPath(baseUrl) + "api/" + (apiVers) + "/"; + } + return normalizeUrlPath(baseUrl); + } + + private static String normalizeUrlPath(String baseUrl) { + if (!baseUrl.matches(".*/$")) { return baseUrl + "/"; } return baseUrl; @@ -205,10 +227,8 @@ private static String buildApiUrlForVersion(String baseUrl, final int apiVers) { private static String buildBaseAppUrlForVersion(String baseUrl) { Matcher matcher = API_VERS_PATTERN.matcher(baseUrl); if (matcher.matches()) { - return matcher.group(1); - } else if (!baseUrl.matches(".*/$")) { - return baseUrl + "/"; + return normalizeUrlPath(matcher.group(1)); } - return baseUrl; + return normalizeUrlPath(baseUrl); } } diff --git a/src/main/java/org/rundeck/client/util/RedirectBypassInterceptor.java b/src/main/java/org/rundeck/client/util/RedirectBypassInterceptor.java new file mode 100644 index 00000000..86b16659 --- /dev/null +++ b/src/main/java/org/rundeck/client/util/RedirectBypassInterceptor.java @@ -0,0 +1,50 @@ +package org.rundeck.client.util; + +import okhttp3.Interceptor; +import okhttp3.Response; + +import java.io.IOException; + +/** + * Change redirect behavior, if Location begins with the bypass url, replace that part with the app URL. + */ +public class RedirectBypassInterceptor implements Interceptor { + private String appBaseUrl; + private String bypassUrl; + + public RedirectBypassInterceptor(final String appBaseUrl, final String bypassUrl) { + this.appBaseUrl = appBaseUrl; + this.bypassUrl = bypassUrl; + } + + @Override + public Response intercept(final Chain chain) throws IOException { + Response originalResponse = chain.proceed(chain.request()); + if (originalResponse.isRedirect()) { + String originalLocation = originalResponse.header("location"); + String newUrl = remapUrl(originalLocation, bypassUrl, appBaseUrl); + if (null != newUrl) { + return originalResponse.newBuilder() + .header("Location", newUrl) + .build(); + } + } + return originalResponse; + } + + /** + * Replace the prefix of the originurl that starts with the bypassurl string with the appbaseurl string + * + * @param origUrl + * @param bypassUrl + * @param appBaseUrl + * + * @return + */ + public static String remapUrl(final String origUrl, final String bypassUrl, final String appBaseUrl) { + if (origUrl.startsWith(bypassUrl)) { + return appBaseUrl + origUrl.substring(bypassUrl.length()); + } + return null; + } +} diff --git a/src/test/groovy/org/rundeck/client/util/RedirectBypassInterceptorSpec.groovy b/src/test/groovy/org/rundeck/client/util/RedirectBypassInterceptorSpec.groovy new file mode 100644 index 00000000..ce1a3bed --- /dev/null +++ b/src/test/groovy/org/rundeck/client/util/RedirectBypassInterceptorSpec.groovy @@ -0,0 +1,23 @@ +package org.rundeck.client.util + +import spock.lang.Specification + +/** + * Created by greg on 9/9/16. + */ +class RedirectBypassInterceptorSpec extends Specification { + def "remap"() { + when: + String result = RedirectBypassInterceptor.remapUrl(orig, bypass, app) + + + then: + result == expect + + where: + orig | bypass | app | expect + "http://host1/c1/path1" | "http://host1" | "http://host2/c2" | "http://host2/c2/c1/path1" + "http://host1/c1/path1" | "http://host1/c1" | "http://host2/c2" | "http://host2/c2/path1" + "http://host1/c1/path1" | "http://host1/c1" | "http://host2" | "http://host2/path1" + } +}