keycloak-uncached
Changes
core/src/main/java/org/keycloak/util/Encode.java 526(+526 -0)
examples/as7-eap-demo/customer-app/pom.xml 20(+0 -20)
examples/as7-eap-demo/customer-app/src/main/java/org/jboss/resteasy/example/oauth/CustomerDatabaseClient.java 36(+0 -36)
examples/as7-eap-demo/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java 49(+49 -0)
examples/as7-eap-demo/database-service/pom.xml 25(+12 -13)
examples/as7-eap-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml 1(+0 -1)
examples/as7-eap-demo/product-app/pom.xml 20(+0 -20)
examples/as7-eap-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java 36(+0 -36)
examples/as7-eap-demo/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java 49(+49 -0)
examples/as7-eap-demo/third-party/pom.xml 12(+1 -11)
examples/as7-eap-demo/third-party/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java 59(+31 -28)
integration/adapter-core/pom.xml 13(+6 -7)
integration/adapter-core/src/main/java/org/keycloak/adapters/config/RealmConfiguration.java 66(+36 -30)
integration/adapter-core/src/main/java/org/keycloak/adapters/config/RealmConfigurationLoader.java 44(+13 -31)
integration/as7-eap6/adapter/pom.xml 36(+19 -17)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java 16(+8 -8)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java 4(+2 -2)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java 3(+1 -2)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java 4(+2 -2)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticatorValve.java 15(+7 -8)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java 59(+21 -38)
integration/jaxrs-oauth-client/pom.xml 71(+71 -0)
integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java 0(+0 -0)
integration/pom.xml 2(+2 -0)
integration/servlet-oauth-client/pom.xml 67(+67 -0)
integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java 70(+54 -16)
integration/undertow/pom.xml 29(+19 -10)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java 2(+1 -1)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java 1(+1 -0)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java 54(+19 -35)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/RealmConfigurationLoader.java 97(+0 -97)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java 2(+1 -1)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java 2(+1 -1)
pom.xml 11(+9 -2)
services/pom.xml 16(+11 -5)
Details
diff --git a/core/src/main/java/org/keycloak/SkeletonKeySession.java b/core/src/main/java/org/keycloak/SkeletonKeySession.java
index 3f10c7e..6859283 100755
--- a/core/src/main/java/org/keycloak/SkeletonKeySession.java
+++ b/core/src/main/java/org/keycloak/SkeletonKeySession.java
@@ -34,4 +34,18 @@ public class SkeletonKeySession implements Serializable {
return metadata;
}
+ protected static ThreadLocal<SkeletonKeySession> local = new ThreadLocal<SkeletonKeySession>();
+
+ public static void pushContext(SkeletonKeySession session) {
+ local.set(session);
+ }
+
+ public static void clearContext() {
+ local.set(null);
+ }
+
+ public static SkeletonKeySession getContext() {
+ return local.get();
+ }
+
}
core/src/main/java/org/keycloak/util/Encode.java 526(+526 -0)
diff --git a/core/src/main/java/org/keycloak/util/Encode.java b/core/src/main/java/org/keycloak/util/Encode.java
new file mode 100755
index 0000000..341f0df
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/Encode.java
@@ -0,0 +1,526 @@
+package org.keycloak.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class Encode
+{
+ private static final String UTF_8 = "UTF-8";
+
+ private static final Pattern PARAM_REPLACEMENT = Pattern.compile("_resteasy_uri_parameter");
+
+ private static final String[] pathEncoding = new String[128];
+ private static final String[] pathSegmentEncoding = new String[128];
+ private static final String[] matrixParameterEncoding = new String[128];
+ private static final String[] queryNameValueEncoding = new String[128];
+ private static final String[] queryStringEncoding = new String[128];
+
+ static
+ {
+ /*
+ * Encode via <a href="http://ietf.org/rfc/rfc3986.txt">RFC 3986</a>. PCHAR is allowed allong with '/'
+ *
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ / "*" / "+" / "," / ";" / "="
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ *
+ */
+ for (int i = 0; i < 128; i++)
+ {
+ if (i >= 'a' && i <= 'z') continue;
+ if (i >= 'A' && i <= 'Z') continue;
+ if (i >= '0' && i <= '9') continue;
+ switch ((char) i)
+ {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '/':
+ case ';':
+ case '=':
+ case ':':
+ case '@':
+ continue;
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.append((char) i);
+ pathEncoding[i] = URLEncoder.encode(sb.toString());
+ }
+ pathEncoding[' '] = "%20";
+ System.arraycopy(pathEncoding, 0, matrixParameterEncoding, 0, pathEncoding.length);
+ matrixParameterEncoding[';'] = "%3B";
+ matrixParameterEncoding['='] = "%3D";
+ matrixParameterEncoding['/'] = "%2F"; // RESTEASY-729
+ System.arraycopy(pathEncoding, 0, pathSegmentEncoding, 0, pathEncoding.length);
+ pathSegmentEncoding['/'] = "%2F";
+ /*
+ * Encode via <a href="http://ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
+ *
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * space encoded as '+'
+ *
+ */
+ for (int i = 0; i < 128; i++)
+ {
+ if (i >= 'a' && i <= 'z') continue;
+ if (i >= 'A' && i <= 'Z') continue;
+ if (i >= '0' && i <= '9') continue;
+ switch ((char) i)
+ {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '?':
+ continue;
+ case ' ':
+ queryNameValueEncoding[i] = "+";
+ continue;
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.append((char) i);
+ queryNameValueEncoding[i] = URLEncoder.encode(sb.toString());
+ }
+
+ /*
+ * query = *( pchar / "/" / "?" )
+
+ */
+ for (int i = 0; i < 128; i++)
+ {
+ if (i >= 'a' && i <= 'z') continue;
+ if (i >= 'A' && i <= 'Z') continue;
+ if (i >= '0' && i <= '9') continue;
+ switch ((char) i)
+ {
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ case ':':
+ case '@':
+ case '?':
+ case '/':
+ continue;
+ case ' ':
+ queryStringEncoding[i] = "%20";
+ continue;
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.append((char) i);
+ queryStringEncoding[i] = URLEncoder.encode(sb.toString());
+ }
+ }
+
+ /**
+ * Keep encoded values "%..." and template parameters intact.
+ */
+ public static String encodeQueryString(String value)
+ {
+ return encodeValue(value, queryStringEncoding);
+ }
+
+ /**
+ * Keep encoded values "%...", matrix parameters, template parameters, and '/' characters intact.
+ */
+ public static String encodePath(String value)
+ {
+ return encodeValue(value, pathEncoding);
+ }
+
+ /**
+ * Keep encoded values "%...", matrix parameters and template parameters intact.
+ */
+ public static String encodePathSegment(String value)
+ {
+ return encodeValue(value, pathSegmentEncoding);
+ }
+
+ /**
+ * Keep encoded values "%..." and template parameters intact.
+ */
+ public static String encodeFragment(String value)
+ {
+ return encodeValue(value, queryNameValueEncoding);
+ }
+
+ /**
+ * Keep encoded values "%..." and template parameters intact.
+ */
+ public static String encodeMatrixParam(String value)
+ {
+ return encodeValue(value, matrixParameterEncoding);
+ }
+
+ /**
+ * Keep encoded values "%..." and template parameters intact.
+ */
+ public static String encodeQueryParam(String value)
+ {
+ return encodeValue(value, queryNameValueEncoding);
+ }
+
+ //private static final Pattern nonCodes = Pattern.compile("%([^a-fA-F0-9]|$)");
+ private static final Pattern nonCodes = Pattern.compile("%([^a-fA-F0-9]|[a-fA-F0-9]$|$|[a-fA-F0-9][^a-fA-F0-9])");
+ private static final Pattern encodedChars = Pattern.compile("%([a-fA-F0-9][a-fA-F0-9])");
+ private static final Pattern encodedCharsMulti = Pattern.compile("((%[a-fA-F0-9][a-fA-F0-9])+)");
+
+ public static String decodePath(String path)
+ {
+ Matcher matcher = encodedCharsMulti.matcher(path);
+ StringBuffer buf = new StringBuffer();
+ CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();
+ while (matcher.find())
+ {
+ decoder.reset();
+ String decoded = decodeBytes(matcher.group(1), decoder);
+ decoded = decoded.replace("\\", "\\\\");
+ decoded = decoded.replace("$", "\\$");
+ matcher.appendReplacement(buf, decoded);
+ }
+ matcher.appendTail(buf);
+ return buf.toString();
+ }
+
+ private static String decodeBytes(String enc, CharsetDecoder decoder)
+ {
+ Matcher matcher = encodedChars.matcher(enc);
+ StringBuffer buf = new StringBuffer();
+ ByteBuffer bytes = ByteBuffer.allocate(enc.length() / 3);
+ while (matcher.find())
+ {
+ int b = Integer.parseInt(matcher.group(1), 16);
+ bytes.put((byte) b);
+ }
+ bytes.flip();
+ try
+ {
+ return decoder.decode(bytes).toString();
+ }
+ catch (CharacterCodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Encode '%' if it is not an encoding sequence
+ *
+ * @param string
+ * @return
+ */
+ public static String encodeNonCodes(String string)
+ {
+ Matcher matcher = nonCodes.matcher(string);
+ StringBuffer buf = new StringBuffer();
+
+
+ // FYI: we do not use the no-arg matcher.find()
+ // coupled with matcher.appendReplacement()
+ // because the matched text may contain
+ // a second % and we must make sure we
+ // encode it (if necessary).
+ int idx = 0;
+ while (matcher.find(idx))
+ {
+ int start = matcher.start();
+ buf.append(string.substring(idx, start));
+ buf.append("%25");
+ idx = start + 1;
+ }
+ buf.append(string.substring(idx));
+ return buf.toString();
+ }
+
+ public static boolean savePathParams(String segment, StringBuffer newSegment, List<String> params)
+ {
+ boolean foundParam = false;
+ // Regular expressions can have '{' and '}' characters. Replace them to do match
+ segment = PathHelper.replaceEnclosedCurlyBraces(segment);
+ Matcher matcher = PathHelper.URI_TEMPLATE_PATTERN.matcher(segment);
+ while (matcher.find())
+ {
+ foundParam = true;
+ String group = matcher.group();
+ // Regular expressions can have '{' and '}' characters. Recover earlier replacement
+ params.add(PathHelper.recoverEnclosedCurlyBraces(group));
+ matcher.appendReplacement(newSegment, "_resteasy_uri_parameter");
+ }
+ matcher.appendTail(newSegment);
+ return foundParam;
+ }
+
+ /**
+ * Keep encoded values "%..." and template parameters intact i.e. "{x}"
+ *
+ * @param segment
+ * @param encoding
+ * @return
+ */
+ public static String encodeValue(String segment, String[] encoding)
+ {
+ ArrayList<String> params = new ArrayList<String>();
+ boolean foundParam = false;
+ StringBuffer newSegment = new StringBuffer();
+ if (savePathParams(segment, newSegment, params))
+ {
+ foundParam = true;
+ segment = newSegment.toString();
+ }
+ String result = encodeFromArray(segment, encoding, false);
+ result = encodeNonCodes(result);
+ segment = result;
+ if (foundParam)
+ {
+ segment = pathParamReplacement(segment, params);
+ }
+ return segment;
+ }
+
+ /**
+ * Encode via <a href="http://ietf.org/rfc/rfc3986.txt">RFC 3986</a>. PCHAR is allowed allong with '/'
+ * <p/>
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ */
+ public static String encodePathAsIs(String segment)
+ {
+ return encodeFromArray(segment, pathEncoding, true);
+ }
+
+ /**
+ * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p"
+ *
+ * @param segment
+ * @return
+ */
+ public static String encodePathSaveEncodings(String segment)
+ {
+ String result = encodeFromArray(segment, pathEncoding, false);
+ result = encodeNonCodes(result);
+ return result;
+ }
+
+ /**
+ * Encode via <a href="http://ietf.org/rfc/rfc3986.txt">RFC 3986</a>. PCHAR is allowed allong with '/'
+ * <p/>
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ */
+ public static String encodePathSegmentAsIs(String segment)
+ {
+ return encodeFromArray(segment, pathSegmentEncoding, true);
+ }
+
+ /**
+ * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p"
+ *
+ * @param segment
+ * @return
+ */
+ public static String encodePathSegmentSaveEncodings(String segment)
+ {
+ String result = encodeFromArray(segment, pathSegmentEncoding, false);
+ result = encodeNonCodes(result);
+ return result;
+ }
+
+
+ /**
+ * Encodes everything of a query parameter name or value.
+ *
+ * @param nameOrValue
+ * @return
+ */
+ public static String encodeQueryParamAsIs(String nameOrValue)
+ {
+ return encodeFromArray(nameOrValue, queryNameValueEncoding, true);
+ }
+
+ /**
+ * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p"
+ *
+ * @param segment
+ * @return
+ */
+ public static String encodeQueryParamSaveEncodings(String segment)
+ {
+ String result = encodeFromArray(segment, queryNameValueEncoding, false);
+ result = encodeNonCodes(result);
+ return result;
+ }
+
+ public static String encodeFragmentAsIs(String nameOrValue)
+ {
+ return encodeFromArray(nameOrValue, queryNameValueEncoding, true);
+ }
+
+ protected static String encodeFromArray(String segment, String[] encodingMap, boolean encodePercent)
+ {
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < segment.length(); i++)
+ {
+ if (!encodePercent && segment.charAt(i) == '%')
+ {
+ result.append(segment.charAt(i));
+ continue;
+ }
+ int idx = segment.charAt(i);
+ String encoding = encode(idx, encodingMap);
+ if (encoding == null)
+ {
+ result.append(segment.charAt(i));
+ }
+ else
+ {
+ result.append(encoding);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * @param zhar integer representation of character
+ * @param encodingMap encoding map
+ * @return URL encoded character
+ */
+ private static String encode(int zhar, String[] encodingMap)
+ {
+ String encoded;
+ if (zhar < encodingMap.length)
+ {
+ encoded = encodingMap[zhar];
+ }
+ else
+ {
+ try
+ {
+ encoded = URLEncoder.encode(Character.toString((char) zhar), UTF_8);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ return encoded;
+ }
+
+ public static String pathParamReplacement(String segment, List<String> params)
+ {
+ StringBuffer newSegment = new StringBuffer();
+ Matcher matcher = PARAM_REPLACEMENT.matcher(segment);
+ int i = 0;
+ while (matcher.find())
+ {
+ String replacement = params.get(i++);
+ // double encode slashes, so that slashes stay where they are
+ replacement = replacement.replace("\\", "\\\\");
+ replacement = replacement.replace("$", "\\$");
+ matcher.appendReplacement(newSegment, replacement);
+ }
+ matcher.appendTail(newSegment);
+ segment = newSegment.toString();
+ return segment;
+ }
+
+ /**
+ * decode an encoded map
+ *
+ * @param map
+ * @return
+ */
+ public static MultivaluedHashMap<String, String> decode(MultivaluedHashMap<String, String> map)
+ {
+ MultivaluedHashMap<String, String> decoded = new MultivaluedHashMap<String, String>();
+ for (Map.Entry<String, List<String>> entry : map.entrySet())
+ {
+ List<String> values = entry.getValue();
+ for (String value : values)
+ {
+ try
+ {
+ decoded.add(URLDecoder.decode(entry.getKey(), UTF_8), URLDecoder.decode(value, UTF_8));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return decoded;
+ }
+
+ public static MultivaluedHashMap<String, String> encode(MultivaluedHashMap<String, String> map)
+ {
+ MultivaluedHashMap<String, String> decoded = new MultivaluedHashMap<String, String>();
+ for (Map.Entry<String, List<String>> entry : map.entrySet())
+ {
+ List<String> values = entry.getValue();
+ for (String value : values)
+ {
+ try
+ {
+ decoded.add(URLEncoder.encode(entry.getKey(), UTF_8), URLEncoder.encode(value, UTF_8));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return decoded;
+ }
+
+ public static String decode(String string)
+ {
+ try
+ {
+ return URLDecoder.decode(string, UTF_8);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/keycloak/util/KeycloakUriBuilder.java b/core/src/main/java/org/keycloak/util/KeycloakUriBuilder.java
new file mode 100755
index 0000000..111c4a4
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/KeycloakUriBuilder.java
@@ -0,0 +1,717 @@
+package org.keycloak.util;
+
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakUriBuilder {
+
+ private String host;
+ private String scheme;
+ private int port = -1;
+
+ private String userInfo;
+ private String path;
+ private String query;
+ private String fragment;
+ private String ssp;
+ private String authority;
+
+ public static KeycloakUriBuilder fromUri(URI uri) {
+ return new KeycloakUriBuilder().uri(uri);
+ }
+
+ public static KeycloakUriBuilder fromUri(String uriTemplate) {
+ return new KeycloakUriBuilder().uri(uriTemplate);
+ }
+
+ public static KeycloakUriBuilder fromPath(String path) throws IllegalArgumentException {
+ return new KeycloakUriBuilder().path(path);
+ }
+
+
+ public KeycloakUriBuilder clone() {
+ KeycloakUriBuilder impl = new KeycloakUriBuilder();
+ impl.host = host;
+ impl.scheme = scheme;
+ impl.port = port;
+ impl.userInfo = userInfo;
+ impl.path = path;
+ impl.query = query;
+ impl.fragment = fragment;
+ impl.ssp = ssp;
+ impl.authority = authority;
+
+ return impl;
+ }
+
+ public static final Pattern opaqueUri = Pattern.compile("^([^:/?#]+):([^/].*)");
+ public static final Pattern hierarchicalUri = Pattern.compile("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
+ private static final Pattern hostPortPattern = Pattern.compile("([^/:]+):(\\d+)");
+
+ public static boolean compare(String s1, String s2) {
+ if (s1 == s2) return true;
+ if (s1 == null || s2 == null) return false;
+ return s1.equals(s2);
+ }
+
+ public static URI relativize(URI from, URI to) {
+ if (!compare(from.getScheme(), to.getScheme())) return to;
+ if (!compare(from.getHost(), to.getHost())) return to;
+ if (from.getPort() != to.getPort()) return to;
+ if (from.getPath() == null && to.getPath() == null) return URI.create("");
+ else if (from.getPath() == null) return URI.create(to.getPath());
+ else if (to.getPath() == null) return to;
+
+
+ String fromPath = from.getPath();
+ if (fromPath.startsWith("/")) fromPath = fromPath.substring(1);
+ String[] fsplit = fromPath.split("/");
+ String toPath = to.getPath();
+ if (toPath.startsWith("/")) toPath = toPath.substring(1);
+ String[] tsplit = toPath.split("/");
+
+ int f = 0;
+
+ for (; f < fsplit.length && f < tsplit.length; f++) {
+ if (!fsplit[f].equals(tsplit[f])) break;
+ }
+
+ KeycloakUriBuilder builder = KeycloakUriBuilder.fromPath("");
+ for (int i = f; i < fsplit.length; i++) builder.path("..");
+ for (int i = f; i < tsplit.length; i++) builder.path(tsplit[i]);
+ return builder.build();
+ }
+
+ /**
+ * You may put path parameters anywhere within the uriTemplate except port
+ *
+ * @param uriTemplate
+ * @return
+ */
+ public static KeycloakUriBuilder fromTemplate(String uriTemplate) {
+ KeycloakUriBuilder impl = new KeycloakUriBuilder();
+ impl.uriTemplate(uriTemplate);
+ return impl;
+ }
+
+ /**
+ * You may put path parameters anywhere within the uriTemplate except port
+ *
+ * @param uriTemplate
+ * @return
+ */
+ public KeycloakUriBuilder uriTemplate(String uriTemplate) {
+ if (uriTemplate == null) throw new IllegalArgumentException("uriTemplate parameter is null");
+ Matcher opaque = opaqueUri.matcher(uriTemplate);
+ if (opaque.matches()) {
+ this.authority = null;
+ this.host = null;
+ this.port = -1;
+ this.userInfo = null;
+ this.query = null;
+ this.scheme = opaque.group(1);
+ this.ssp = opaque.group(2);
+ return this;
+ } else {
+ Matcher match = hierarchicalUri.matcher(uriTemplate);
+ if (match.matches()) {
+ ssp = null;
+ return parseHierarchicalUri(uriTemplate, match);
+ }
+ }
+ throw new IllegalArgumentException("Illegal uri template: " + uriTemplate);
+ }
+
+ protected KeycloakUriBuilder parseHierarchicalUri(String uriTemplate, Matcher match) {
+ boolean scheme = match.group(2) != null;
+ if (scheme) this.scheme = match.group(2);
+ String authority = match.group(4);
+ if (authority != null) {
+ this.authority = null;
+ String host = match.group(4);
+ int at = host.indexOf('@');
+ if (at > -1) {
+ String user = host.substring(0, at);
+ host = host.substring(at + 1);
+ this.userInfo = user;
+ }
+ Matcher hostPortMatch = hostPortPattern.matcher(host);
+ if (hostPortMatch.matches()) {
+ this.host = hostPortMatch.group(1);
+ int val = 0;
+ try {
+ this.port = Integer.parseInt(hostPortMatch.group(2));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Illegal uri template: " + uriTemplate, e);
+ }
+ } else {
+ this.host = host;
+ }
+ }
+ if (match.group(5) != null) {
+ String group = match.group(5);
+ if (!scheme && !"".equals(group) && !group.startsWith("/") && group.indexOf(':') > -1)
+ throw new IllegalArgumentException("Illegal uri template: " + uriTemplate);
+ if (!"".equals(group)) replacePath(group);
+ }
+ if (match.group(7) != null) replaceQuery(match.group(7));
+ if (match.group(9) != null) fragment(match.group(9));
+ return this;
+ }
+
+ public KeycloakUriBuilder uri(String uriTemplate) throws IllegalArgumentException {
+ return uriTemplate(uriTemplate);
+ }
+
+ public KeycloakUriBuilder uri(URI uri) throws IllegalArgumentException {
+ if (uri == null) throw new IllegalArgumentException("URI was null");
+
+ if (uri.getRawFragment() != null) fragment = uri.getRawFragment();
+
+ if (uri.isOpaque()) {
+ scheme = uri.getScheme();
+ ssp = uri.getRawSchemeSpecificPart();
+ return this;
+ }
+
+ if (uri.getScheme() == null) {
+ if (ssp != null) {
+ if (uri.getRawSchemeSpecificPart() != null) {
+ ssp = uri.getRawSchemeSpecificPart();
+ return this;
+ }
+ }
+ } else {
+ scheme = uri.getScheme();
+ }
+
+ ssp = null;
+ if (uri.getRawAuthority() != null) {
+ if (uri.getRawUserInfo() == null && uri.getHost() == null && uri.getPort() == -1) {
+ authority = uri.getRawAuthority();
+ userInfo = null;
+ host = null;
+ port = -1;
+ } else {
+ authority = null;
+ if (uri.getRawUserInfo() != null) {
+ userInfo = uri.getRawUserInfo();
+ }
+ if (uri.getHost() != null) {
+ host = uri.getHost();
+ }
+ if (uri.getPort() != -1) {
+ port = uri.getPort();
+ }
+ }
+ }
+
+ if (uri.getRawPath() != null && uri.getRawPath().length() > 0) {
+ path = uri.getRawPath();
+ }
+ if (uri.getRawQuery() != null && uri.getRawQuery().length() > 0) {
+ query = uri.getRawQuery();
+ }
+
+ return this;
+ }
+
+ public KeycloakUriBuilder scheme(String scheme) throws IllegalArgumentException {
+ this.scheme = scheme;
+ return this;
+ }
+
+ public KeycloakUriBuilder schemeSpecificPart(String ssp) throws IllegalArgumentException {
+ if (ssp == null) throw new IllegalArgumentException("schemeSpecificPart was null");
+
+ StringBuilder sb = new StringBuilder();
+ if (scheme != null) sb.append(scheme).append(':');
+ if (ssp != null)
+ sb.append(ssp);
+ if (fragment != null && fragment.length() > 0) sb.append('#').append(fragment);
+ URI uri = URI.create(sb.toString());
+
+ if (uri.getRawSchemeSpecificPart() != null && uri.getRawPath() == null) {
+ this.ssp = uri.getRawSchemeSpecificPart();
+ } else {
+ this.ssp = null;
+ userInfo = uri.getRawUserInfo();
+ host = uri.getHost();
+ port = uri.getPort();
+ path = uri.getRawPath();
+ query = uri.getRawQuery();
+
+ }
+ return this;
+
+ }
+
+ public KeycloakUriBuilder userInfo(String ui) {
+ this.userInfo = ui;
+ return this;
+ }
+
+ public KeycloakUriBuilder host(String host) throws IllegalArgumentException {
+ if (host != null && host.equals("")) throw new IllegalArgumentException("invalid host");
+ this.host = host;
+ return this;
+ }
+
+ public KeycloakUriBuilder port(int port) throws IllegalArgumentException {
+ if (port < -1) throw new IllegalArgumentException("Invalid port value");
+ this.port = port;
+ return this;
+ }
+
+ protected static String paths(boolean encode, String basePath, String... segments) {
+ String path = basePath;
+ if (path == null) path = "";
+ for (String segment : segments) {
+ if ("".equals(segment)) continue;
+ if (path.endsWith("/")) {
+ if (segment.startsWith("/")) {
+ segment = segment.substring(1);
+ if ("".equals(segment)) continue;
+ }
+ if (encode) segment = Encode.encodePath(segment);
+ path += segment;
+ } else {
+ if (encode) segment = Encode.encodePath(segment);
+ if ("".equals(path)) {
+ path = segment;
+ } else if (segment.startsWith("/")) {
+ path += segment;
+ } else {
+ path += "/" + segment;
+ }
+ }
+
+ }
+ return path;
+ }
+
+ public KeycloakUriBuilder path(String segment) throws IllegalArgumentException {
+ if (segment == null) throw new IllegalArgumentException("path was null");
+ path = paths(true, path, segment);
+ return this;
+ }
+
+ public KeycloakUriBuilder replaceMatrix(String matrix) throws IllegalArgumentException {
+ if (matrix == null) matrix = "";
+ if (!matrix.startsWith(";")) matrix = ";" + matrix;
+ matrix = Encode.encodePath(matrix);
+ if (path == null) {
+ path = matrix;
+ } else {
+ int start = path.lastIndexOf('/');
+ if (start < 0) start = 0;
+ int matrixIndex = path.indexOf(';', start);
+ if (matrixIndex > -1) path = path.substring(0, matrixIndex) + matrix;
+ else path += matrix;
+
+ }
+ return this;
+ }
+
+ public KeycloakUriBuilder replaceQuery(String query) throws IllegalArgumentException {
+ if (query == null || query.length() == 0) {
+ this.query = null;
+ return this;
+ }
+ this.query = Encode.encodeQueryString(query);
+ return this;
+ }
+
+ public KeycloakUriBuilder fragment(String fragment) throws IllegalArgumentException {
+ if (fragment == null) {
+ this.fragment = null;
+ return this;
+ }
+ this.fragment = Encode.encodeFragment(fragment);
+ return this;
+ }
+
+ /**
+ * Only replace path params in path of URI. This changes state of URIBuilder.
+ *
+ * @param name
+ * @param value
+ * @param isEncoded
+ * @return
+ */
+ public KeycloakUriBuilder substitutePathParam(String name, Object value, boolean isEncoded) {
+ if (path != null) {
+ StringBuffer buffer = new StringBuffer();
+ replacePathParameter(name, value.toString(), isEncoded, path, buffer, false);
+ path = buffer.toString();
+ }
+ return this;
+ }
+
+ public URI buildFromMap(Map<String, ? extends Object> values) throws IllegalArgumentException {
+ if (values == null) throw new IllegalArgumentException("values parameter is null");
+ return buildUriFromMap(values, false, true);
+ }
+
+ public URI buildFromEncodedMap(Map<String, ? extends Object> values) throws IllegalArgumentException {
+ if (values == null) throw new IllegalArgumentException("values parameter is null");
+ return buildUriFromMap(values, true, false);
+ }
+
+ public URI buildFromMap(Map<String, ?> values, boolean encodeSlashInPath) throws IllegalArgumentException {
+ if (values == null) throw new IllegalArgumentException("values parameter is null");
+ return buildUriFromMap(values, false, encodeSlashInPath);
+ }
+
+ protected URI buildUriFromMap(Map<String, ? extends Object> paramMap, boolean fromEncodedMap, boolean encodeSlash) throws IllegalArgumentException {
+ String buf = buildString(paramMap, fromEncodedMap, false, encodeSlash);
+ try {
+ return URI.create(buf);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create URI: " + buf, e);
+ }
+ }
+
+ private String buildString(Map<String, ? extends Object> paramMap, boolean fromEncodedMap, boolean isTemplate, boolean encodeSlash) {
+ for (Map.Entry<String, ? extends Object> entry : paramMap.entrySet()) {
+ if (entry.getKey() == null) throw new IllegalArgumentException("map key is null");
+ if (entry.getValue() == null) throw new IllegalArgumentException("map value is null");
+ }
+ StringBuffer buffer = new StringBuffer();
+
+ if (scheme != null)
+ replaceParameter(paramMap, fromEncodedMap, isTemplate, scheme, buffer, encodeSlash).append(":");
+ if (ssp != null) {
+ buffer.append(ssp);
+ } else if (userInfo != null || host != null || port != -1) {
+ buffer.append("//");
+ if (userInfo != null)
+ replaceParameter(paramMap, fromEncodedMap, isTemplate, userInfo, buffer, encodeSlash).append("@");
+ if (host != null) {
+ if ("".equals(host)) throw new RuntimeException("empty host name");
+ replaceParameter(paramMap, fromEncodedMap, isTemplate, host, buffer, encodeSlash);
+ }
+ if (port != -1) buffer.append(":").append(Integer.toString(port));
+ } else if (authority != null) {
+ buffer.append("//");
+ replaceParameter(paramMap, fromEncodedMap, isTemplate, authority, buffer, encodeSlash);
+ }
+ if (path != null) {
+ StringBuffer tmp = new StringBuffer();
+ replaceParameter(paramMap, fromEncodedMap, isTemplate, path, tmp, encodeSlash);
+ String tmpPath = tmp.toString();
+ if (userInfo != null || host != null) {
+ if (!tmpPath.startsWith("/")) buffer.append("/");
+ }
+ buffer.append(tmpPath);
+ }
+ if (query != null) {
+ buffer.append("?");
+ replaceQueryStringParameter(paramMap, fromEncodedMap, isTemplate, query, buffer);
+ }
+ if (fragment != null) {
+ buffer.append("#");
+ replaceParameter(paramMap, fromEncodedMap, isTemplate, fragment, buffer, encodeSlash);
+ }
+ return buffer.toString();
+ }
+
+ protected StringBuffer replacePathParameter(String name, String value, boolean isEncoded, String string, StringBuffer buffer, boolean encodeSlash) {
+ Matcher matcher = createUriParamMatcher(string);
+ while (matcher.find()) {
+ String param = matcher.group(1);
+ if (!param.equals(name)) continue;
+ if (!isEncoded) {
+ if (encodeSlash) value = Encode.encodePath(value);
+ else value = Encode.encodePathSegment(value);
+
+ } else {
+ value = Encode.encodeNonCodes(value);
+ }
+ // if there is a $ then we must backslash it or it will screw up regex group substitution
+ value = value.replace("$", "\\$");
+ matcher.appendReplacement(buffer, value);
+ }
+ matcher.appendTail(buffer);
+ return buffer;
+ }
+
+ public static Matcher createUriParamMatcher(String string) {
+ Matcher matcher = PathHelper.URI_PARAM_PATTERN.matcher(PathHelper.replaceEnclosedCurlyBraces(string));
+ return matcher;
+ }
+
+ protected StringBuffer replaceParameter(Map<String, ? extends Object> paramMap, boolean fromEncodedMap, boolean isTemplate, String string, StringBuffer buffer, boolean encodeSlash) {
+ Matcher matcher = createUriParamMatcher(string);
+ while (matcher.find()) {
+ String param = matcher.group(1);
+ Object valObj = paramMap.get(param);
+ if (valObj == null && !isTemplate) {
+ throw new IllegalArgumentException("NULL value for template parameter: " + param);
+ } else if (valObj == null && isTemplate) {
+ matcher.appendReplacement(buffer, matcher.group());
+ continue;
+ }
+ String value = valObj.toString();
+ if (value != null) {
+ if (!fromEncodedMap) {
+ if (encodeSlash) value = Encode.encodePathSegmentAsIs(value);
+ else value = Encode.encodePathAsIs(value);
+ } else {
+ if (encodeSlash) value = Encode.encodePathSegmentSaveEncodings(value);
+ else value = Encode.encodePathSaveEncodings(value);
+ }
+ matcher.appendReplacement(buffer, Matcher.quoteReplacement(value));
+ } else {
+ throw new IllegalArgumentException("path param " + param + " has not been provided by the parameter map");
+ }
+ }
+ matcher.appendTail(buffer);
+ return buffer;
+ }
+
+ protected StringBuffer replaceQueryStringParameter(Map<String, ? extends Object> paramMap, boolean fromEncodedMap, boolean isTemplate, String string, StringBuffer buffer) {
+ Matcher matcher = createUriParamMatcher(string);
+ while (matcher.find()) {
+ String param = matcher.group(1);
+ Object valObj = paramMap.get(param);
+ if (valObj == null && !isTemplate) {
+ throw new IllegalArgumentException("NULL value for template parameter: " + param);
+ } else if (valObj == null && isTemplate) {
+ matcher.appendReplacement(buffer, matcher.group());
+ continue;
+ }
+ String value = valObj.toString();
+ if (value != null) {
+ if (!fromEncodedMap) {
+ value = Encode.encodeQueryParamAsIs(value);
+ } else {
+ value = Encode.encodeQueryParamSaveEncodings(value);
+ }
+ matcher.appendReplacement(buffer, value);
+ } else {
+ throw new IllegalArgumentException("path param " + param + " has not been provided by the parameter map");
+ }
+ }
+ matcher.appendTail(buffer);
+ return buffer;
+ }
+
+ /**
+ * Return a unique order list of path params
+ *
+ * @return
+ */
+ public List<String> getPathParamNamesInDeclarationOrder() {
+ List<String> params = new ArrayList<String>();
+ HashSet<String> set = new HashSet<String>();
+ if (scheme != null) addToPathParamList(params, set, scheme);
+ if (userInfo != null) addToPathParamList(params, set, userInfo);
+ if (host != null) addToPathParamList(params, set, host);
+ if (path != null) addToPathParamList(params, set, path);
+ if (query != null) addToPathParamList(params, set, query);
+ if (fragment != null) addToPathParamList(params, set, fragment);
+
+ return params;
+ }
+
+ private void addToPathParamList(List<String> params, HashSet<String> set, String string) {
+ Matcher matcher = PathHelper.URI_PARAM_PATTERN.matcher(PathHelper.replaceEnclosedCurlyBraces(string));
+ while (matcher.find()) {
+ String param = matcher.group(1);
+ if (set.contains(param)) continue;
+ else {
+ set.add(param);
+ params.add(param);
+ }
+ }
+ }
+
+ public URI build(Object... values) throws IllegalArgumentException {
+ if (values == null) throw new IllegalArgumentException("values parameter is null");
+ return buildFromValues(true, false, values);
+ }
+
+ protected URI buildFromValues(boolean encodeSlash, boolean encoded, Object... values) {
+ List<String> params = getPathParamNamesInDeclarationOrder();
+ if (values.length < params.size())
+ throw new IllegalArgumentException("You did not supply enough values to fill path parameters");
+
+ Map<String, Object> pathParams = new HashMap<String, Object>();
+
+
+ for (int i = 0; i < params.size(); i++) {
+ String pathParam = params.get(i);
+ Object val = values[i];
+ if (val == null) throw new IllegalArgumentException("A value was null");
+ pathParams.put(pathParam, val.toString());
+ }
+ String buf = null;
+ try {
+ buf = buildString(pathParams, encoded, false, encodeSlash);
+ return new URI(buf);
+ //return URI.create(buf);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create URI: " + buf, e);
+ }
+ }
+
+ public KeycloakUriBuilder matrixParam(String name, Object... values) throws IllegalArgumentException {
+ if (name == null) throw new IllegalArgumentException("name parameter is null");
+ if (values == null) throw new IllegalArgumentException("values parameter is null");
+ if (path == null) path = "";
+ for (Object val : values) {
+ if (val == null) throw new IllegalArgumentException("null value");
+ path += ";" + Encode.encodeMatrixParam(name) + "=" + Encode.encodeMatrixParam(val.toString());
+ }
+ return this;
+ }
+
+ private static final Pattern PARAM_REPLACEMENT = Pattern.compile("_resteasy_uri_parameter");
+
+
+ public KeycloakUriBuilder queryParam(String name, Object... values) throws IllegalArgumentException {
+ if (name == null) throw new IllegalArgumentException("name parameter is null");
+ if (values == null) throw new IllegalArgumentException("values parameter is null");
+ for (Object value : values) {
+ if (value == null) throw new IllegalArgumentException("A passed in value was null");
+ if (query == null) query = "";
+ else query += "&";
+ query += Encode.encodeQueryParam(name) + "=" + Encode.encodeQueryParam(value.toString());
+ }
+ return this;
+ }
+
+ public KeycloakUriBuilder replaceQueryParam(String name, Object... values) throws IllegalArgumentException {
+ if (name == null) throw new IllegalArgumentException("name parameter is null");
+ if (query == null || query.equals("")) {
+ if (values != null) return queryParam(name, values);
+ return this;
+ }
+
+ String[] params = query.split("&");
+ query = null;
+
+ String replacedName = Encode.encodeQueryParam(name);
+
+
+ for (String param : params) {
+ int pos = param.indexOf('=');
+ if (pos >= 0) {
+ String paramName = param.substring(0, pos);
+ if (paramName.equals(replacedName)) continue;
+ } else {
+ if (param.equals(replacedName)) continue;
+ }
+ if (query == null) query = "";
+ else query += "&";
+ query += param;
+ }
+ // don't set values if values is null
+ if (values == null) return this;
+ // don't set values if values is null
+ if (values == null) return this;
+ return queryParam(name, values);
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getUserInfo() {
+ return userInfo;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public String getFragment() {
+ return fragment;
+ }
+
+ public KeycloakUriBuilder segment(String... segments) throws IllegalArgumentException {
+ if (segments == null) throw new IllegalArgumentException("segments parameter was null");
+ for (String segment : segments) {
+ if (segment == null) throw new IllegalArgumentException("A segment is null");
+ path(Encode.encodePathSegment(segment));
+ }
+ return this;
+ }
+
+ public KeycloakUriBuilder replacePath(String path) {
+ if (path == null) {
+ this.path = null;
+ return this;
+ }
+ this.path = Encode.encodePath(path);
+ return this;
+ }
+
+ public URI build(Object[] values, boolean encodeSlashInPath) throws IllegalArgumentException {
+ if (values == null) throw new IllegalArgumentException("values param is null");
+ return buildFromValues(encodeSlashInPath, false, values);
+ }
+
+ public String toTemplate() {
+ return buildString(new HashMap<String, Object>(), true, true, true);
+ }
+
+ public KeycloakUriBuilder resolveTemplate(String name, Object value) throws IllegalArgumentException {
+ if (name == null) throw new IllegalArgumentException("name param is null");
+ if (value == null) throw new IllegalArgumentException("value param is null");
+ HashMap<String, Object> vals = new HashMap<String, Object>();
+ vals.put(name, value);
+ return resolveTemplates(vals);
+ }
+
+ public KeycloakUriBuilder resolveTemplates(Map<String, Object> templateValues) throws IllegalArgumentException {
+ if (templateValues == null) throw new IllegalArgumentException("templateValues param null");
+ String str = buildString(templateValues, false, true, true);
+ return fromTemplate(str);
+ }
+
+ public KeycloakUriBuilder resolveTemplate(String name, Object value, boolean encodeSlashInPath) throws IllegalArgumentException {
+ if (name == null) throw new IllegalArgumentException("name param is null");
+ if (value == null) throw new IllegalArgumentException("value param is null");
+ HashMap<String, Object> vals = new HashMap<String, Object>();
+ vals.put(name, value);
+ String str = buildString(vals, false, true, encodeSlashInPath);
+ return fromTemplate(str);
+ }
+
+ public KeycloakUriBuilder resolveTemplates(Map<String, Object> templateValues, boolean encodeSlashInPath) throws IllegalArgumentException {
+ if (templateValues == null) throw new IllegalArgumentException("templateValues param null");
+ String str = buildString(templateValues, false, true, encodeSlashInPath);
+ return fromTemplate(str);
+ }
+
+ public KeycloakUriBuilder resolveTemplatesFromEncoded(Map<String, Object> templateValues) throws IllegalArgumentException {
+ if (templateValues == null) throw new IllegalArgumentException("templateValues param null");
+ String str = buildString(templateValues, true, true, true);
+ return fromTemplate(str);
+ }
+}
diff --git a/core/src/main/java/org/keycloak/util/PathHelper.java b/core/src/main/java/org/keycloak/util/PathHelper.java
new file mode 100755
index 0000000..644f03e
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/PathHelper.java
@@ -0,0 +1,62 @@
+/**
+ *
+ */
+package org.keycloak.util;
+
+import java.util.regex.Pattern;
+
+
+/**
+ * A utility class for handling URI template parameters. As the Java
+ * regulare expressions package does not handle named groups, this
+ * class attempts to simulate that functionality by using groups.
+ *
+ * @author Ryan J. McDonough
+ * @author Bill Burke
+ * @since 1.0
+ * Nov 8, 2006
+ */
+public class PathHelper
+{
+ public static final String URI_PARAM_NAME_REGEX = "\\w[\\w\\.-]*";
+ public static final String URI_PARAM_REGEX_REGEX = "[^{}][^{}]*";
+ public static final String URI_PARAM_REGEX = "\\{\\s*(" + URI_PARAM_NAME_REGEX + ")\\s*(:\\s*(" + URI_PARAM_REGEX_REGEX + "))?\\}";
+ public static final Pattern URI_PARAM_PATTERN = Pattern.compile(URI_PARAM_REGEX);
+
+ /**
+ * A regex pattern that searches for a URI template parameter in the form of {*}
+ */
+ public static final Pattern URI_TEMPLATE_PATTERN = Pattern.compile("(\\{([^}]+)\\})");
+
+ public static final char openCurlyReplacement = 6;
+ public static final char closeCurlyReplacement = 7;
+
+ public static String replaceEnclosedCurlyBraces(String str)
+ {
+ char[] chars = str.toCharArray();
+ int open = 0;
+ for (int i = 0; i < chars.length; i++)
+ {
+ if (chars[i] == '{')
+ {
+ if (open != 0) chars[i] = openCurlyReplacement;
+ open++;
+ }
+ else if (chars[i] == '}')
+ {
+ open--;
+ if (open != 0)
+ {
+ chars[i] = closeCurlyReplacement;
+ }
+ }
+ }
+ return new String(chars);
+ }
+
+ public static String recoverEnclosedCurlyBraces(String str)
+ {
+ return str.replace(openCurlyReplacement, '{').replace(closeCurlyReplacement, '}');
+ }
+
+}
\ No newline at end of file
examples/as7-eap-demo/customer-app/pom.xml 20(+0 -20)
diff --git a/examples/as7-eap-demo/customer-app/pom.xml b/examples/as7-eap-demo/customer-app/pom.xml
index 5d88ff3..3fe1702 100755
--- a/examples/as7-eap-demo/customer-app/pom.xml
+++ b/examples/as7-eap-demo/customer-app/pom.xml
@@ -29,26 +29,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-client</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-adapter-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>
<version>${project.version}</version>
diff --git a/examples/as7-eap-demo/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java b/examples/as7-eap-demo/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
new file mode 100755
index 0000000..6da7843
--- /dev/null
+++ b/examples/as7-eap-demo/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
@@ -0,0 +1,49 @@
+package org.keycloak.example;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.util.JsonSerialization;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CustomerDatabaseClient {
+
+ static class TypedList extends ArrayList<String> {}
+
+ public static List<String> getCustomers() {
+ SkeletonKeySession session = SkeletonKeySession.getContext();
+ HttpClient client = new HttpClientBuilder()
+ .trustStore(session.getMetadata().getTruststore())
+ .hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.ANY).build();
+ try {
+ HttpGet get = new HttpGet("http://localhost:8080/database/customers");
+ get.addHeader("Authorization", "Bearer " + session.getTokenString());
+ try {
+ HttpResponse response = client.execute(get);
+ HttpEntity entity = response.getEntity();
+ InputStream is = entity.getContent();
+ try {
+ return JsonSerialization.readValue(is, TypedList.class);
+ } finally {
+ is.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+ }
+}
diff --git a/examples/as7-eap-demo/customer-app/src/main/webapp/admin/admin.jsp b/examples/as7-eap-demo/customer-app/src/main/webapp/admin/admin.jsp
old mode 100644
new mode 100755
index e132e37..39c2a43
--- a/examples/as7-eap-demo/customer-app/src/main/webapp/admin/admin.jsp
+++ b/examples/as7-eap-demo/customer-app/src/main/webapp/admin/admin.jsp
@@ -2,7 +2,7 @@
pageEncoding="ISO-8859-1"%>
<html>
<head>
- <title>Customer Admin Iterface</title>
+ <title>Customer Admin Interface</title>
</head>
<body bgcolor="#E3F6CE">
<h1>Customer Admin Interface</h1>
diff --git a/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp b/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp
index 7eb5f58..344bd3e 100755
--- a/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp
+++ b/examples/as7-eap-demo/customer-app/src/main/webapp/customers/view.jsp
@@ -1,20 +1,22 @@
-<%@ page import="javax.ws.rs.core.UriBuilder" language="java" contentType="text/html; charset=ISO-8859-1"
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
+<%@ page import="org.keycloak.example.CustomerDatabaseClient" %>
+<%@ page import="org.keycloak.util.KeycloakUriBuilder" %>
<html>
<head>
<title>Customer View Page</title>
</head>
<body bgcolor="#E3F6CE">
<%
- String logoutUri = UriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/tokens/logout")
- .queryParam("redirect_uri", "http://localhost:8080/customer-portal").build().toString();
- String acctUri = UriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/account").build().toString();
+ String logoutUri = KeycloakUriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/tokens/logout")
+ .queryParam("redirect_uri", "http://localhost:8080/customer-portal").build().toString();
+ String acctUri = "http://localhost:8080/auth-server/rest/realms/demo/account";
%>
<p>Goto: <a href="http://localhost:8080/product-portal">products</a> | <a href="<%=logoutUri%>">logout</a> | <a href="<%=acctUri%>">manage acct</a></p>
User <b><%=request.getUserPrincipal().getName()%></b> made this request.
<h2>Customer Listing</h2>
<%
-java.util.List<String> list = org.jboss.resteasy.example.oauth.CustomerDatabaseClient.getCustomers(request);
+java.util.List<String> list = CustomerDatabaseClient.getCustomers();
for (String cust : list)
{
out.print("<p>");
diff --git a/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
index 697d61c..c54e4ab 100755
--- a/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
+++ b/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -2,9 +2,6 @@
<deployment>
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<dependencies>
- <module name="org.bouncycastle"/>
- <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
- <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml b/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml
index 3cec19c..a28a265 100755
--- a/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml
+++ b/examples/as7-eap-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml
@@ -1,5 +1,5 @@
<jboss-web>
<valve>
- <class-name>org.keycloak.adapters.as7.OAuthManagedResourceValve</class-name>
+ <class-name>org.keycloak.adapters.as7.OAuthAuthenticatorValve</class-name>
</valve>
</jboss-web>
\ No newline at end of file
examples/as7-eap-demo/database-service/pom.xml 25(+12 -13)
diff --git a/examples/as7-eap-demo/database-service/pom.xml b/examples/as7-eap-demo/database-service/pom.xml
index 57ef592..fe9ea5a 100755
--- a/examples/as7-eap-demo/database-service/pom.xml
+++ b/examples/as7-eap-demo/database-service/pom.xml
@@ -30,24 +30,23 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-adapter-core</artifactId>
- <version>${project.version}</version>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>provided</scope>
</dependency>
+
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>
<version>${project.version}</version>
+ <!--
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </exclusion>
+ </exclusions>
+ -->
</dependency>
</dependencies>
diff --git a/examples/as7-eap-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/as7-eap-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
index 45b708c..c54e4ab 100755
--- a/examples/as7-eap-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
+++ b/examples/as7-eap-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -2,7 +2,6 @@
<deployment>
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<dependencies>
- <module name="org.bouncycastle"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
\ No newline at end of file
examples/as7-eap-demo/product-app/pom.xml 20(+0 -20)
diff --git a/examples/as7-eap-demo/product-app/pom.xml b/examples/as7-eap-demo/product-app/pom.xml
index 5054870..6e8bf03 100755
--- a/examples/as7-eap-demo/product-app/pom.xml
+++ b/examples/as7-eap-demo/product-app/pom.xml
@@ -29,26 +29,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-client</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-adapter-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>
<version>${project.version}</version>
diff --git a/examples/as7-eap-demo/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java b/examples/as7-eap-demo/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java
new file mode 100755
index 0000000..61dcec6
--- /dev/null
+++ b/examples/as7-eap-demo/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java
@@ -0,0 +1,49 @@
+package org.keycloak.example.oauth;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProductDatabaseClient
+{
+ static class TypedList extends ArrayList<String> {}
+
+ public static List<String> getProducts() {
+ SkeletonKeySession session = SkeletonKeySession.getContext();
+ HttpClient client = new HttpClientBuilder()
+ .trustStore(session.getMetadata().getTruststore())
+ .hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.ANY).build();
+ try {
+ HttpGet get = new HttpGet("http://localhost:8080/database/products");
+ get.addHeader("Authorization", "Bearer " + session.getTokenString());
+ try {
+ HttpResponse response = client.execute(get);
+ HttpEntity entity = response.getEntity();
+ InputStream is = entity.getContent();
+ try {
+ return JsonSerialization.readValue(is, TypedList.class);
+ } finally {
+ is.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+ }
+
+}
diff --git a/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp b/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp
index 88c6493..cd3d8d0 100755
--- a/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp
+++ b/examples/as7-eap-demo/product-app/src/main/webapp/products/view.jsp
@@ -1,21 +1,23 @@
-<%@ page import="javax.ws.rs.core.UriBuilder" language="java" contentType="text/html; charset=ISO-8859-1"
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
+<%@ page import="org.keycloak.example.oauth.ProductDatabaseClient" %>
+<%@ page import="org.keycloak.util.KeycloakUriBuilder" %>
<html>
<head>
<title>Product View Page</title>
</head>
<body bgcolor="#F5F6CE">
<%
- String logoutUri = UriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/tokens/logout")
+ String logoutUri = KeycloakUriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/tokens/logout")
.queryParam("redirect_uri", "http://localhost:8080/product-portal").build().toString();
- String acctUri = UriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/account").build().toString();
+ String acctUri = "http://localhost:8080/auth-server/rest/realms/demo/account";
%>
<p>Goto: <a href="http://localhost:8080/customer-portal">customers</a> | <a href="<%=logoutUri%>">logout</a> | <a href="<%=acctUri%>">manage acct</a></p>
User <b><%=request.getUserPrincipal().getName()%></b> made this request.
<h2>Product Listing</h2>
<%
-java.util.List<String> list = org.jboss.resteasy.example.oauth.ProductDatabaseClient.getProducts(request);
+java.util.List<String> list = ProductDatabaseClient.getProducts();
for (String cust : list)
{
out.print("<p>");
diff --git a/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml b/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml
index 3cec19c..a28a265 100755
--- a/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml
+++ b/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml
@@ -1,5 +1,5 @@
<jboss-web>
<valve>
- <class-name>org.keycloak.adapters.as7.OAuthManagedResourceValve</class-name>
+ <class-name>org.keycloak.adapters.as7.OAuthAuthenticatorValve</class-name>
</valve>
</jboss-web>
\ No newline at end of file
examples/as7-eap-demo/third-party/pom.xml 12(+1 -11)
diff --git a/examples/as7-eap-demo/third-party/pom.xml b/examples/as7-eap-demo/third-party/pom.xml
index 04308a9..89372bc 100755
--- a/examples/as7-eap-demo/third-party/pom.xml
+++ b/examples/as7-eap-demo/third-party/pom.xml
@@ -22,18 +22,8 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-client</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
+ <artifactId>keycloak-servlet-oauth-client</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
diff --git a/examples/as7-eap-demo/third-party/src/main/webapp/pull_data.jsp b/examples/as7-eap-demo/third-party/src/main/webapp/pull_data.jsp
old mode 100644
new mode 100755
index 63ad9d9..a64f674
--- a/examples/as7-eap-demo/third-party/src/main/webapp/pull_data.jsp
+++ b/examples/as7-eap-demo/third-party/src/main/webapp/pull_data.jsp
@@ -1,3 +1,4 @@
+<%@ page import="org.keycloak.example.oauth.ProductDatabaseClient" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
@@ -7,7 +8,7 @@
<body>
<h2>Pulled Product Listing</h2>
<%
-java.util.List<String> list = org.jboss.resteasy.example.oauth.ProductDatabaseClient.getProducts(request);
+java.util.List<String> list = ProductDatabaseClient.getProducts(request);
for (String prod : list)
{
out.print("<p>");
diff --git a/examples/as7-eap-demo/third-party/src/main/webapp/redirect.jsp b/examples/as7-eap-demo/third-party/src/main/webapp/redirect.jsp
old mode 100644
new mode 100755
index 35ff870..c74a9ae
--- a/examples/as7-eap-demo/third-party/src/main/webapp/redirect.jsp
+++ b/examples/as7-eap-demo/third-party/src/main/webapp/redirect.jsp
@@ -1,3 +1,3 @@
-<%
- org.jboss.resteasy.example.oauth.ProductDatabaseClient.redirect(request, response);
+<%@ page import="org.keycloak.example.oauth.ProductDatabaseClient" %><%
+ ProductDatabaseClient.redirect(request, response);
%>
\ No newline at end of file
diff --git a/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
index 74f5dff..c54e4ab 100755
--- a/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
+++ b/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -2,8 +2,6 @@
<deployment>
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<dependencies>
- <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
- <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/web.xml b/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/web.xml
index 501b203..958839d 100755
--- a/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/web.xml
+++ b/examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/web.xml
@@ -7,7 +7,7 @@
<module-name>oauth-client</module-name>
<listener>
- <listener-class>org.jboss.resteasy.example.oauth.Bootstrap</listener-class>
+ <listener-class>org.keycloak.example.oauth.Bootstrap</listener-class>
</listener>
<!--
<security-constraint>
integration/adapter-core/pom.xml 13(+6 -7)
diff --git a/integration/adapter-core/pom.xml b/integration/adapter-core/pom.xml
index 9cda78b..23d9114 100755
--- a/integration/adapter-core/pom.xml
+++ b/integration/adapter-core/pom.xml
@@ -22,14 +22,17 @@
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -37,13 +40,9 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-client</artifactId>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/config/AdapterConfig.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/config/AdapterConfig.java
index ee038d8..5bdaf5b 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/config/AdapterConfig.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/config/AdapterConfig.java
@@ -21,7 +21,7 @@ public class AdapterConfig
protected String resource;
@JsonProperty("realm-public-key")
protected String realmKey;
- @JsonProperty("auth-url")
+ @JsonProperty("auth-url")
protected String authUrl;
@JsonProperty("code-url")
protected String codeUrl;
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java
new file mode 100755
index 0000000..3f1d473
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java
@@ -0,0 +1,274 @@
+package org.keycloak.adapters;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Abstraction for creating HttpClients. Allows SSL configuration.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class HttpClientBuilder {
+ public static enum HostnameVerificationPolicy {
+ /**
+ * Hostname verification is not done on the server's certificate
+ */
+ ANY,
+ /**
+ * Allows wildcards in subdomain names i.e. *.foo.com
+ */
+ WILDCARD,
+ /**
+ * CN must match hostname connecting to
+ */
+ STRICT
+ }
+
+
+ /**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+ private static class PassthroughTrustManager implements X509TrustManager {
+ public void checkClientTrusted(X509Certificate[] chain,
+ String authType) throws CertificateException {
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain,
+ String authType) throws CertificateException {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+
+ protected KeyStore truststore;
+ protected KeyStore clientKeyStore;
+ protected String clientPrivateKeyPassword;
+ protected boolean disableTrustManager;
+ protected HostnameVerificationPolicy policy = HostnameVerificationPolicy.WILDCARD;
+ protected SSLContext sslContext;
+ protected int connectionPoolSize = 100;
+ protected int maxPooledPerRoute = 0;
+ protected long connectionTTL = -1;
+ protected TimeUnit connectionTTLUnit = TimeUnit.MILLISECONDS;
+ protected HostnameVerifier verifier = null;
+ protected long socketTimeout = -1;
+ protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
+ protected long establishConnectionTimeout = -1;
+ protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
+
+
+ /**
+ * Socket inactivity timeout
+ *
+ * @param timeout
+ * @param unit
+ * @return
+ */
+ public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit)
+ {
+ this.socketTimeout = timeout;
+ this.socketTimeoutUnits = unit;
+ return this;
+ }
+
+ /**
+ * When trying to make an initial socket connection, what is the timeout?
+ *
+ * @param timeout
+ * @param unit
+ * @return
+ */
+ public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit)
+ {
+ this.establishConnectionTimeout = timeout;
+ this.establishConnectionTimeoutUnits = unit;
+ return this;
+ }
+
+ public HttpClientBuilder connectionTTL(long ttl, TimeUnit unit) {
+ this.connectionTTL = ttl;
+ this.connectionTTLUnit = unit;
+ return this;
+ }
+
+ public HttpClientBuilder maxPooledPerRoute(int maxPooledPerRoute) {
+ this.maxPooledPerRoute = maxPooledPerRoute;
+ return this;
+ }
+
+ public HttpClientBuilder connectionPoolSize(int connectionPoolSize) {
+ this.connectionPoolSize = connectionPoolSize;
+ return this;
+ }
+
+ /**
+ * Disable trust management and hostname verification. <i>NOTE</i> this is a security
+ * hole, so only set this option if you cannot or do not want to verify the identity of the
+ * host you are communicating with.
+ */
+ public HttpClientBuilder disableTrustManager() {
+ this.disableTrustManager = true;
+ return this;
+ }
+
+ /**
+ * SSL policy used to verify hostnames
+ *
+ * @param policy
+ * @return
+ */
+ public HttpClientBuilder hostnameVerification(HostnameVerificationPolicy policy) {
+ this.policy = policy;
+ return this;
+ }
+
+
+ public HttpClientBuilder sslContext(SSLContext sslContext) {
+ this.sslContext = sslContext;
+ return this;
+ }
+
+ public HttpClientBuilder trustStore(KeyStore truststore) {
+ this.truststore = truststore;
+ return this;
+ }
+
+ public HttpClientBuilder keyStore(KeyStore keyStore, String password) {
+ this.clientKeyStore = keyStore;
+ this.clientPrivateKeyPassword = password;
+ return this;
+ }
+
+ public HttpClientBuilder keyStore(KeyStore keyStore, char[] password) {
+ this.clientKeyStore = keyStore;
+ this.clientPrivateKeyPassword = new String(password);
+ return this;
+ }
+
+
+ static class VerifierWrapper implements X509HostnameVerifier {
+ protected HostnameVerifier verifier;
+
+ VerifierWrapper(HostnameVerifier verifier) {
+ this.verifier = verifier;
+ }
+
+ @Override
+ public void verify(String host, SSLSocket ssl) throws IOException {
+ if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure");
+ }
+
+ @Override
+ public void verify(String host, X509Certificate cert) throws SSLException {
+ throw new SSLException("This verification path not implemented");
+ }
+
+ @Override
+ public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
+ throw new SSLException("This verification path not implemented");
+ }
+
+ @Override
+ public boolean verify(String s, SSLSession sslSession) {
+ return verifier.verify(s, sslSession);
+ }
+ }
+
+ public HttpClient build() {
+ X509HostnameVerifier verifier = null;
+ if (this.verifier != null) verifier = new VerifierWrapper(this.verifier);
+ else {
+ switch (policy) {
+ case ANY:
+ verifier = new AllowAllHostnameVerifier();
+ break;
+ case WILDCARD:
+ verifier = new BrowserCompatHostnameVerifier();
+ break;
+ case STRICT:
+ verifier = new StrictHostnameVerifier();
+ break;
+ }
+ }
+ try {
+ SSLSocketFactory sslsf = null;
+ SSLContext theContext = sslContext;
+ if (disableTrustManager) {
+ theContext = SSLContext.getInstance("SSL");
+ theContext.init(null, new TrustManager[]{new PassthroughTrustManager()},
+ new SecureRandom());
+ verifier = new AllowAllHostnameVerifier();
+ sslsf = new SSLSocketFactory(theContext, verifier);
+ } else if (theContext != null) {
+ sslsf = new SSLSocketFactory(theContext, verifier);
+ } else if (clientKeyStore != null || truststore != null) {
+ sslsf = new SSLSocketFactory(SSLSocketFactory.TLS, clientKeyStore, clientPrivateKeyPassword, truststore, null, verifier);
+ } else {
+ final SSLContext tlsContext = SSLContext.getInstance(SSLSocketFactory.TLS);
+ tlsContext.init(null, null, null);
+ sslsf = new SSLSocketFactory(tlsContext, verifier);
+ }
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(
+ new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
+ Scheme httpsScheme = new Scheme("https", 443, sslsf);
+ registry.register(httpsScheme);
+ ClientConnectionManager cm = null;
+ if (connectionPoolSize > 0) {
+ ThreadSafeClientConnManager tcm = new ThreadSafeClientConnManager(registry, connectionTTL, connectionTTLUnit);
+ tcm.setMaxTotal(connectionPoolSize);
+ if (maxPooledPerRoute == 0) maxPooledPerRoute = connectionPoolSize;
+ tcm.setDefaultMaxPerRoute(maxPooledPerRoute);
+ cm = tcm;
+
+ } else {
+ cm = new SingleClientConnManager(registry);
+ }
+ BasicHttpParams params = new BasicHttpParams();
+ if (socketTimeout > -1)
+ {
+ HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
+
+ }
+ if (establishConnectionTimeout > -1)
+ {
+ HttpConnectionParams.setConnectionTimeout(params, (int)establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
+ }
+ return new DefaultHttpClient(cm, params);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java
new file mode 100755
index 0000000..469ab06
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java
@@ -0,0 +1,140 @@
+package org.keycloak.adapters;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.message.BasicNameValuePair;
+import org.keycloak.adapters.config.RealmConfiguration;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.KeycloakUriBuilder;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class TokenGrantRequest {
+
+ public static class HttpFailure extends Exception {
+ private int status;
+ private String error;
+
+ public HttpFailure(int status, String error) {
+ this.status = status;
+ this.error = error;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public String getError() {
+ return error;
+ }
+ }
+
+ public static AccessTokenResponse invoke(RealmConfiguration config, String code, String redirectUri) throws HttpFailure, IOException {
+ String codeUrl = config.getCodeUrl();
+ String client_id = config.getMetadata().getResourceName();
+ Map<String,String> credentials = config.getResourceCredentials();
+ HttpClient client = config.getClient();
+
+ return invoke(client, code, codeUrl, redirectUri, client_id, credentials);
+ }
+
+ public static AccessTokenResponse invoke(HttpClient client, String code, String codeUrl, String redirectUri, String client_id, Map<String, String> credentials) throws IOException, HttpFailure {
+ List<NameValuePair> formparams = new ArrayList<NameValuePair>();
+ redirectUri = stripOauthParametersFromRedirect(redirectUri);
+ String password = credentials.get("password");
+ formparams.add(new BasicNameValuePair("grant_type", "authorization_code"));
+ formparams.add(new BasicNameValuePair("code", code));
+ formparams.add(new BasicNameValuePair("client_id", client_id));
+ formparams.add(new BasicNameValuePair(CredentialRepresentation.PASSWORD, password));
+ formparams.add(new BasicNameValuePair("redirect_uri", redirectUri));
+ HttpResponse response = null;
+ UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
+ HttpPost post = new HttpPost(codeUrl);
+ post.setEntity(form);
+ response = client.execute(post);
+ int status = response.getStatusLine().getStatusCode();
+ HttpEntity entity = response.getEntity();
+ if (status != 200) {
+ error(status, entity);
+ }
+ if (entity == null) {
+ throw new HttpFailure(status, null);
+ }
+ InputStream is = entity.getContent();
+ try {
+ return JsonSerialization.readValue(is, AccessTokenResponse.class);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ignored) {
+
+ }
+ }
+ }
+
+
+ protected static String readString(InputStream in) throws IOException
+ {
+ char[] buffer = new char[1024];
+ StringBuilder builder = new StringBuilder();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ int wasRead = 0;
+ do
+ {
+ wasRead = reader.read(buffer, 0, 1024);
+ if (wasRead > 0)
+ {
+ builder.append(buffer, 0, wasRead);
+ }
+ }
+ while (wasRead > -1);
+
+ return builder.toString();
+ }
+
+
+ protected static void error(int status, HttpEntity entity) throws HttpFailure, IOException {
+ String body = null;
+ if (entity != null) {
+ InputStream is = entity.getContent();
+ try {
+ body = readString(is);
+ } catch (IOException e) {
+
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ignored) {
+
+ }
+ }
+ }
+ throw new HttpFailure(status, body);
+ }
+
+ protected static String stripOauthParametersFromRedirect(String uri) {
+ KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
+ .replaceQueryParam("code", null)
+ .replaceQueryParam("state", null);
+ return builder.build().toString();
+ }
+
+
+
+}
integration/as7-eap6/adapter/pom.xml 36(+19 -17)
diff --git a/integration/as7-eap6/adapter/pom.xml b/integration/as7-eap6/adapter/pom.xml
index 3950f6b..34a0ef9 100755
--- a/integration/as7-eap6/adapter/pom.xml
+++ b/integration/as7-eap6/adapter/pom.xml
@@ -23,30 +23,38 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.jboss.spec.javax.servlet</groupId>
- <artifactId>jboss-servlet-api_3.0_spec</artifactId>
- <scope>provided</scope>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1.2</version>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <scope>provided</scope>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk16</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-xc</artifactId>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-client</artifactId>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
-
<dependency>
<groupId>org.jboss.web</groupId>
<artifactId>jbossweb</artifactId>
@@ -60,12 +68,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.picketbox</groupId>
- <artifactId>picketbox</artifactId>
- <version>4.0.7.Final</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java
index 25f85db..5738b3d 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java
@@ -18,9 +18,9 @@ import java.util.Set;
/**
* Pre-installed actions that must be authenticated
- *
+ * <p/>
* Actions include:
- *
+ * <p/>
* CORS Origin Check and Response headers
* K_QUERY_BEARER_TOKEN: Get bearer token from server for Javascripts CORS requests
*
@@ -47,24 +47,24 @@ public class AuthenticatedActionsValve extends ValveBase {
if (corsRequest(request, response, session)) return;
String requestUri = request.getRequestURI();
if (requestUri.endsWith("K_QUERY_BEARER_TOKEN")) {
- queryBearerToken(request, response, session);
- return;
+ queryBearerToken(request, response, session);
+ return;
}
getNext().invoke(request, response);
}
public SkeletonKeySession getSkeletonKeySession(Request request) {
- SkeletonKeySession skSession = (SkeletonKeySession)request.getAttribute(SkeletonKeySession.class.getName());
+ SkeletonKeySession skSession = (SkeletonKeySession) request.getAttribute(SkeletonKeySession.class.getName());
if (skSession != null) return skSession;
Session session = request.getSessionInternal();
if (session != null) {
- return (SkeletonKeySession) session.getNote(SkeletonKeySession.class.getName());
+ return (SkeletonKeySession) session.getNote(SkeletonKeySession.class.getName());
}
return null;
}
protected void queryBearerToken(Request request, Response response, SkeletonKeySession session) throws IOException, ServletException {
- log.debugv("queryBearerToken {0}",request.getRequestURI());
+ log.debugv("queryBearerToken {0}", request.getRequestURI());
if (abortTokenResponse(request, response, session)) return;
response.setStatus(200);
response.setContentType("text/plain");
@@ -75,7 +75,7 @@ public class AuthenticatedActionsValve extends ValveBase {
protected boolean abortTokenResponse(Request request, Response response, SkeletonKeySession session) throws IOException {
if (session == null) {
- log.debugv("session was null, sending back 401: {0}",request.getRequestURI());
+ log.debugv("session was null, sending back 401: {0}", request.getRequestURI());
response.sendError(401);
return true;
}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
index 95f7b53..2f5e379 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
@@ -10,8 +10,8 @@ import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.deploy.LoginConfig;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.as7.config.CatalinaAdapterConfigLoader;
import org.keycloak.adapters.config.AdapterConfig;
import org.keycloak.adapters.config.AdapterConfigLoader;
@@ -63,7 +63,7 @@ public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements
}
super.invoke(request, response);
} finally {
- ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession
+ SkeletonKeySession.clearContext();
}
}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
index 3d8bd0d..5745444 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
@@ -2,7 +2,6 @@ package org.keycloak.adapters.as7;
import org.apache.catalina.connector.Request;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RSATokenVerifier;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
@@ -109,7 +108,7 @@ public class CatalinaBearerTokenAuthenticator {
request.setAuthType("OAUTH_BEARER");
SkeletonKeySession skSession = new SkeletonKeySession(tokenString, token, resourceMetadata);
request.setAttribute(SkeletonKeySession.class.getName(), skSession);
- ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
+ SkeletonKeySession.pushContext(skSession);
return true;
}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java
index 64c509a..604a2ed 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java
@@ -1,13 +1,13 @@
package org.keycloak.adapters.as7.config;
import org.apache.catalina.Context;
+import org.keycloak.adapters.config.RealmConfigurationLoader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
-public class CatalinaAdapterConfigLoader extends RealmConfigurationLoader
-{
+public class CatalinaAdapterConfigLoader extends RealmConfigurationLoader {
public CatalinaAdapterConfigLoader(Context context) {
InputStream is = null;
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
index 75648b1..8d21b67 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
@@ -2,19 +2,16 @@ package org.keycloak.adapters.as7;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
-import org.keycloak.adapters.RealmConfiguration;
import org.keycloak.VerificationException;
+import org.keycloak.adapters.TokenGrantRequest;
+import org.keycloak.adapters.config.RealmConfiguration;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyToken;
-import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.util.KeycloakUriBuilder;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.Form;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
@@ -137,7 +134,7 @@ public class ServletOAuthLogin {
// disabled?
return null;
}
- UriBuilder secureUrl = UriBuilder.fromUri(url).scheme("https").port(-1);
+ KeycloakUriBuilder secureUrl = KeycloakUriBuilder.fromUri(url).scheme("https").port(-1);
if (port != 443) secureUrl.port(port);
url = secureUrl.build().toString();
}
@@ -159,7 +156,7 @@ public class ServletOAuthLogin {
String state = getStateCode();
String redirect = getRedirectUri(state);
if (redirect == null) {
- sendError(Response.Status.FORBIDDEN.getStatusCode());
+ sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
setCookie(realmInfo.getStateCookieName(), state, null, getDefaultCookiePath(), realmInfo.isSslRequired());
@@ -216,42 +213,28 @@ public class ServletOAuthLogin {
// abort if not HTTPS
if (realmInfo.isSslRequired() && !isRequestSecure()) {
log.error("SSL is required");
- sendError(Response.Status.FORBIDDEN.getStatusCode());
+ sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
if (!checkStateCookie()) return false;
- String client_id = realmInfo.getMetadata().getResourceName();
- String password = realmInfo.getResourceCredentials().asMap().getFirst("password");
- //String authHeader = BasicAuthHelper.createHeader(client_id, password);
String redirectUri = stripOauthParametersFromRedirect();
- Form form = new Form();
- form.param("grant_type", "authorization_code")
- .param("code", code)
- .param("client_id", client_id)
- .param(CredentialRepresentation.PASSWORD, password)
- .param("redirect_uri", redirectUri);
-
- Response res = realmInfo.getCodeUrl().request()
- //.header(HttpHeaders.AUTHORIZATION, authHeader)
- .post(Entity.form(form));
- AccessTokenResponse tokenResponse;
+ AccessTokenResponse tokenResponse = null;
try {
- if (res.getStatus() != 200) {
- log.error("failed to turn code into token");
- log.error("status from server: " + res.getStatus());
- if (res.getStatus() == 400 && res.getMediaType() != null) {
- log.error(" " + res.readEntity(String.class));
- }
- sendError(Response.Status.FORBIDDEN.getStatusCode());
- return false;
+ tokenResponse = TokenGrantRequest.invoke(realmInfo, code, redirectUri);
+ } catch (TokenGrantRequest.HttpFailure failure) {
+ log.error("failed to turn code into token");
+ log.error("status from server: " + failure.getStatus());
+ if (failure.getStatus() == 400 && failure.getError() != null) {
+ log.error(" " + failure.getError());
}
- log.debug("media type: " + res.getMediaType());
- log.debug("Content-Type header: " + res.getHeaderString("Content-Type"));
- tokenResponse = res.readEntity(AccessTokenResponse.class);
- } finally {
- res.close();
+ sendError(HttpServletResponse.SC_FORBIDDEN);
+ return false;
+
+ } catch (IOException e) {
+ log.error("failed to turn code into token");
+ sendError(HttpServletResponse.SC_FORBIDDEN);
}
tokenString = tokenResponse.getToken();
@@ -260,7 +243,7 @@ public class ServletOAuthLogin {
log.debug("Token Verification succeeded!");
} catch (VerificationException e) {
log.error("failed verification of token");
- sendError(Response.Status.FORBIDDEN.getStatusCode());
+ sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
// redirect to URL without oauth query parameters
@@ -273,7 +256,7 @@ public class ServletOAuthLogin {
*/
protected String stripOauthParametersFromRedirect() {
StringBuffer buf = request.getRequestURL().append("?").append(request.getQueryString());
- UriBuilder builder = UriBuilder.fromUri(buf.toString())
+ KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(buf.toString())
.replaceQueryParam("code", null)
.replaceQueryParam("state", null);
return builder.build().toString();
integration/jaxrs-oauth-client/pom.xml 71(+71 -0)
diff --git a/integration/jaxrs-oauth-client/pom.xml b/integration/jaxrs-oauth-client/pom.xml
new file mode 100755
index 0000000..65282b5
--- /dev/null
+++ b/integration/jaxrs-oauth-client/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<project>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-alpha-1-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-jaxrs-oauth-client</artifactId>
+ <name>Keycloak JAX-RS OAuth Client</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-client</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jackson-provider</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
integration/pom.xml 2(+2 -0)
diff --git a/integration/pom.xml b/integration/pom.xml
index 2100379..5512c45 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -16,6 +16,8 @@
<modules>
<module>adapter-core</module>
+ <module>jaxrs-oauth-client</module>
+ <module>servlet-oauth-client</module>
<module>as7-eap6/adapter</module>
<module>undertow</module>
<!-- <module>as7-eap6/jboss-modules</module> -->
integration/servlet-oauth-client/pom.xml 67(+67 -0)
diff --git a/integration/servlet-oauth-client/pom.xml b/integration/servlet-oauth-client/pom.xml
new file mode 100755
index 0000000..038f1ab
--- /dev/null
+++ b/integration/servlet-oauth-client/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<project>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-alpha-1-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-servlet-oauth-client</artifactId>
+ <name>Keycloak Servlet OAuth Client</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk16</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
integration/undertow/pom.xml 29(+19 -10)
diff --git a/integration/undertow/pom.xml b/integration/undertow/pom.xml
index 3060d39..f3ae801 100755
--- a/integration/undertow/pom.xml
+++ b/integration/undertow/pom.xml
@@ -23,27 +23,36 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.jboss.spec.javax.servlet</groupId>
- <artifactId>jboss-servlet-api_3.0_spec</artifactId>
- <scope>provided</scope>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1.2</version>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs</artifactId>
- <scope>provided</scope>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk16</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-client</artifactId>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-xc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
index ef7120e..749c6b0 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
@@ -6,7 +6,7 @@ import io.undertow.security.idm.Account;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.AttachmentKey;
import org.jboss.logging.Logger;
-import org.keycloak.adapters.RealmConfiguration;
+import org.keycloak.adapters.config.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeyPrincipal;
import org.keycloak.SkeletonKeySession;
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
index 16f71bd..02a39d4 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
@@ -8,6 +8,7 @@ import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.ServletSessionConfig;
import org.jboss.logging.Logger;
import org.keycloak.adapters.config.AdapterConfig;
+import org.keycloak.adapters.config.RealmConfigurationLoader;
import javax.servlet.ServletContext;
import java.io.InputStream;
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
index 8806ac6..433feb2 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
@@ -8,16 +8,14 @@ import io.undertow.server.handlers.CookieImpl;
import io.undertow.util.Headers;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
-import org.keycloak.adapters.RealmConfiguration;
+import org.keycloak.adapters.config.RealmConfiguration;
import org.keycloak.VerificationException;
+import org.keycloak.adapters.TokenGrantRequest;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyToken;
-import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.util.KeycloakUriBuilder;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.core.Form;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
import java.util.Deque;
import java.util.Map;
import java.util.UUID;
@@ -60,7 +58,7 @@ public class OAuthAuthenticator {
}
protected String getRequestUrl() {
- UriBuilder uriBuilder = UriBuilder.fromUri(exchange.getRequestURI())
+ KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
.replaceQuery(exchange.getQueryString());
if (!exchange.isHostIncludedInRequestURI()) uriBuilder.scheme(exchange.getRequestScheme()).host(exchange.getHostAndPort());
return uriBuilder.build().toString();
@@ -107,7 +105,7 @@ public class OAuthAuthenticator {
// disabled?
return null;
}
- UriBuilder secureUrl = UriBuilder.fromUri(url).scheme("https").port(-1);
+ KeycloakUriBuilder secureUrl = KeycloakUriBuilder.fromUri(url).scheme("https").port(-1);
if (port != 443) secureUrl.port(port);
url = secureUrl.build().toString();
}
@@ -237,34 +235,20 @@ public class OAuthAuthenticator {
KeycloakChallenge challenge = checkStateCookie();
if (challenge != null) return challenge;
- String client_id = realmInfo.getMetadata().getResourceName();
- String password = realmInfo.getResourceCredentials().asMap().getFirst("password");
- //String authHeader = BasicAuthHelper.createHeader(client_id, password);
- redirectUri = stripOauthParametersFromRedirect();
- Form form = new Form();
- form.param("grant_type", "authorization_code")
- .param("code", code)
- .param("client_id", client_id)
- .param(CredentialRepresentation.PASSWORD, password)
- .param("redirect_uri", redirectUri);
-
- Response res = realmInfo.getCodeUrl().request()
- .post(Entity.form(form));
- AccessTokenResponse tokenResponse;
+ AccessTokenResponse tokenResponse = null;
try {
- if (res.getStatus() != 200) {
- log.error("failed to turn code into token");
- log.error("status from server: " + res.getStatus());
- if (res.getStatus() == 400 && res.getMediaType() != null) {
- log.error(" " + res.readEntity(String.class));
- }
- return challenge(403);
+ tokenResponse = TokenGrantRequest.invoke(realmInfo, code, redirectUri);
+ } catch (TokenGrantRequest.HttpFailure failure) {
+ log.error("failed to turn code into token");
+ log.error("status from server: " + failure.getStatus());
+ if (failure.getStatus() == 400 && failure.getError() != null) {
+ log.error(" " + failure.getError());
}
- log.debug("media type: " + res.getMediaType());
- log.debug("Content-Type header: " + res.getHeaderString("Content-Type"));
- tokenResponse = res.readEntity(AccessTokenResponse.class);
- } finally {
- res.close();
+ return challenge(403);
+
+ } catch (IOException e) {
+ log.error("failed to turn code into token");
+ return challenge(403);
}
tokenString = tokenResponse.getToken();
@@ -283,7 +267,7 @@ public class OAuthAuthenticator {
* strip out unwanted query parameters and redirect so bookmarks don't retain oauth protocol bits
*/
protected String stripOauthParametersFromRedirect() {
- UriBuilder builder = UriBuilder.fromUri(exchange.getRequestURI())
+ KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
.replaceQuery(exchange.getQueryString())
.replaceQueryParam("code", null)
.replaceQueryParam("state", null);
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java
index 5c33717..9df6863 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java
@@ -3,7 +3,7 @@ package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager;
import io.undertow.servlet.handlers.ServletRequestContext;
-import org.keycloak.adapters.RealmConfiguration;
+import org.keycloak.adapters.config.RealmConfiguration;
import org.keycloak.ResourceMetadata;
import org.keycloak.SkeletonKeySession;
import org.keycloak.adapters.config.AdapterConfig;
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java
index ce807a6..e891a1d 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java
@@ -2,7 +2,7 @@ package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager;
-import org.keycloak.adapters.RealmConfiguration;
+import org.keycloak.adapters.config.RealmConfiguration;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
pom.xml 11(+9 -2)
diff --git a/pom.xml b/pom.xml
index fc62fe8..0921449 100755
--- a/pom.xml
+++ b/pom.xml
@@ -76,8 +76,8 @@
<module>core</module>
<module>core-jaxrs</module>
<module>model</module>
- <module>services</module>
<module>integration</module>
+ <module>services</module>
<module>social</module>
<module>forms</module>
<module>admin-ui-styles</module>
@@ -327,7 +327,14 @@
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
-
+ <!-- Keep this at 4.1.2 to make sure we're compatible with AS7 -->
+ <!-- the dependency seems to override Resteasy 3.0.5's dependeing on 4.2.1
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1.2</version>
+ </dependency>
+ -->
</dependencies>
</dependencyManagement>
services/pom.xml 16(+11 -5)
diff --git a/services/pom.xml b/services/pom.xml
index 56e8481..3c1e0fb 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -37,6 +37,12 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-jaxrs-oauth-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version>
<scope>test</scope>
@@ -83,7 +89,7 @@
</exclusion>
</exclusions>
</dependency>
- <dependency>
+ <dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<scope>provided</scope>
@@ -162,11 +168,11 @@
<artifactId>hibernate-entitymanager</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.icegreen</groupId>
- <artifactId>greenmail</artifactId>
+ <dependency>
+ <groupId>com.icegreen</groupId>
+ <artifactId>greenmail</artifactId>
<scope>test</scope>
- </dependency>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java
index f6cc0d0..f6a4911 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java
@@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
+import java.net.URI;
import java.nio.charset.Charset;
import java.util.List;
@@ -34,7 +35,13 @@ public class DummySocialServlet extends HttpServlet {
String state = null;
String redirectUri = null;
- List<NameValuePair> query = URLEncodedUtils.parse(req.getQueryString(), Charset.forName("UTF-8"));
+ List<NameValuePair> query = null;
+ try {
+ URI uri = URI.create(req.getRequestURL().append('?').append(req.getQueryString()).toString());
+ query = URLEncodedUtils.parse(uri, "UTF-8");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
for (NameValuePair p : query) {
if ("state".equals(p.getName())) {
state = p.getValue();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 6b6e1d5..49acb71 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -43,6 +43,7 @@ import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.core.UriBuilder;
+import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
@@ -127,7 +128,12 @@ public class OAuthClient {
parameters.add(new BasicNameValuePair("password", password));
}
- UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, Charset.forName("UTF-8"));
+ UrlEncodedFormEntity formEntity = null;
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
post.setEntity(formEntity);
try {