From eaa7298ef11ff9391d22283742d576e50132ed16 Mon Sep 17 00:00:00 2001 From: William Kim Date: Thu, 25 Jul 2013 11:01:13 -0400 Subject: [PATCH] init commit for Webfinger normilizer utility class. --- .../service/impl/WebfingerIssuerService.java | 74 +---------- .../util/WebfingerURLNormalizer.java | 115 ++++++++++++++++++ 2 files changed, 117 insertions(+), 72 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java index d30a15357..8c6a18205 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java @@ -22,14 +22,13 @@ package org.mitre.openid.connect.client.service.impl; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.apache.http.client.HttpClient; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.DefaultHttpClient; +import org.mitre.discovery.util.WebfingerURLNormalizer; import org.mitre.openid.connect.client.model.IssuerServiceResponse; import org.mitre.openid.connect.client.service.IssuerService; import org.slf4j.Logger; @@ -38,7 +37,6 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; import com.google.common.base.Strings; import com.google.common.cache.CacheBuilder; @@ -58,9 +56,6 @@ public class WebfingerIssuerService implements IssuerService { private static Logger logger = LoggerFactory.getLogger(WebfingerIssuerService.class); - // pattern used to parse user input; we can't use the built-in java URI parser - private static final Pattern pattern = Pattern.compile("^((https|acct|http|mailto):(//)?)?((([^@]+)@)?(([^:]+)(:(\\d*))?))([^\\?]+)?(\\?([^#]+))?(#(.*))?$"); - // map of user input -> issuer, loaded dynamically from webfinger discover private LoadingCache issuers; @@ -90,7 +85,7 @@ public class WebfingerIssuerService implements IssuerService { String identifier = request.getParameter(parameterName); if (!Strings.isNullOrEmpty(identifier)) { try { - String issuer = issuers.get(normalizeResource(identifier)); + String issuer = issuers.get(WebfingerURLNormalizer.normalizeResource(identifier)); if (!whitelist.isEmpty() && !whitelist.contains(issuer)) { throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + issuer); } @@ -111,71 +106,6 @@ public class WebfingerIssuerService implements IssuerService { } } - /** - * Normalize the resource string as per OIDC Discovery. - * @param identifier - * @return the normalized string, or null if the string can't be normalized - */ - private UriComponents normalizeResource(String identifier) { - // try to parse the URI - // NOTE: we can't use the Java built-in URI class because it doesn't split the parts appropriately - - if (Strings.isNullOrEmpty(identifier)) { - logger.warn("Can't normalize null or empty URI: " + identifier); - return null; // nothing we can do - } else { - - //UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(identifier); - UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); - - //Pattern regex = Pattern.compile("^(([^:/?#]+):)?(//(([^@/]*)@)?([^/?#:]*)(:(\\d*))?)?([^?#]*)(\\?([^#]*))?(#(.*))?"); - Matcher m = pattern.matcher(identifier); - if (m.matches()) { - builder.scheme(m.group(2)); - builder.userInfo(m.group(6)); - builder.host(m.group(8)); - String port = m.group(10); - if (!Strings.isNullOrEmpty(port)) { - builder.port(Integer.parseInt(port)); - } - builder.path(m.group(11)); - builder.query(m.group(13)); - builder.fragment(m.group(15)); // we throw away the hash, but this is the group it would be if we kept it - } else { - // doesn't match the pattern, throw it out - logger.warn("Parser couldn't match input: " + identifier); - return null; - } - - UriComponents n = builder.build(); - - if (Strings.isNullOrEmpty(n.getScheme())) { - if (!Strings.isNullOrEmpty(n.getUserInfo()) - && Strings.isNullOrEmpty(n.getPath()) - && Strings.isNullOrEmpty(n.getQuery()) - && n.getPort() < 0) { - - // scheme empty, userinfo is not empty, path/query/port are empty - // set to "acct" (rule 2) - builder.scheme("acct"); - - } else { - // scheme is empty, but rule 2 doesn't apply - // set scheme to "https" (rule 3) - builder.scheme("https"); - } - } - - // fragment must be stripped (rule 4) - builder.fragment(null); - - return builder.build(); - } - - - } - - /** * @return the parameterName */ diff --git a/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java b/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java new file mode 100644 index 000000000..1fe1eae8f --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright 2013 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.mitre.discovery.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import com.google.common.base.Strings; + +/** + * Provides utility methods for normalizing and parsing URIs for use with Webfinger Discovery. + * + * @author wkim + * + */ +public class WebfingerURLNormalizer { + + private static Logger logger = LoggerFactory.getLogger(WebfingerURLNormalizer.class); + + // pattern used to parse user input; we can't use the built-in java URI parser + private static final Pattern pattern = Pattern.compile("^((https|acct|http|mailto):(//)?)?((([^@]+)@)?(([^:]+)(:(\\d*))?))([^\\?]+)?(\\?([^#]+))?(#(.*))?$"); + + + /** + * Private constructor to prevent instantiation. + */ + private WebfingerURLNormalizer() { + // intentionally blank + } + + /** + * Normalize the resource string as per OIDC Discovery. + * @param identifier + * @return the normalized string, or null if the string can't be normalized + */ + public static UriComponents normalizeResource(String identifier) { + // try to parse the URI + // NOTE: we can't use the Java built-in URI class because it doesn't split the parts appropriately + + if (Strings.isNullOrEmpty(identifier)) { + logger.warn("Can't normalize null or empty URI: " + identifier); + return null; // nothing we can do + } else { + + //UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(identifier); + UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); + + //Pattern regex = Pattern.compile("^(([^:/?#]+):)?(//(([^@/]*)@)?([^/?#:]*)(:(\\d*))?)?([^?#]*)(\\?([^#]*))?(#(.*))?"); + Matcher m = pattern.matcher(identifier); + if (m.matches()) { + builder.scheme(m.group(2)); + builder.userInfo(m.group(6)); + builder.host(m.group(8)); + String port = m.group(10); + if (!Strings.isNullOrEmpty(port)) { + builder.port(Integer.parseInt(port)); + } + builder.path(m.group(11)); + builder.query(m.group(13)); + builder.fragment(m.group(15)); // we throw away the hash, but this is the group it would be if we kept it + } else { + // doesn't match the pattern, throw it out + logger.warn("Parser couldn't match input: " + identifier); + return null; + } + + UriComponents n = builder.build(); + + if (Strings.isNullOrEmpty(n.getScheme())) { + if (!Strings.isNullOrEmpty(n.getUserInfo()) + && Strings.isNullOrEmpty(n.getPath()) + && Strings.isNullOrEmpty(n.getQuery()) + && n.getPort() < 0) { + + // scheme empty, userinfo is not empty, path/query/port are empty + // set to "acct" (rule 2) + builder.scheme("acct"); + + } else { + // scheme is empty, but rule 2 doesn't apply + // set scheme to "https" (rule 3) + builder.scheme("https"); + } + } + + // fragment must be stripped (rule 4) + builder.fragment(null); + + return builder.build(); + } + + + } + + + +}