/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.util;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class UaaUrlUtilsTest {
private List<String> invalidWildCardUrls = Arrays.asList("*", "**", "*/**", "**/*", "*/*", "**/**");
private List<String> invalidHttpWildCardUrls = Arrays.asList(
"http://*",
"http://**",
"http://*/**",
"http://*/*",
"http://**/*",
"http://a*",
"http://*.com",
"http://*domain*",
"http://*domain.com",
"http://*domain/path",
"http://local*",
"*.valid.com/*/with/path**",
"http://**/path",
"https://*.*.*.com/*/with/path**",
"www.*/path",
"www.invalid.com/*/with/path**",
"www.*.invalid.com/*/with/path**",
"http://username:password@*.com",
"http://username:password@*.com/path"
);
private List<String> validUrls = Arrays.asList(
"http://localhost",
"http://localhost:8080",
"http://localhost:8080/uaa",
"http://valid.com",
"http://sub.valid.com",
"http://valid.com/with/path",
"https://subsub.sub.valid.com/**",
"https://valid.com/path/*/path",
"http://sub.valid.com/*/with/path**",
"http*://sub.valid.com/*/with/path**",
"http*://*.valid.com/*/with/path**",
"http://*.valid.com/*/with/path**",
"https://*.valid.com/*/with/path**",
"https://*.*.valid.com/*/with/path**",
"http://sub*.valid.com/*/with/path**",
"http://*.domain.com",
"http://username:password@some.server.com",
"http://username:password@some.server.com/path"
);
@Before
public void setUp() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
}
@After
public void tearDown() throws Exception {
IdentityZoneHolder.clear();
RequestContextHolder.setRequestAttributes(null);
}
@Test
public void getParameterMapFromQueryString() {
String url = "http://localhost:8080/uaa/oauth/authorize?client_id=app-addnew-false4cEsLB&response_type=code&redirect_uri=http%3A%2F%2Fnosuchhostname%3A0%2Fnosuchendpoint";
Map<String,String[]> map = UaaUrlUtils.getParameterMap(url);
assertNotNull(map);
assertEquals("app-addnew-false4cEsLB", map.get("client_id")[0]);
assertEquals("http://nosuchhostname:0/nosuchendpoint", map.get("redirect_uri")[0]);
}
@Test
public void testGetUaaUrl() throws Exception {
assertEquals("http://localhost", UaaUrlUtils.getUaaUrl());
}
@Test
public void testGetBaseURL() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("login.domain");
request.setRequestURI("/something");
request.setServletPath("/something");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
assertEquals("http://login.domain", UaaUrlUtils.getBaseURL(request));
}
@Test
public void testGetBaseURLWhenPathMatchesHostname() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("login.domain");
request.setRequestURI("/login");
request.setServletPath("/login");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
assertEquals("http://login.domain", UaaUrlUtils.getBaseURL(request));
}
@Test
public void testGetBaseURLOnLocalhost() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("localhost");
request.setServerPort(8080);
request.setRequestURI("/uaa/something");
request.setServletPath("/something");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
assertEquals("http://localhost:8080/uaa", UaaUrlUtils.getBaseURL(request));
}
@Test
public void test_ZoneAware_UaaUrl() throws Exception {
IdentityZone zone = MultitenancyFixture.identityZone("id","subdomain");
IdentityZoneHolder.set(zone);
assertEquals("http://localhost", UaaUrlUtils.getUaaUrl(""));
assertEquals("http://subdomain.localhost", UaaUrlUtils.getUaaUrl("",true));
}
@Test
public void testGetUaaUrlWithPath() throws Exception {
assertEquals("http://localhost/login", UaaUrlUtils.getUaaUrl("/login"));
}
@Test
public void testGetUaaUrlWithZone() throws Exception {
setIdentityZone("zone1");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("zone1.localhost");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
assertEquals("http://zone1.localhost", UaaUrlUtils.getUaaUrl());
}
@Test
public void testGetUaaUrlWithZoneAndPath() throws Exception {
setIdentityZone("zone1");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("zone1.localhost");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
assertEquals("http://zone1.localhost/login", UaaUrlUtils.getUaaUrl("/login"));
}
@Test
public void testGetHost() throws Exception {
assertEquals("localhost", UaaUrlUtils.getUaaHost());
}
@Test
public void testGetHostWithZone() throws Exception {
setIdentityZone("zone1");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("zone1.localhost");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
assertEquals("zone1.localhost", UaaUrlUtils.getUaaHost());
}
@Test
public void testLocalhostPortAndContextPathUrl() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("localhost");
request.setServerPort(8080);
request.setContextPath("/uaa");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
String url = UaaUrlUtils.getUaaUrl("/something");
assertThat(url, is("http://localhost:8080/uaa/something"));
}
@Test
public void testSecurityProtocol() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("https");
request.setServerPort(8443);
request.setServerName("localhost");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
String url = UaaUrlUtils.getUaaUrl("/something");
assertThat(url, is("https://localhost:8443/something"));
}
@Test
public void testMultiDomainUrls() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("login.localhost");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
String url = UaaUrlUtils.getUaaUrl("/something");
assertThat(url, is("http://login.localhost/something"));
}
@Test
public void testZonedAndMultiDomainUrls() {
IdentityZoneHolder.set(MultitenancyFixture.identityZone("testzone1-id", "testzone1"));
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("testzone1.login.localhost");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
String url = UaaUrlUtils.getUaaUrl("/something");
assertThat(url, is("http://testzone1.login.localhost/something"));
}
@Test
public void testXForwardedPrefixUrls() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setScheme("http");
request.setServerName("login.localhost");
request.addHeader("X-Forwarded-Prefix", "/prefix");
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attrs);
String url = UaaUrlUtils.getUaaUrl("/something");
assertThat(url, is("http://login.localhost/prefix/something"));
}
@Test
public void findMatchingRedirectUri_usesAntPathMatching() {
//matches pattern
String matchingRedirectUri1 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton("http://matching.redirect/*"), "http://matching.redirect/", null);
assertThat(matchingRedirectUri1, equalTo("http://matching.redirect/"));
//matches pattern
String matchingRedirectUri2 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton("http://matching.redirect/*"), "http://matching.redirect/anything-but-forward-slash", null);
assertThat(matchingRedirectUri2, equalTo("http://matching.redirect/anything-but-forward-slash"));
//does not match pattern, but no fallback
matchingRedirectUri2 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton("http://matching.redirect/*"), "http://does.not.match/redirect", null);
assertThat(matchingRedirectUri2, equalTo("http://does.not.match/redirect"));
//does not match pattern, but fallback provided
matchingRedirectUri2 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton("http://matching.redirect/*"), "http://does.not.match/redirect", "http://fallback.url/redirect");
assertThat(matchingRedirectUri2, equalTo("http://fallback.url/redirect"));
String pattern2 = "http://matching.redirect/**";
String redirect3 = "http://matching.redirect/whatever/you/want";
String matchingRedirectUri3 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton(pattern2), redirect3, null);
assertThat(matchingRedirectUri3, equalTo(redirect3));
String pattern3 = "http://matching.redirect/?";
String redirect4 = "http://matching.redirect/t";
String matchingRedirectUri4 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton(pattern3), redirect4, null);
assertThat(matchingRedirectUri4, equalTo(redirect4));
String redirect5 = "http://non-matching.redirect/two";
String fallback = "http://fallback.to/this";
String matchingRedirectUri5 = UaaUrlUtils.findMatchingRedirectUri(Collections.singleton(pattern3), redirect5, fallback);
assertThat(matchingRedirectUri5, equalTo(fallback));
}
@Test
public void test_add_query_parameter() {
String url = "http://sub.domain.com";
String name = "name";
String value = "value";
assertEquals("http://sub.domain.com?name=value", UaaUrlUtils.addQueryParameter(url, name, value));
assertEquals("http://sub.domain.com/?name=value", UaaUrlUtils.addQueryParameter(url+"/", name, value));
assertEquals("http://sub.domain.com?key=value&name=value", UaaUrlUtils.addQueryParameter(url+"?key=value", name, value));
assertEquals("http://sub.domain.com?key=value&name=value#frag=fragvalue", UaaUrlUtils.addQueryParameter(url+"?key=value#frag=fragvalue", name, value));
assertEquals("http://sub.domain.com?name=value#frag=fragvalue", UaaUrlUtils.addQueryParameter(url+"#frag=fragvalue", name, value));
}
@Test
public void test_add_fragment_component() {
String url = "http://sub.domain.com";
String component = "name=value";
assertEquals("http://sub.domain.com#name=value", UaaUrlUtils.addFragmentComponent(url, component));
}
@Test
public void test_add_fragment_component_to_prior_fragment() {
String url = "http://sub.domain.com#frag";
String component = "name=value";
assertEquals("http://sub.domain.com#frag&name=value", UaaUrlUtils.addFragmentComponent(url, component));
}
@Test
public void test_validate_valid_redirect_uri() {
validateRedirectUri(validUrls, true);
validateRedirectUri(convertToHttps(validUrls), true);
}
@Test
public void test_validate_invalid_redirect_uri() {
validateRedirectUri(invalidWildCardUrls, false);
validateRedirectUri(invalidHttpWildCardUrls, false);
validateRedirectUri(convertToHttps(invalidHttpWildCardUrls), false);
}
private void validateRedirectUri(List<String> urls, boolean result) {
Map<String, String> failed = getFailedUrls(urls, result);
if (!failed.isEmpty()) {
StringBuilder builder = new StringBuilder("\n");
failed.entrySet().forEach(entry ->
builder.append(entry.getValue()).append("\n")
);
fail(builder.toString());
}
}
private Map<String, String> getFailedUrls(List<String> urls, boolean result) {
Map<String, String> failed = new LinkedHashMap<>();
urls.stream().forEach(
url -> {
String message = "Assertion failed for " + (result ? "" : "in") + "valid url:" + url;
if (result != UaaUrlUtils.isValidRegisteredRedirectUrl(url)) {
failed.put(url, message);
}
}
);
return failed;
}
private List<String> convertToHttps(List<String> urls) {
return urls.stream().map(url -> url.replace("http:", "https:")).collect(Collectors.toList());
}
private void setIdentityZone(String subdomain) {
IdentityZone zone = MultitenancyFixture.identityZone(subdomain, subdomain);
IdentityZoneHolder.set(zone);
}
}