diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-menu.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-menu.html
index f527953c52..f34df5f1ec 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-menu.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-menu.html
@@ -9,7 +9,7 @@
Users
- Roles
+ Roles
Applications
OAuth Clients
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 45f1a42a49..2773989d18 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -173,7 +173,7 @@ public class AuthenticationManager {
public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers, boolean checkActive) {
logger.info("authenticateIdentityCookie");
Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE);
- if (cookie == null) {
+ if (cookie == null || "".equals(cookie.getValue())) {
logger.infov("authenticateCookie could not find cookie: {0}", KEYCLOAK_IDENTITY_COOKIE);
return null;
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
index 554ed7d6f0..f03d4102ec 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
@@ -156,7 +156,7 @@ public class UserFederationResource {
@GET
@NoCache
@Path("instances/{id}")
- @Consumes("application/json")
+ @Produces("application/json")
public UserFederationProviderRepresentation getProviderInstance(@PathParam("id") String id) {
logger.info("getProvider");
auth.requireView();
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index 68bf394117..0b10857f43 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -1,199 +1,203 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2012, Red Hat, Inc., and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.keycloak.services.resources.flows;
-
-import org.jboss.logging.Logger;
-import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
-import org.jboss.resteasy.spi.HttpRequest;
-import org.keycloak.ClientConnection;
-import org.keycloak.OAuth2Constants;
-import org.keycloak.audit.Audit;
-import org.keycloak.audit.Details;
-import org.keycloak.audit.EventType;
-import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.RequiredCredentialModel;
-import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserModel.RequiredAction;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.services.managers.AccessCode;
-import org.keycloak.services.managers.AuthenticationManager;
-import org.keycloak.services.managers.TokenManager;
-
-import javax.ws.rs.core.Cookie;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * @author Bill Burke
- * @author Stian Thorgersen
- */
-public class OAuthFlows {
-
- private static final Logger log = Logger.getLogger(OAuthFlows.class);
-
- private final KeycloakSession session;
-
- private final RealmModel realm;
-
- private final HttpRequest request;
-
- private final UriInfo uriInfo;
-
- private ClientConnection clientConnection;
- private final AuthenticationManager authManager;
-
- private final TokenManager tokenManager;
-
- OAuthFlows(KeycloakSession session, RealmModel realm, HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection, AuthenticationManager authManager,
- TokenManager tokenManager) {
- this.session = session;
- this.realm = realm;
- this.request = request;
- this.uriInfo = uriInfo;
- this.clientConnection = clientConnection;
- this.authManager = authManager;
- this.tokenManager = tokenManager;
- }
-
- public Response redirectAccessCode(AccessCode accessCode, UserSessionModel userSession, String state, String redirect) {
- String code = accessCode.getCode();
- UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
- log.debugv("redirectAccessCode: state: {0}", state);
- if (state != null)
- redirectUri.queryParam(OAuth2Constants.STATE, state);
- Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
- Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
-
- Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
- if (sessionCookie != null) {
- String oldSessionId = sessionCookie.getValue().split("/")[2];
- if (!oldSessionId.equals(userSession.getId())) {
- UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
- if (oldSession != null) {
- log.debugv("Removing old user session: session: {0}", oldSessionId);
- session.sessions().removeUserSession(realm, oldSession);
- }
- }
- }
-
- // refresh the cookies!
- authManager.createLoginCookie(realm, accessCode.getUser(), userSession, uriInfo, clientConnection);
- if (userSession.isRememberMe()) authManager.createRememberMeCookie(realm, uriInfo, clientConnection);
- return location.build();
- }
-
- public Response redirectError(ClientModel client, String error, String state, String redirect) {
- UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
- if (state != null) {
- redirectUri.queryParam(OAuth2Constants.STATE, state);
- }
- return Response.status(302).location(redirectUri.build()).build();
- }
-
- public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, Audit audit) {
- isTotpConfigurationRequired(user);
- isEmailVerificationRequired(user);
-
- boolean isResource = client instanceof ApplicationModel;
- AccessCode accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, this.session, realm, client, user, session);
-
- log.debugv("processAccessCode: isResource: {0}", isResource);
- log.debugv("processAccessCode: go to oauth page?: {0}",
- !isResource);
-
- audit.detail(Details.CODE_ID, accessCode.getCodeId());
-
- Set requiredActions = user.getRequiredActions();
- if (!requiredActions.isEmpty()) {
- RequiredAction action = user.getRequiredActions().iterator().next();
- accessCode.setRequiredAction(action);
-
- if (action.equals(RequiredAction.VERIFY_EMAIL)) {
- audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
- }
-
- return Flows.forms(this.session, realm, client, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
- .createResponse(action);
- }
-
- if (!isResource) {
- accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT);
-
- List realmRoles = new LinkedList();
- MultivaluedMap resourceRoles = new MultivaluedMapImpl();
- for (RoleModel r : accessCode.getRequestedRoles()) {
- if (r.getContainer() instanceof RealmModel) {
- realmRoles.add(r);
- } else {
- resourceRoles.add(((ApplicationModel) r.getContainer()).getName(), r);
- }
- }
-
- return Flows.forms(this.session, realm, client, uriInfo)
- .setAccessCode(accessCode.getCode())
- .setAccessRequest(realmRoles, resourceRoles)
- .setClient(client)
- .createOAuthGrant();
- }
-
- if (redirect != null) {
- audit.success();
-
- accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
- return redirectAccessCode(accessCode, session, state, redirect);
- } else {
- return null;
- }
- }
-
- public Response forwardToSecurityFailure(String message) {
- return Flows.forms(session, realm, null, uriInfo).setError(message).createErrorPage();
- }
-
- private void isTotpConfigurationRequired(UserModel user) {
- for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
- if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
- user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
- log.debug("User is required to configure totp");
- }
- }
- }
-
- private void isEmailVerificationRequired(UserModel user) {
- if (realm.isVerifyEmail() && !user.isEmailVerified()) {
- user.addRequiredAction(RequiredAction.VERIFY_EMAIL);
- log.debug("User is required to verify email");
- }
- }
-
-}
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.services.resources.flows;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.audit.Audit;
+import org.keycloak.audit.Details;
+import org.keycloak.audit.EventType;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.AccessCode;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.TokenManager;
+
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @author Stian Thorgersen
+ */
+public class OAuthFlows {
+
+ private static final Logger log = Logger.getLogger(OAuthFlows.class);
+
+ private final KeycloakSession session;
+
+ private final RealmModel realm;
+
+ private final HttpRequest request;
+
+ private final UriInfo uriInfo;
+
+ private ClientConnection clientConnection;
+ private final AuthenticationManager authManager;
+
+ private final TokenManager tokenManager;
+
+ OAuthFlows(KeycloakSession session, RealmModel realm, HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection, AuthenticationManager authManager,
+ TokenManager tokenManager) {
+ this.session = session;
+ this.realm = realm;
+ this.request = request;
+ this.uriInfo = uriInfo;
+ this.clientConnection = clientConnection;
+ this.authManager = authManager;
+ this.tokenManager = tokenManager;
+ }
+
+ public Response redirectAccessCode(AccessCode accessCode, UserSessionModel userSession, String state, String redirect) {
+ String code = accessCode.getCode();
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
+ log.debugv("redirectAccessCode: state: {0}", state);
+ if (state != null)
+ redirectUri.queryParam(OAuth2Constants.STATE, state);
+ Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
+ Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
+
+ Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
+ if (sessionCookie != null) {
+
+ String[] split = sessionCookie.getValue().split("/");
+ if (split.length >= 3) {
+ String oldSessionId = split[2];
+ if (!oldSessionId.equals(userSession.getId())) {
+ UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
+ if (oldSession != null) {
+ log.debugv("Removing old user session: session: {0}", oldSessionId);
+ session.sessions().removeUserSession(realm, oldSession);
+ }
+ }
+ }
+ }
+
+ // refresh the cookies!
+ authManager.createLoginCookie(realm, accessCode.getUser(), userSession, uriInfo, clientConnection);
+ if (userSession.isRememberMe()) authManager.createRememberMeCookie(realm, uriInfo, clientConnection);
+ return location.build();
+ }
+
+ public Response redirectError(ClientModel client, String error, String state, String redirect) {
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
+ if (state != null) {
+ redirectUri.queryParam(OAuth2Constants.STATE, state);
+ }
+ return Response.status(302).location(redirectUri.build()).build();
+ }
+
+ public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, Audit audit) {
+ isTotpConfigurationRequired(user);
+ isEmailVerificationRequired(user);
+
+ boolean isResource = client instanceof ApplicationModel;
+ AccessCode accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, this.session, realm, client, user, session);
+
+ log.debugv("processAccessCode: isResource: {0}", isResource);
+ log.debugv("processAccessCode: go to oauth page?: {0}",
+ !isResource);
+
+ audit.detail(Details.CODE_ID, accessCode.getCodeId());
+
+ Set requiredActions = user.getRequiredActions();
+ if (!requiredActions.isEmpty()) {
+ RequiredAction action = user.getRequiredActions().iterator().next();
+ accessCode.setRequiredAction(action);
+
+ if (action.equals(RequiredAction.VERIFY_EMAIL)) {
+ audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
+ }
+
+ return Flows.forms(this.session, realm, client, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
+ .createResponse(action);
+ }
+
+ if (!isResource) {
+ accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT);
+
+ List realmRoles = new LinkedList();
+ MultivaluedMap resourceRoles = new MultivaluedMapImpl();
+ for (RoleModel r : accessCode.getRequestedRoles()) {
+ if (r.getContainer() instanceof RealmModel) {
+ realmRoles.add(r);
+ } else {
+ resourceRoles.add(((ApplicationModel) r.getContainer()).getName(), r);
+ }
+ }
+
+ return Flows.forms(this.session, realm, client, uriInfo)
+ .setAccessCode(accessCode.getCode())
+ .setAccessRequest(realmRoles, resourceRoles)
+ .setClient(client)
+ .createOAuthGrant();
+ }
+
+ if (redirect != null) {
+ audit.success();
+
+ accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
+ return redirectAccessCode(accessCode, session, state, redirect);
+ } else {
+ return null;
+ }
+ }
+
+ public Response forwardToSecurityFailure(String message) {
+ return Flows.forms(session, realm, null, uriInfo).setError(message).createErrorPage();
+ }
+
+ private void isTotpConfigurationRequired(UserModel user) {
+ for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
+ if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
+ user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
+ log.debug("User is required to configure totp");
+ }
+ }
+ }
+
+ private void isEmailVerificationRequired(UserModel user) {
+ if (realm.isVerifyEmail() && !user.isEmailVerified()) {
+ user.addRequiredAction(RequiredAction.VERIFY_EMAIL);
+ log.debug("User is required to verify email");
+ }
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/util/CookieHelper.java b/services/src/main/java/org/keycloak/services/util/CookieHelper.java
index 6d0d3a4f63..3b30b237ab 100755
--- a/services/src/main/java/org/keycloak/services/util/CookieHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/CookieHelper.java
@@ -1,9 +1,9 @@
package org.keycloak.services.util;
+import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
/**
* @author Bill Burke
@@ -24,16 +24,12 @@ public class CookieHelper {
* @param httpOnly
*/
public static void addCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) {
- HttpServletResponse response = ResteasyProviderFactory.getContextData(HttpServletResponse.class);
- Cookie cookie = new Cookie(name, value);
- if (path != null) cookie.setPath(path);
- if (domain != null) cookie.setDomain(domain);
- if (comment != null) cookie.setComment(comment);
- cookie.setMaxAge(maxAge);
- cookie.setSecure(secure);
- cookie.setHttpOnly(httpOnly);
-
- response.addCookie(cookie);
-
+ HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
+ StringBuffer cookieBuf = new StringBuffer();
+ ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly);
+ String cookie = cookieBuf.toString();
+ response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie);
}
+
+
}
diff --git a/services/src/main/java/org/keycloak/services/util/ServerCookie.java b/services/src/main/java/org/keycloak/services/util/ServerCookie.java
new file mode 100755
index 0000000000..d41cc2e05b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/util/ServerCookie.java
@@ -0,0 +1,305 @@
+package org.keycloak.services.util;
+
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Server-side cookie representation. borrowed from Tomcat.
+ */
+public class ServerCookie implements Serializable {
+ private static final String tspecials = ",; ";
+ private static final String tspecials2 = "()<>@,;:\\\"/[]?={} \t";
+
+ /*
+ * Tests a string and returns true if the string counts as a
+ * reserved token in the Java language.
+ *
+ * @param value the String
to be tested
+ *
+ * @return true
if the String
is a reserved
+ * token; false
if it is not
+ */
+ public static boolean isToken(String value) {
+ if (value == null) return true;
+ int len = value.length();
+
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+
+ if (tspecials.indexOf(c) != -1)
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean containsCTL(String value, int version) {
+ if (value == null) return false;
+ int len = value.length();
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+ if (c < 0x20 || c >= 0x7f) {
+ if (c == 0x09)
+ continue; //allow horizontal tabs
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public static boolean isToken2(String value) {
+ if (value == null) return true;
+ int len = value.length();
+
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+ if (tspecials2.indexOf(c) != -1)
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @deprecated - Not used
+ */
+ public static boolean checkName(String name) {
+ if (!isToken(name)
+ || name.equalsIgnoreCase("Comment") // rfc2019
+ || name.equalsIgnoreCase("Discard") // rfc2965
+ || name.equalsIgnoreCase("Domain") // rfc2019
+ || name.equalsIgnoreCase("Expires") // Netscape
+ || name.equalsIgnoreCase("Max-Age") // rfc2019
+ || name.equalsIgnoreCase("Path") // rfc2019
+ || name.equalsIgnoreCase("Secure") // rfc2019
+ || name.equalsIgnoreCase("Version") // rfc2019
+ // TODO remaining RFC2965 attributes
+ ) {
+ return false;
+ }
+ return true;
+ }
+
+ // -------------------- Cookie parsing tools
+
+
+ /**
+ * Return the header name to set the cookie, based on cookie version.
+ */
+ public static String getCookieHeaderName(int version) {
+ // TODO Re-enable logging when RFC2965 is implemented
+ // log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
+ if (version == 1) {
+ // XXX RFC2965 not referenced in Servlet Spec
+ // Set-Cookie2 is not supported by Netscape 4, 6, IE 3, 5
+ // Set-Cookie2 is supported by Lynx and Opera
+ // Need to check on later IE and FF releases but for now...
+ // RFC2109
+ return "Set-Cookie";
+ // return "Set-Cookie2";
+ } else {
+ // Old Netscape
+ return "Set-Cookie";
+ }
+ }
+
+ /**
+ * US locale - all HTTP dates are in english
+ */
+ private final static Locale LOCALE_US = Locale.US;
+
+ /**
+ * GMT timezone - all HTTP dates are on GMT
+ */
+ public final static TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+ /**
+ * Pattern used for old cookies
+ */
+ private final static String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
+
+
+ private final static DateFormat oldCookieFormat = new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
+
+ public static String formatOldCookie(Date d) {
+ String ocf = null;
+ synchronized (oldCookieFormat) {
+ ocf = oldCookieFormat.format(d);
+ }
+ return ocf;
+ }
+
+ public static void formatOldCookie(Date d, StringBuffer sb,
+ FieldPosition fp) {
+ synchronized (oldCookieFormat) {
+ oldCookieFormat.format(d, sb, fp);
+ }
+ }
+
+
+ private static final String ancientDate = formatOldCookie(new Date(10000));
+
+
+ // TODO RFC2965 fields also need to be passed
+ public static void appendCookieValue(StringBuffer headerBuf,
+ int version,
+ String name,
+ String value,
+ String path,
+ String domain,
+ String comment,
+ int maxAge,
+ boolean isSecure,
+ boolean httpOnly) {
+ StringBuffer buf = new StringBuffer();
+ // Servlet implementation checks name
+ buf.append(name);
+ buf.append("=");
+ // Servlet implementation does not check anything else
+
+ // NOTE!!! BROWSERS REALLY DON'T LIKE QUOTING
+ //maybeQuote2(version, buf, value);
+ buf.append(value);
+
+ // Add version 1 specific information
+ if (version == 1) {
+ // Version=1 ... required
+ buf.append("; Version=1");
+
+ // Comment=comment
+ if (comment != null) {
+ buf.append("; Comment=");
+ //maybeQuote2(version, buf, comment);
+ buf.append(comment);
+ }
+ }
+
+ // Add domain information, if present
+ if (domain != null) {
+ buf.append("; Domain=");
+ //maybeQuote2(version, buf, domain);
+ buf.append(domain);
+ }
+
+ // Max-Age=secs ... or use old "Expires" format
+ // TODO RFC2965 Discard
+ if (maxAge >= 0) {
+ // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format )
+ buf.append("; Expires=");
+ // To expire immediately we need to set the time in past
+ if (maxAge == 0)
+ buf.append(ancientDate);
+ else
+ formatOldCookie
+ (new Date(System.currentTimeMillis() +
+ maxAge * 1000L), buf,
+ new FieldPosition(0));
+
+ buf.append("; Max-Age=");
+ buf.append(maxAge);
+ }
+
+ // Path=path
+ if (path != null) {
+ buf.append("; Path=");
+ buf.append(path);
+ }
+
+ // Secure
+ if (isSecure) {
+ buf.append("; Secure");
+ }
+
+ // HttpOnly
+ if (httpOnly) {
+ buf.append("; HttpOnly");
+ }
+
+ headerBuf.append(buf);
+ }
+
+ /**
+ * @deprecated - Not used
+ */
+ @Deprecated
+ public static void maybeQuote(int version, StringBuffer buf, String value) {
+ // special case - a \n or \r shouldn't happen in any case
+ if (isToken(value)) {
+ buf.append(value);
+ } else {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value, 0, value.length()));
+ buf.append('"');
+ }
+ }
+
+ public static boolean alreadyQuoted(String value) {
+ if (value == null || value.length() == 0) return false;
+ return (value.charAt(0) == '\"' && value.charAt(value.length() - 1) == '\"');
+ }
+
+ /**
+ * Quotes values using rules that vary depending on Cookie version.
+ *
+ * @param version
+ * @param buf
+ * @param value
+ */
+ public static void maybeQuote2(int version, StringBuffer buf, String value) {
+ if (value == null || value.length() == 0) {
+ buf.append("\"\"");
+ } else if (containsCTL(value, version))
+ throw new IllegalArgumentException("Control character in cookie value, consider BASE64 encoding your value");
+ else if (alreadyQuoted(value)) {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value, 1, value.length() - 1));
+ buf.append('"');
+ } else if (version == 0 && !isToken(value)) {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value, 0, value.length()));
+ buf.append('"');
+ } else if (version == 1 && !isToken2(value)) {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value, 0, value.length()));
+ buf.append('"');
+ } else {
+ buf.append(value);
+ }
+ }
+
+
+ /**
+ * Escapes any double quotes in the given string.
+ *
+ * @param s the input string
+ * @param beginIndex start index inclusive
+ * @param endIndex exclusive
+ * @return The (possibly) escaped string
+ */
+ private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) {
+
+ if (s == null || s.length() == 0 || s.indexOf('"') == -1) {
+ return s;
+ }
+
+ StringBuffer b = new StringBuffer();
+ for (int i = beginIndex; i < endIndex; i++) {
+ char c = s.charAt(i);
+ if (c == '\\') {
+ b.append(c);
+ //ignore the character after an escape, just append it
+ if (++i >= endIndex) throw new IllegalArgumentException("Invalid escape character in cookie value.");
+ b.append(s.charAt(i));
+ } else if (c == '"')
+ b.append('\\').append('"');
+ else
+ b.append(c);
+ }
+
+ return b.toString();
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
index 99f6558117..9b4859027b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
@@ -3,6 +3,7 @@ package org.keycloak.testsuite.forms;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -102,6 +103,13 @@ public class FederationProvidersIntegrationTest {
@WebResource
protected AccountPasswordPage changePasswordPage;
+ @Test
+ @Ignore
+ public void runit() throws Exception {
+ Thread.sleep(10000000);
+
+ }
+
static UserModel addUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
UserModel user = session.users().addUser(realm, username);
user.setEmail(email);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java
index b2d80fc5c6..686b785e75 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java
@@ -180,6 +180,11 @@ public class AccessTokenPerfTest {
}
Assert.assertEquals(302, response.getStatus());
uri = response.getLocation();
+ for (String header : response.getHeaders().keySet()) {
+ for (Object value : response.getHeaders().get(header)) {
+ System.out.println(header + ": " + value);
+ }
+ }
response.close();
Assert.assertNotNull(uri);