Skip to content

Commit

Permalink
feat(oxauth): support wildcard character in redirect_uri #1918 (#1919)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuriyz authored Aug 20, 2024
1 parent 068777d commit 6a2c291
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ public class AppConfiguration implements Configuration {
private Set<String> jmsBrokerURISet;
private String jmsUserName;
private String jmsPassword;
private Boolean allowWildcardRedirectUri;
private List<String> clientWhiteList;
private List<String> clientBlackList;
private Boolean legacyIdTokenClaims;
Expand Down Expand Up @@ -1725,6 +1726,14 @@ public void setJmsPassword(String jmsPassword) {
this.jmsPassword = jmsPassword;
}

public Boolean getAllowWildcardRedirectUri() {
return allowWildcardRedirectUri;
}

public void setAllowWildcardRedirectUri(Boolean allowWildcardRedirectUri) {
this.allowWildcardRedirectUri = allowWildcardRedirectUri;
}

public List<String> getClientWhiteList() {
return clientWhiteList;
}
Expand Down
83 changes: 53 additions & 30 deletions Model/src/main/java/org/gluu/oxauth/model/util/URLPatternList.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.gluu.oxauth.model.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.net.MalformedURLException;
Expand All @@ -18,13 +19,19 @@ public class URLPatternList {
private static final Logger LOG = Logger.getLogger(URLPatternList.class);

private List<URLPattern> urlPatternList;
private boolean wildcardEnabled = false;

public URLPatternList() {
this.urlPatternList = new ArrayList<URLPattern>();
this(new ArrayList<String>());
}

public URLPatternList(List<String> urlPatternList) {
this();
this(urlPatternList, false);
}

public URLPatternList(List<String> urlPatternList, boolean wildcardEnabled) {
this.urlPatternList = new ArrayList<>();
this.wildcardEnabled = wildcardEnabled;

if (urlPatternList != null) {
for (String urlPattern : urlPatternList) {
Expand All @@ -38,6 +45,10 @@ public boolean isUrlListed(String uri) {
return true;
}

if (wildcardEnabled) {
uri = StringUtils.replace(uri, "*", "a");
}

URI parsedUri = URI.create(uri);

for (URLPattern pattern : urlPatternList) {
Expand All @@ -50,35 +61,46 @@ public boolean isUrlListed(String uri) {
}

public void addListEntry(String urlPattern) {
if (urlPatternList != null) {
try {
if (urlPattern.compareTo("*") == 0) {
LOG.debug("Unlimited access to network resources");
urlPatternList = null;
} else { // specific access
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
Matcher m = parts.matcher(urlPattern);
if (m.matches()) {
String scheme = m.group(2);
String host = m.group(4);
// Special case for two urls which are allowed to have empty hosts
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
String port = m.group(8);
String path = m.group(9);
if (scheme == null) {
urlPatternList.add(new URLPattern("http", host, port, path));
urlPatternList.add(new URLPattern("https", host, port, path));
} else {
urlPatternList.add(new URLPattern(scheme, host, port, path));
}
}
if (urlPatternList == null) {
return;
}

if (urlPattern.compareTo("*") == 0) {
LOG.debug("Unlimited access to network resources");
urlPatternList = null;
return;
}

try {
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
Matcher m = parts.matcher(urlPattern);
if (m.matches()) {
String scheme = m.group(2);
String host = m.group(4);
// Special case for two urls which are allowed to have empty hosts
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
String port = m.group(8);
String path = m.group(9);
if (scheme == null) {
urlPatternList.add(new URLPattern("http", host, port, path));
urlPatternList.add(new URLPattern("https", host, port, path));
} else {
urlPatternList.add(new URLPattern(scheme, host, port, path));
}
} catch (Exception e) {
LOG.debug("Failed to add origin " + urlPattern);
}
} catch (Exception e) {
LOG.debug("Failed to add origin " + urlPattern);
}
}

public boolean isWildcardEnabled() {
return wildcardEnabled;
}

public void setWildcardEnabled(boolean wildcardEnabled) {
this.wildcardEnabled = wildcardEnabled;
}

private static class URLPattern {
public Pattern scheme;
public Pattern host;
Expand Down Expand Up @@ -116,10 +138,11 @@ public URLPattern(String scheme, String host, String port, String path) throws M

public boolean matches(URI uri) {
try {
return ((scheme == null || scheme.matcher(uri.getScheme()).matches()) &&
(host == null || host.matcher(uri.getHost()).matches()) &&
(port == null || port.equals(uri.getPort())) &&
(path == null || path.matcher(uri.getPath()).matches()));
final boolean schemaMatches = scheme == null || scheme.matcher(uri.getScheme()).matches();
final boolean hostMatches = host == null || host.matcher(uri.getHost()).matches();
final boolean portMatches = port == null || port.equals(uri.getPort());
final boolean pathMatches = path == null || path.matcher(uri.getPath()).matches();
return schemaMatches && hostMatches && portMatches && pathMatches;
} catch (Exception e) {
LOG.debug(e.toString());
return false;
Expand Down
17 changes: 17 additions & 0 deletions Model/src/test/java/org/gluu/oxauth/model/util/Tester.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.gluu.oxauth.model.util;

/**
* @author Yuriy Z
*/
public class Tester {
private Tester() {
}

public static void showTitle(String title) {
title = "TEST: " + title;

System.out.println("#######################################################");
System.out.println(title);
System.out.println("#######################################################");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.gluu.oxauth.model.util;

import org.testng.annotations.Test;

import java.util.Collections;
import java.util.List;

import static org.gluu.oxauth.model.util.Tester.showTitle;
import static org.testng.Assert.assertTrue;
import static org.testng.AssertJUnit.assertFalse;

/**
* @author Yuriy Z
*/
public class URLPatternListTest {

@Test
public void isUrlListed_forUrlWithWildcard_shouldMatch() {
showTitle("isUrlListed_forUrlWithWildcard_shouldMatch");

List<String> urlPatterns = Collections.singletonList("*.gluu.org");

URLPatternList urlPatternList = new URLPatternList(urlPatterns, true);
assertTrue(urlPatternList.isUrlListed("https://*.gluu.org"));
assertTrue(urlPatternList.isUrlListed("https://abc.gluu.org"));
}

@Test
public void isUrlListed_forUrlWithoutWildcardSupport_shouldFail() {
showTitle("isUrlListed_forUrlWithoutWildcardSupport_shouldFail");

List<String> urlPatterns = Collections.singletonList("*.gluu.org");

URLPatternList urlPatternList = new URLPatternList(urlPatterns, false);
assertFalse(urlPatternList.isUrlListed("https://*.gluu.org"));
assertTrue(urlPatternList.isUrlListed("https://abc.gluu.org"));
}


@Test
public void isUrlListed_forAllowAll_shouldMatch() {
showTitle("isUrlListed_forAllowAll_shouldMatch");

List<String> urlPatterns = Collections.singletonList("*");

URLPatternList urlPatternList = new URLPatternList(urlPatterns, true);
assertTrue(urlPatternList.isUrlListed("https://*.gluu.org"));
assertTrue(urlPatternList.isUrlListed("https://abc.gluu.org"));

urlPatternList = new URLPatternList(urlPatterns, false);
assertTrue(urlPatternList.isUrlListed("https://*.gluu.org"));
assertTrue(urlPatternList.isUrlListed("https://abc.gluu.org"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,6 @@

package org.gluu.oxauth.model.registration;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.lang.StringUtils;
import org.gluu.oxauth.client.RegisterRequest;
import org.gluu.oxauth.model.common.GrantType;
Expand All @@ -36,6 +23,20 @@
import org.json.JSONArray;
import org.slf4j.Logger;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.apache.commons.lang.BooleanUtils.isTrue;

/**
* Validates the parameters received for the register web service.
*
Expand Down Expand Up @@ -315,7 +316,7 @@ public boolean validateRedirectUris(List<GrantType> grantTypes, List<ResponseTyp
}

// Validate Redirect Uris checking the white list and black list
if (valid) {
if (valid || isTrue(appConfiguration.getAllowWildcardRedirectUri())) {
valid = checkWhiteListRedirectUris(redirectUris) && checkBlackListRedirectUris(redirectUris);
}

Expand Down Expand Up @@ -348,7 +349,8 @@ public boolean validateInitiateLoginUri(String initiateLoginUri) {
private boolean checkWhiteListRedirectUris(List<String> redirectUris) {
boolean valid = true;
List<String> whiteList = appConfiguration.getClientWhiteList();
URLPatternList urlPatternList = new URLPatternList(whiteList);
boolean wildcardSupported = appConfiguration.getAllowWildcardRedirectUri();
URLPatternList urlPatternList = new URLPatternList(whiteList, wildcardSupported);

for (String redirectUri : redirectUris) {
valid &= urlPatternList.isUrlListed(redirectUri);
Expand All @@ -363,7 +365,8 @@ private boolean checkWhiteListRedirectUris(List<String> redirectUris) {
private boolean checkBlackListRedirectUris(List<String> redirectUris) {
boolean valid = true;
List<String> blackList = appConfiguration.getClientBlackList();
URLPatternList urlPatternList = new URLPatternList(blackList);
boolean wildcardSupported = appConfiguration.getAllowWildcardRedirectUri();
URLPatternList urlPatternList = new URLPatternList(blackList, wildcardSupported);

for (String redirectUri : redirectUris) {
valid &= !urlPatternList.isUrlListed(redirectUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package org.gluu.oxauth.service;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
Expand All @@ -27,10 +28,9 @@
import javax.inject.Inject;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;

import static org.apache.commons.lang.BooleanUtils.isTrue;

/**
* @author Javier Rojas Blum
Expand Down Expand Up @@ -118,6 +118,15 @@ public String validateRedirectionUri(@NotNull Client client, String redirectionU
return redirectUris[0];
}
}

if (isTrue(appConfiguration.getAllowWildcardRedirectUri()) && redirectUris != null && redirectUris.length > 0) {
URLPatternList urlPatternList = new URLPatternList(Arrays.asList(redirectUris), true);
boolean valid = urlPatternList.isUrlListed(redirectionUri);
if (valid) {
log.trace("Allowed by wildcard redirect_uris: {}", Joiner.on(",").join(redirectUris));
return redirectionUri;
}
}
} catch (Exception e) {
return null;
}
Expand Down

0 comments on commit 6a2c291

Please sign in to comment.